better distance heuristic
This commit is contained in:
parent
20a6d2faeb
commit
2bf743a272
3 changed files with 179 additions and 91 deletions
116
src/gamestate.rs
116
src/gamestate.rs
|
|
@ -2,15 +2,18 @@ use std::fmt::Display;
|
|||
|
||||
use simple_mcts::Game;
|
||||
|
||||
use crate::pathfind::GoalDistanceMap;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct PlayerState {
|
||||
xy: u8,
|
||||
walls_left: u8,
|
||||
pub walls_left: u8,
|
||||
}
|
||||
|
||||
impl PlayerState {
|
||||
pub const P1_START: Self = Self::new(4, 0, 9);
|
||||
pub const P2_START: Self = Self::new(4, 8, 9);
|
||||
pub const INITIAL_WALLS: u8 = 10;
|
||||
pub const P1_START: Self = Self::new(4, 0, Self::INITIAL_WALLS);
|
||||
pub const P2_START: Self = Self::new(4, 8, Self::INITIAL_WALLS);
|
||||
|
||||
pub const fn new(x: u8, y: u8, walls_left: u8) -> Self {
|
||||
let mut res = Self { xy: 0, walls_left };
|
||||
|
|
@ -54,24 +57,24 @@ pub struct WallState {
|
|||
|
||||
impl WallState {
|
||||
#[inline]
|
||||
pub fn block_cleaned_hori(&mut self, byte_idx: u8, bit: u8) {
|
||||
fn block_cleaned_hori(&mut self, byte_idx: u8, bit: u8) {
|
||||
self.horizontals[byte_idx as usize] |= 1 << bit;
|
||||
}
|
||||
#[inline]
|
||||
pub fn block_cleaned_verti(&mut self, byte_idx: u8, bit: u8) {
|
||||
fn block_cleaned_verti(&mut self, byte_idx: u8, bit: u8) {
|
||||
self.verticals[byte_idx as usize] |= 1 << bit;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn can_walk_between_cleaned_hori(&self, byte_idx: u8, bit: u8) -> bool {
|
||||
fn can_walk_between_cleaned_hori(&self, byte_idx: u8, bit: u8) -> bool {
|
||||
(self.horizontals[byte_idx as usize] >> bit) & 1 != 0
|
||||
}
|
||||
#[inline]
|
||||
pub fn can_walk_between_cleaned_verti(&self, byte_idx: u8, bit: u8) -> bool {
|
||||
fn can_walk_between_cleaned_verti(&self, byte_idx: u8, bit: u8) -> bool {
|
||||
(self.verticals[byte_idx as usize] >> bit) & 1 != 0
|
||||
}
|
||||
|
||||
pub fn block(&mut self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) {
|
||||
fn block(&mut self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) {
|
||||
match (from_x.wrapping_sub(to_x), from_y.wrapping_sub(to_y)) {
|
||||
(1, 0) => self.block_cleaned_verti(to_y, to_x),
|
||||
(0xff, 0) => self.block_cleaned_verti(from_y, from_x),
|
||||
|
|
@ -92,6 +95,24 @@ impl WallState {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_place(&self, x: u8, y: u8, vertical: bool) -> bool {
|
||||
if vertical {
|
||||
self.can_walk_between(x, y, x + 1, y) && self.can_walk_between(x, y + 1, x + 1, y + 1)
|
||||
} else {
|
||||
self.can_walk_between(x, y, x, y + 1) && self.can_walk_between(x + 1, y, x + 1, y + 1)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn place(&mut self, x: u8, y: u8, vertical: bool) {
|
||||
if vertical {
|
||||
self.block(x, y, x + 1, y);
|
||||
self.block(x, y + 1, x + 1, y + 1);
|
||||
} else {
|
||||
self.block(x, y, x, y + 1);
|
||||
self.block(x + 1, y, x + 1, y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WallState {
|
||||
|
|
@ -103,30 +124,55 @@ impl Default for WallState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PlayerIdentifier {
|
||||
P1,
|
||||
P2,
|
||||
}
|
||||
|
||||
impl PlayerIdentifier {
|
||||
pub fn swap(&mut self) {
|
||||
use PlayerIdentifier::*;
|
||||
*self = match self {
|
||||
P1 => P2,
|
||||
P2 => P1,
|
||||
};
|
||||
}
|
||||
|
||||
pub const fn y_goal(&self) -> u8 {
|
||||
match self {
|
||||
PlayerIdentifier::P1 => 8,
|
||||
PlayerIdentifier::P2 => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GameState {
|
||||
pub p1: PlayerState,
|
||||
pub p2: PlayerState,
|
||||
pub walls: WallState,
|
||||
pub whose_turn: bool,
|
||||
pub current_player: PlayerIdentifier,
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
pub fn current_player(&self) -> &PlayerState {
|
||||
if self.whose_turn { &self.p1 } else { &self.p2 }
|
||||
pub fn current_player_state(&self) -> &PlayerState {
|
||||
match self.current_player {
|
||||
PlayerIdentifier::P1 => &self.p1,
|
||||
PlayerIdentifier::P2 => &self.p2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_player_mut(&mut self) -> &mut PlayerState {
|
||||
if self.whose_turn {
|
||||
&mut self.p1
|
||||
} else {
|
||||
&mut self.p2
|
||||
pub fn current_player_state_mut(&mut self) -> &mut PlayerState {
|
||||
match self.current_player {
|
||||
PlayerIdentifier::P1 => &mut self.p1,
|
||||
PlayerIdentifier::P2 => &mut self.p2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mcts_result(&self) -> Option<f64> {
|
||||
let p1_won = self.p1.y() == 8;
|
||||
let p2_won = self.p2.y() == 0;
|
||||
let p1_won = self.p1.y() == PlayerIdentifier::P1.y_goal();
|
||||
let p2_won = self.p2.y() == PlayerIdentifier::P2.y_goal();
|
||||
|
||||
let outcome_for_p1 = match (p1_won, p2_won) {
|
||||
(false, false) => return None,
|
||||
|
|
@ -135,12 +181,9 @@ impl GameState {
|
|||
(true, true) => 0.0,
|
||||
};
|
||||
|
||||
Some(if self.whose_turn {
|
||||
//p1 wants to win
|
||||
outcome_for_p1
|
||||
} else {
|
||||
//p2 wants to win
|
||||
-1.0 * outcome_for_p1
|
||||
Some(match self.current_player {
|
||||
PlayerIdentifier::P1 => outcome_for_p1,
|
||||
PlayerIdentifier::P2 => -1.0 * outcome_for_p1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -151,13 +194,15 @@ impl Default for GameState {
|
|||
p1: PlayerState::P1_START,
|
||||
p2: PlayerState::P2_START,
|
||||
walls: Default::default(),
|
||||
whose_turn: true,
|
||||
current_player: PlayerIdentifier::P1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GameState {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let dm = GoalDistanceMap::new(&self.walls, self.current_player);
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"P1: {}, P2: {}\n",
|
||||
|
|
@ -191,12 +236,12 @@ impl Display for GameState {
|
|||
};
|
||||
write!(f, "{wall}")?;
|
||||
}
|
||||
let player = if self.p1.x() == x && 8 - self.p1.y() == y {
|
||||
"P1"
|
||||
} else if self.p2.x() == x && 8 - self.p2.y() == y {
|
||||
"P2"
|
||||
let player = if self.p1.x() == x && self.p1.y() == y {
|
||||
"\x1b[1mP1\x1b[0m".to_string()
|
||||
} else if self.p2.x() == x && self.p2.y() == y {
|
||||
"\x1b[1mP2\x1b[0m".to_string()
|
||||
} else {
|
||||
" "
|
||||
format!("{:^2}", dm.at(x, y))
|
||||
};
|
||||
write!(f, "{player}")?;
|
||||
}
|
||||
|
|
@ -209,7 +254,7 @@ impl Display for GameState {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::gamestate::{GameState, PlayerState, WallState};
|
||||
use crate::gamestate::WallState;
|
||||
|
||||
#[test]
|
||||
fn test_blocking() {
|
||||
|
|
@ -233,14 +278,5 @@ mod tests {
|
|||
assert!(w.can_walk_between(7, 0, 8, 0));
|
||||
w.block(7, 0, 8, 0);
|
||||
assert!(!w.can_walk_between(7, 0, 8, 0));
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
GameState {
|
||||
p1: PlayerState::P1_START,
|
||||
p2: PlayerState::P2_START,
|
||||
walls: w,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue