-
Notifications
You must be signed in to change notification settings - Fork 56
Expand file tree
/
Copy pathbot.rs
More file actions
112 lines (102 loc) · 3.85 KB
/
Copy pathbot.rs
File metadata and controls
112 lines (102 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use turtle::rand::random_range;
use crate::{
battlestate::{AttackOutcome, BattleState},
channel::{Channel, Message},
game::Turn,
grid::Cell,
};
pub struct Bot {
channel: Channel,
state: BattleState,
turn: Turn,
}
impl Bot {
pub fn new(port: u16) -> Self {
Self {
channel: Channel::client(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)),
state: BattleState::new(),
turn: Turn::Opponent,
}
}
fn random_attack_location(&self) -> (u8, u8) {
loop {
let x = random_range(0, 9);
let y = random_range(0, 9);
if self.state.can_bomb(&(x, y)) {
return (x, y);
}
}
}
fn get_attack_location(&self) -> (u8, u8) {
// Iterator on positions of all the bombed (Hit, not Destroyed) locations in AttackGrid
let bombed_locations = self
.state
.attack_grid()
.iter()
.flatten()
.enumerate()
.filter(|(_, &cell)| cell == Cell::Bombed)
.map(|(loc, _)| ((loc as f32 / 10.0).floor() as i32, loc as i32 % 10));
// Iterate over each bombed location until an attackable position
// is found in the neighbourhood of the bombed location and return it
for loc in bombed_locations {
let attackable = [(-1, 0), (1, 0), (0, -1), (0, 1)]
.iter()
.map(|n| (n.0 + loc.0, n.1 + loc.1))
.filter(|pos| matches!(pos.0, 0..=9) && matches!(pos.1, 0..=9))
.map(|pos| (pos.0 as u8, pos.1 as u8))
.find(|pos| self.state.can_bomb(&pos));
if let Some(pos) = attackable {
return pos;
}
}
// Otherwise return a random attack location if no bombed locations are present
self.random_attack_location()
}
/// Similar to Game::run but without graphics
pub fn play(&mut self) {
loop {
match self.turn {
Turn::Me => {
let attack_location = self.get_attack_location();
self.channel.send_message(&Message::AttackCoordinates(attack_location));
match self.channel.receive_message() {
Message::AttackResult(outcome) => {
self.state.set_attack_outcome(&attack_location, outcome);
match outcome {
AttackOutcome::Miss | AttackOutcome::Destroyed(_) => {
self.turn.flip();
}
_ => (),
}
}
_ => panic!("Expected Message of AttackResult from Opponent."),
}
}
Turn::Opponent => match self.channel.receive_message() {
Message::AttackCoordinates(p) => {
let outcome = self.state.incoming_attack(&p);
self.channel.send_message(&Message::AttackResult(outcome));
match outcome {
AttackOutcome::Miss | AttackOutcome::Destroyed(_) => {
self.turn.flip();
}
AttackOutcome::Hit => (),
}
}
_ => panic!("Expected Message of AttackCoordinates from Opponent"),
},
}
match (self.state.ships_lost, self.state.destroyed_rival_ships) {
(5, _) => {
break;
}
(_, 5) => {
break;
}
(_, _) => continue,
}
}
}
}