coooooolors
This commit is contained in:
parent
6ab78f2b1c
commit
d00a7a6fec
3 changed files with 238 additions and 53 deletions
261
src/gamestate.rs
261
src/gamestate.rs
|
|
@ -1,9 +1,83 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use simple_mcts::Game;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use crate::pathfind::GoalDistanceMap;
|
||||
|
||||
pub trait BoardRepresentation: Default + Clone + Copy {
|
||||
fn can_walk_between(&self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) -> bool;
|
||||
fn blocked_by_player(
|
||||
&self,
|
||||
from_x: u8,
|
||||
from_y: u8,
|
||||
to_x: u8,
|
||||
to_y: u8,
|
||||
) -> Option<PlayerIdentifier> {
|
||||
None
|
||||
}
|
||||
|
||||
fn can_place(&self, x: u8, y: u8, vertical: bool) -> bool;
|
||||
fn place(&mut self, x: u8, y: u8, vertical: bool, current_player: PlayerIdentifier);
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct Compact(WallState);
|
||||
impl BoardRepresentation for Compact {
|
||||
fn can_walk_between(&self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) -> bool {
|
||||
self.0.can_walk_between(from_x, from_y, to_x, to_y)
|
||||
}
|
||||
|
||||
fn can_place(&self, x: u8, y: u8, vertical: bool) -> bool {
|
||||
self.0.can_place(x, y, vertical)
|
||||
}
|
||||
|
||||
fn place(&mut self, x: u8, y: u8, vertical: bool, _current_player: PlayerIdentifier) {
|
||||
self.0.place(x, y, vertical);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Copy)]
|
||||
pub struct PerPlayer {
|
||||
p1: WallState,
|
||||
p2: WallState,
|
||||
}
|
||||
impl BoardRepresentation for PerPlayer {
|
||||
fn can_walk_between(&self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) -> bool {
|
||||
self.p1.can_walk_between(from_x, from_y, to_x, to_y)
|
||||
&& self.p2.can_walk_between(from_x, from_y, to_x, to_y)
|
||||
}
|
||||
fn blocked_by_player(
|
||||
&self,
|
||||
from_x: u8,
|
||||
from_y: u8,
|
||||
to_x: u8,
|
||||
to_y: u8,
|
||||
) -> Option<PlayerIdentifier> {
|
||||
match (
|
||||
!self.p1.can_walk_between(from_x, from_y, to_x, to_y),
|
||||
!self.p2.can_walk_between(from_x, from_y, to_x, to_y),
|
||||
) {
|
||||
(false, false) => None,
|
||||
(true, false) => Some(PlayerIdentifier::P1),
|
||||
(false, true) => Some(PlayerIdentifier::P2),
|
||||
(true, true) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn can_place(&self, x: u8, y: u8, vertical: bool) -> bool {
|
||||
self.p1.can_place(x, y, vertical) && self.p2.can_place(x, y, vertical)
|
||||
}
|
||||
|
||||
fn place(&mut self, x: u8, y: u8, vertical: bool, current_player: PlayerIdentifier) {
|
||||
match current_player {
|
||||
PlayerIdentifier::P1 => {
|
||||
self.p1.place(x, y, vertical);
|
||||
}
|
||||
PlayerIdentifier::P2 => {
|
||||
self.p2.place(x, y, vertical);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct PlayerState {
|
||||
xy: u8,
|
||||
|
|
@ -145,17 +219,24 @@ impl PlayerIdentifier {
|
|||
PlayerIdentifier::P2 => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn color(&self) -> &str {
|
||||
match self {
|
||||
PlayerIdentifier::P1 => "\x1b[38;2;0;0;255m",
|
||||
PlayerIdentifier::P2 => "\x1b[38;2;255;0;0m",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GameState {
|
||||
pub struct GameState<R: BoardRepresentation = PerPlayer> {
|
||||
pub p1: PlayerState,
|
||||
pub p2: PlayerState,
|
||||
pub walls: WallState,
|
||||
pub walls: R,
|
||||
pub current_player: PlayerIdentifier,
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
impl<R: BoardRepresentation> GameState<R> {
|
||||
pub fn current_player_state(&self) -> &PlayerState {
|
||||
match self.current_player {
|
||||
PlayerIdentifier::P1 => &self.p1,
|
||||
|
|
@ -186,9 +267,13 @@ impl GameState {
|
|||
PlayerIdentifier::P2 => -1.0 * outcome_for_p1,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn place(&mut self, x: u8, y: u8, vertical: bool) {
|
||||
self.walls.place(x, y, vertical, self.current_player)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GameState {
|
||||
impl<R: BoardRepresentation> Default for GameState<R> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
p1: PlayerState::P1_START,
|
||||
|
|
@ -204,6 +289,36 @@ impl Display for GameState {
|
|||
let d1 = GoalDistanceMap::new(&self.walls, PlayerIdentifier::P1);
|
||||
let d2 = GoalDistanceMap::new(&self.walls, PlayerIdentifier::P2);
|
||||
|
||||
let crossing = |f: &mut Formatter<'_>,
|
||||
wall_above,
|
||||
wall_below,
|
||||
wall_left,
|
||||
wall_right,
|
||||
pi: Option<PlayerIdentifier>| {
|
||||
if let Some(pi) = pi {
|
||||
write!(f, "{}", pi.color())?;
|
||||
}
|
||||
match (wall_above, wall_below, wall_left, wall_right) {
|
||||
(false, false, false, false) => write!(f, "·"),
|
||||
(false, true, false, false) => write!(f, "╵"),
|
||||
(true, false, false, false) => write!(f, "╷"),
|
||||
(false, false, true, false) => write!(f, "╴"),
|
||||
(false, false, false, true) => write!(f, "╶"),
|
||||
(true, true, false, false) => write!(f, "│"),
|
||||
(false, false, true, true) => write!(f, "─"),
|
||||
(true, false, true, false) => write!(f, "┐"),
|
||||
(false, true, false, true) => write!(f, "└"),
|
||||
(false, true, true, false) => write!(f, "┘"),
|
||||
(true, false, false, true) => write!(f, "┌"),
|
||||
(true, true, true, false) => write!(f, "┤"),
|
||||
(true, false, true, true) => write!(f, "┬"),
|
||||
(false, true, true, true) => write!(f, "┴"),
|
||||
(true, true, false, true) => write!(f, "├"),
|
||||
(true, true, true, true) => write!(f, "┼"),
|
||||
}?;
|
||||
write!(f, "\x1b[0m")
|
||||
};
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"P1: {} walls, {} away from win",
|
||||
|
|
@ -216,17 +331,42 @@ impl Display for GameState {
|
|||
self.p2.walls_left,
|
||||
d2.at(self.p2.x(), self.p2.y())
|
||||
)?;
|
||||
writeln!(f, "╭──┬──┬──┬──┬──┬──┬──┬──┬──╮")?;
|
||||
write!(f, "╭──")?;
|
||||
for x in 1..9 {
|
||||
crossing(
|
||||
f,
|
||||
!self.walls.can_walk_between(x - 1, 0, x, 0),
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
self.walls.blocked_by_player(x - 1, 0, x, 0),
|
||||
)?;
|
||||
write!(f, "──")?;
|
||||
}
|
||||
writeln!(f, "╮")?;
|
||||
|
||||
for y in 0..9 {
|
||||
if y > 0 {
|
||||
write!(f, "├")?;
|
||||
crossing(
|
||||
f,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
!self.walls.can_walk_between(0, y - 1, 0, y),
|
||||
self.walls.blocked_by_player(0, y - 1, 0, y),
|
||||
)?;
|
||||
|
||||
for x in 0..9 {
|
||||
let wall = if self.walls.can_walk_between(x, y - 1, x, y) {
|
||||
' '
|
||||
" ".to_string()
|
||||
} else {
|
||||
'─'
|
||||
if let Some(pi) = self.walls.blocked_by_player(x, y - 1, x, y) {
|
||||
format!("{}─", pi.color())
|
||||
} else {
|
||||
"─".to_string()
|
||||
}
|
||||
};
|
||||
write!(f, "{wall}{wall}")?;
|
||||
write!(f, "{wall}{wall}\x1b[0m")?;
|
||||
if x != 8 {
|
||||
let wall_above = !self.walls.can_walk_between(x, y, x + 1, y);
|
||||
let wall_below =
|
||||
|
|
@ -235,45 +375,81 @@ impl Display for GameState {
|
|||
let wall_right =
|
||||
y != 0 && !self.walls.can_walk_between(x + 1, y, x + 1, y - 1);
|
||||
|
||||
match (wall_above, wall_below, wall_left, wall_right) {
|
||||
(false, false, false, false) => write!(f, "·")?,
|
||||
(false, true, false, false) => write!(f, "╵")?,
|
||||
(true, false, false, false) => write!(f, "╷")?,
|
||||
(false, false, true, false) => write!(f, "╴")?,
|
||||
(false, false, false, true) => write!(f, "╶")?,
|
||||
(true, true, false, false) => write!(f, "│")?,
|
||||
(false, false, true, true) => write!(f, "─")?,
|
||||
(false, true, true, false) => write!(f, "┐")?,
|
||||
(true, false, false, true) => write!(f, "└")?,
|
||||
(true, false, true, false) => write!(f, "┘")?,
|
||||
(false, true, false, true) => write!(f, "┌")?,
|
||||
(true, true, true, false) => write!(f, "┤")?,
|
||||
(false, true, true, true) => write!(f, "┬")?,
|
||||
(true, false, true, true) => write!(f, "┴")?,
|
||||
(true, true, false, true) => write!(f, "├")?,
|
||||
(true, true, true, true) => write!(f, "┼")?,
|
||||
let mut neighboring_colors = Vec::with_capacity(4);
|
||||
|
||||
if wall_above && let Some(pi) = self.walls.blocked_by_player(x, y, x + 1, y)
|
||||
{
|
||||
neighboring_colors.push(pi)
|
||||
}
|
||||
if wall_below
|
||||
&& let Some(pi) = self.walls.blocked_by_player(x, y - 1, x + 1, y - 1)
|
||||
{
|
||||
neighboring_colors.push(pi)
|
||||
}
|
||||
if wall_left && let Some(pi) = self.walls.blocked_by_player(x, y, x, y - 1)
|
||||
{
|
||||
neighboring_colors.push(pi)
|
||||
}
|
||||
if wall_right
|
||||
&& let Some(pi) = self.walls.blocked_by_player(x + 1, y, x + 1, y - 1)
|
||||
{
|
||||
neighboring_colors.push(pi)
|
||||
}
|
||||
|
||||
// write!(f, "┼")?;
|
||||
let num_p1 = neighboring_colors
|
||||
.iter()
|
||||
.filter(|i| matches!(i, PlayerIdentifier::P1))
|
||||
.count();
|
||||
let num_p2 = neighboring_colors
|
||||
.iter()
|
||||
.filter(|i| matches!(i, PlayerIdentifier::P2))
|
||||
.count();
|
||||
|
||||
crossing(
|
||||
f,
|
||||
wall_above,
|
||||
wall_below,
|
||||
wall_left,
|
||||
wall_right,
|
||||
if num_p1 == 0 && num_p2 == 0 {
|
||||
None
|
||||
} else if num_p1 > num_p2 {
|
||||
Some(PlayerIdentifier::P1)
|
||||
} else {
|
||||
Some(PlayerIdentifier::P2)
|
||||
},
|
||||
)?;
|
||||
}
|
||||
}
|
||||
write!(f, "┤\n│")?;
|
||||
crossing(
|
||||
f,
|
||||
true,
|
||||
true,
|
||||
!self.walls.can_walk_between(8, y - 1, 8, y),
|
||||
false,
|
||||
self.walls.blocked_by_player(8, y - 1, 8, y),
|
||||
)?;
|
||||
write!(f, "\n│")?;
|
||||
} else {
|
||||
write!(f, "│")?;
|
||||
}
|
||||
for x in 0..9 {
|
||||
if x > 0 {
|
||||
let wall = if self.walls.can_walk_between(x - 1, y, x, y) {
|
||||
' '
|
||||
" ".to_string()
|
||||
} else {
|
||||
'│'
|
||||
if let Some(pi) = self.walls.blocked_by_player(x - 1, y, x, y) {
|
||||
format!("{}│", pi.color())
|
||||
} else {
|
||||
"│".to_string()
|
||||
}
|
||||
};
|
||||
write!(f, "{wall}")?;
|
||||
write!(f, "{wall}\x1b[0m")?;
|
||||
}
|
||||
let player = if self.p1.x() == x && self.p1.y() == y {
|
||||
"\x1b[1mP1\x1b[0m".to_owned()
|
||||
format!("\x1b[1m{}P1\x1b[0m", PlayerIdentifier::P1.color())
|
||||
} else if self.p2.x() == x && self.p2.y() == y {
|
||||
"\x1b[1mP2\x1b[0m".to_owned()
|
||||
format!("\x1b[1m{}P2\x1b[0m", PlayerIdentifier::P2.color())
|
||||
} else {
|
||||
// format!("{:^2}", dm.at(x, y))
|
||||
" ".to_owned()
|
||||
|
|
@ -282,7 +458,18 @@ impl Display for GameState {
|
|||
}
|
||||
writeln!(f, "│")?;
|
||||
}
|
||||
writeln!(f, "╰──┴──┴──┴──┴──┴──┴──┴──┴──╯")?;
|
||||
write!(f, "╰──")?;
|
||||
for x in 1..9 {
|
||||
crossing(
|
||||
f,
|
||||
false,
|
||||
!self.walls.can_walk_between(x - 1, 8, x, 8),
|
||||
true,
|
||||
true,
|
||||
self.walls.blocked_by_player(x - 1, 8, x, 8),
|
||||
)?;
|
||||
write!(f, "──")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue