parent
4c795da09b
commit
109220a3f9
@ -1,5 +1,84 @@ |
|||||||
# This file is automatically @generated by Cargo. |
# This file is automatically @generated by Cargo. |
||||||
# It is not intended for manual editing. |
# It is not intended for manual editing. |
||||||
|
[[package]] |
||||||
|
name = "cfg-if" |
||||||
|
version = "0.1.10" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" |
||||||
|
|
||||||
[[package]] |
[[package]] |
||||||
name = "gammon" |
name = "gammon" |
||||||
version = "0.1.0" |
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"rand", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "getrandom" |
||||||
|
version = "0.1.14" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"libc", |
||||||
|
"wasi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "libc" |
||||||
|
version = "0.2.69" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "ppv-lite86" |
||||||
|
version = "0.2.6" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand" |
||||||
|
version = "0.7.3" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" |
||||||
|
dependencies = [ |
||||||
|
"getrandom", |
||||||
|
"libc", |
||||||
|
"rand_chacha", |
||||||
|
"rand_core", |
||||||
|
"rand_hc", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_chacha" |
||||||
|
version = "0.2.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" |
||||||
|
dependencies = [ |
||||||
|
"ppv-lite86", |
||||||
|
"rand_core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_core" |
||||||
|
version = "0.5.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" |
||||||
|
dependencies = [ |
||||||
|
"getrandom", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "rand_hc" |
||||||
|
version = "0.2.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" |
||||||
|
dependencies = [ |
||||||
|
"rand_core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "wasi" |
||||||
|
version = "0.9.0+wasi-snapshot-preview1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" |
||||||
|
@ -0,0 +1,553 @@ |
|||||||
|
use std::default::Default; |
||||||
|
use std::{fmt, mem}; |
||||||
|
use std::fmt::{Display, Formatter, Write}; |
||||||
|
use rand::{Rng, rngs::OsRng}; |
||||||
|
use std::borrow::Borrow; |
||||||
|
|
||||||
|
#[derive(Debug,Clone)] |
||||||
|
struct Board([Bin; 24]); |
||||||
|
|
||||||
|
impl Board { |
||||||
|
fn can_bear_off(&self, color : Color) -> bool { |
||||||
|
for i in (HOME_MAX)+1..=BOARD_MAX { |
||||||
|
match (self.0)[i as usize].as_color() { |
||||||
|
Some(c) if c == color => { |
||||||
|
return false; |
||||||
|
}, |
||||||
|
_ => { |
||||||
|
// empty or the other color
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
true |
||||||
|
} |
||||||
|
|
||||||
|
fn can_place(&self, pos: u8, color : Color) -> bool { |
||||||
|
if pos > BOARD_MAX { |
||||||
|
false |
||||||
|
} else { |
||||||
|
let c = (self.0)[pos as usize].as_color(); |
||||||
|
|
||||||
|
c.is_none() || |
||||||
|
c.unwrap() == color || |
||||||
|
(self.0)[pos as usize].as_count() == 1 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn can_move_from(&self, pos: u8, color : Color) -> bool { |
||||||
|
if pos > BOARD_MAX { |
||||||
|
false |
||||||
|
} else { |
||||||
|
let c = (self.0)[pos as usize].as_color(); |
||||||
|
|
||||||
|
c.is_some() && |
||||||
|
c.unwrap() == color |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[must_use] |
||||||
|
fn apply_move_mut(&mut self, from : u8, to : u8) -> Option<Color> { |
||||||
|
let old_color = self.0[to as usize].as_color(); |
||||||
|
let color = self.0[from as usize].subtract_mut(); |
||||||
|
self.0[to as usize].add_mut(color); |
||||||
|
old_color |
||||||
|
} |
||||||
|
|
||||||
|
#[must_use] |
||||||
|
fn apply_enter_mut(&mut self, pos : u8, color : Color) -> Option<Color> { |
||||||
|
let old_color = self.0[pos as usize].as_color(); |
||||||
|
self.0[pos as usize].add_mut(color); |
||||||
|
old_color |
||||||
|
} |
||||||
|
|
||||||
|
fn apply_bearoff_mut(&mut self, pos : u8) { |
||||||
|
self.0[pos as usize].subtract_mut(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy,PartialEq,Eq)] |
||||||
|
pub enum Color { |
||||||
|
Black, |
||||||
|
White, |
||||||
|
} |
||||||
|
|
||||||
|
impl Color { |
||||||
|
fn opposite(self) -> Color { |
||||||
|
match self { |
||||||
|
Color::Black => Color::White, |
||||||
|
Color::White => Color::Black, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy)] |
||||||
|
pub enum Bin { |
||||||
|
Empty, |
||||||
|
Black(u8), |
||||||
|
White(u8), |
||||||
|
} |
||||||
|
|
||||||
|
impl Bin { |
||||||
|
pub fn as_color(self) -> Option<Color> { |
||||||
|
match self { |
||||||
|
Bin::Empty => None, |
||||||
|
Bin::Black(_) => Some(Color::Black), |
||||||
|
Bin::White(_) => Some(Color::White), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn as_count(self) -> u8 { |
||||||
|
match self { |
||||||
|
Bin::Empty => 0, |
||||||
|
Bin::Black(n) | Bin::White(n) => n, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn add_mut(&mut self, color : Color) { |
||||||
|
mem::replace(self, match self.borrow() { |
||||||
|
Bin::Empty => { |
||||||
|
match color { |
||||||
|
Color::Black => Bin::Black(1), |
||||||
|
Color::White => Bin::White(1), |
||||||
|
} |
||||||
|
}, |
||||||
|
Bin::Black(n) => Bin::Black(*n + 1), |
||||||
|
Bin::White(n) => Bin::White(*n + 1), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
fn subtract_mut(&mut self) -> Color { |
||||||
|
match self { |
||||||
|
Bin::Empty => { |
||||||
|
panic!("Subtract from empty bin"); |
||||||
|
}, |
||||||
|
Bin::Black(n) => { |
||||||
|
*n -= 1u8; |
||||||
|
if *n == 0 { |
||||||
|
mem::replace(self, Bin::Empty); |
||||||
|
} |
||||||
|
Color::Black |
||||||
|
}, |
||||||
|
Bin::White(n) => { |
||||||
|
*n -= 1u8; |
||||||
|
if *n == 0 { |
||||||
|
mem::replace(self, Bin::Empty); |
||||||
|
} |
||||||
|
Color::White |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
struct Player { |
||||||
|
born_off : u8, |
||||||
|
to_place : u8, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct State { |
||||||
|
turn : Color, |
||||||
|
board: Board, |
||||||
|
roll: Roll, |
||||||
|
player : Player, |
||||||
|
other : Player, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug,Clone,Copy)] |
||||||
|
pub enum Move { |
||||||
|
InBoard { |
||||||
|
from: u8, |
||||||
|
to : u8 |
||||||
|
}, |
||||||
|
Enter(u8), |
||||||
|
BearOff(u8) |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub enum Error { |
||||||
|
Internal, |
||||||
|
MalformedMove, |
||||||
|
NoSourceStone, |
||||||
|
TargetOccupied, |
||||||
|
NotBearingOff, |
||||||
|
NotAllPlaced, |
||||||
|
NothingToPlace, |
||||||
|
NoMatchingRoll, |
||||||
|
NoValidMoves, |
||||||
|
NotYourTurn, |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for Error { |
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||||
|
// TODO
|
||||||
|
write!(f, "{:?}", self) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl std::error::Error for Error {} |
||||||
|
|
||||||
|
#[derive(Debug, Clone)] |
||||||
|
pub struct Roll { |
||||||
|
/// The dice as rolled
|
||||||
|
dice: (u8, u8), |
||||||
|
/// Remaining moves based on the dice and steps already performed
|
||||||
|
remaining_moves: Vec<u8>, |
||||||
|
} |
||||||
|
|
||||||
|
impl Roll { |
||||||
|
pub fn new() -> Self { |
||||||
|
Self::from_dice(( |
||||||
|
OsRng.gen_range(1, 7), |
||||||
|
OsRng.gen_range(1, 7) |
||||||
|
)) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn from_dice(dice: (u8,u8)) -> Self { |
||||||
|
let remaining_moves; |
||||||
|
if dice.0 == dice.1 { |
||||||
|
remaining_moves = vec![dice.0, dice.0, dice.0, dice.0]; |
||||||
|
} else { |
||||||
|
remaining_moves = vec![dice.0, dice.1]; |
||||||
|
} |
||||||
|
|
||||||
|
Roll { |
||||||
|
dice, |
||||||
|
remaining_moves |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn have_equal(&self, eq : u8) -> bool { |
||||||
|
self.remaining_moves.iter() |
||||||
|
.any(|v| *v == eq) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn have_equal_or_greater(&self, mineq : u8) -> bool { |
||||||
|
self.remaining_moves.iter() |
||||||
|
.any(|v| *v >= mineq) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_equal_or_greater(&self, mineq : u8) -> Option<u8> { |
||||||
|
self.remaining_moves.iter() |
||||||
|
.find(|v| *v >= &mineq) |
||||||
|
.cloned() |
||||||
|
} |
||||||
|
|
||||||
|
fn remove_mut(&mut self, needed : u8) { |
||||||
|
let nth = self.remaining_moves.iter().position(|v| *v == needed).unwrap(); |
||||||
|
self.remaining_moves.remove(nth); |
||||||
|
} |
||||||
|
|
||||||
|
/// # Panics
|
||||||
|
/// if not available
|
||||||
|
pub fn remove(&mut self, needed : u8) -> Roll { |
||||||
|
let mut next = self.clone(); |
||||||
|
next.remove_mut(needed); |
||||||
|
next |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// The board is reversed based on the active player's color to make the algorithm unified.
|
||||||
|
// The movement is always to lower positions, with home at 5-0
|
||||||
|
const BOARD_MAX : u8 = 23; |
||||||
|
const HOME_MAX : u8 = 5; |
||||||
|
|
||||||
|
impl State { |
||||||
|
pub fn start(turn : Option<Color>) -> Self { |
||||||
|
// The state is laid out for the white player
|
||||||
|
let mut state = State { |
||||||
|
turn: Color::White, |
||||||
|
board: Board([ |
||||||
|
// 1 .. 12 (bottom row, right to left)
|
||||||
|
Bin::Black(2), // white player's home side
|
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::White(5), // 5 in 0-based
|
||||||
|
Bin::Empty, |
||||||
|
Bin::White(3), |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Black(5), |
||||||
|
// 13 .. 24 (top row, left to right)
|
||||||
|
Bin::White(5), // 12 in 0-based
|
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Black(3), |
||||||
|
Bin::Empty, |
||||||
|
Bin::Black(5), // 18 in 0-based
|
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::Empty, |
||||||
|
Bin::White(2), // black player's home side
|
||||||
|
]), |
||||||
|
roll: Roll::new(), |
||||||
|
player: Player { born_off: 0, to_place: 0 }, |
||||||
|
other: Player { born_off: 0, to_place: 0 } |
||||||
|
}; |
||||||
|
|
||||||
|
if turn.map(|c| c == Color::Black).unwrap_or_else(|| OsRng.gen()) { |
||||||
|
// flip the board so black starts
|
||||||
|
state.switch_sides_mut(); |
||||||
|
} |
||||||
|
state |
||||||
|
} |
||||||
|
|
||||||
|
pub fn check_move(&self, mv : Move) -> Result<(), Error> { |
||||||
|
match mv { |
||||||
|
Move::InBoard{from, to} => { |
||||||
|
if to > from { |
||||||
|
// can only go lower
|
||||||
|
return Err(Error::MalformedMove); |
||||||
|
} |
||||||
|
|
||||||
|
if self.player.to_place != 0 { |
||||||
|
return Err(Error::NotAllPlaced); |
||||||
|
} |
||||||
|
|
||||||
|
if !self.board.can_move_from(from, self.turn) { |
||||||
|
return Err(Error::NoSourceStone); |
||||||
|
} |
||||||
|
|
||||||
|
if !self.board.can_place(to, self.turn) { |
||||||
|
return Err(Error::TargetOccupied); |
||||||
|
} |
||||||
|
|
||||||
|
let needed = from - to; |
||||||
|
if !self.roll.have_equal(needed) { |
||||||
|
return Err(Error::NoMatchingRoll); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
}, |
||||||
|
Move::Enter(pos) => { |
||||||
|
if self.player.to_place == 0 { |
||||||
|
return Err(Error::NothingToPlace); |
||||||
|
} |
||||||
|
|
||||||
|
if pos > BOARD_MAX { |
||||||
|
return Err(Error::MalformedMove); |
||||||
|
} |
||||||
|
|
||||||
|
let needed = 24 - pos; |
||||||
|
|
||||||
|
if !self.roll.have_equal(needed) { |
||||||
|
return Err(Error::NoMatchingRoll); |
||||||
|
} |
||||||
|
|
||||||
|
if !self.board.can_place(pos, self.turn) { |
||||||
|
return Err(Error::TargetOccupied); |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
}, |
||||||
|
Move::BearOff(pos) => { |
||||||
|
if pos > HOME_MAX { |
||||||
|
return Err(Error::MalformedMove); |
||||||
|
} |
||||||
|
|
||||||
|
if self.player.to_place != 0 { |
||||||
|
return Err(Error::NotAllPlaced); |
||||||
|
} |
||||||
|
|
||||||
|
if !self.board.can_bear_off(self.turn) { |
||||||
|
// still have checkers in higher positions
|
||||||
|
return Err(Error::NotBearingOff); |
||||||
|
} |
||||||
|
|
||||||
|
if !self.roll.have_equal_or_greater(pos + 1) { |
||||||
|
return Err(Error::NoMatchingRoll); |
||||||
|
// TODO must always bear off the highest possible, if multiple checkers are available
|
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn apply_move(&self, turn : Color, mv : Move) -> Result<Self, Error> { |
||||||
|
if turn != self.turn { |
||||||
|
return Err(Error::NotYourTurn); |
||||||
|
} |
||||||
|
|
||||||
|
self.check_move(mv)?; |
||||||
|
|
||||||
|
print!("{:?} plays move: ", turn); |
||||||
|
|
||||||
|
let mut next = self.clone(); |
||||||
|
|
||||||
|
match mv { |
||||||
|
Move::InBoard { from, to } => { |
||||||
|
println!("In-board {} -> {}", from, to); |
||||||
|
|
||||||
|
let old_color = next.board.apply_move_mut(from, to); |
||||||
|
let needed = from - to; // move is always downward
|
||||||
|
next.roll.remove_mut(needed); |
||||||
|
|
||||||
|
if let Some(c) = old_color { |
||||||
|
if c != self.turn { |
||||||
|
println!("{:?} stone at position {} is hit.", c, to); |
||||||
|
|
||||||
|
// hit opposite color
|
||||||
|
next.other.to_place += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
Move::Enter(pos) => { |
||||||
|
println!("Enter -> {}", pos); |
||||||
|
|
||||||
|
let old_color = next.board.apply_enter_mut(pos, self.turn); |
||||||
|
let needed = 24 - pos; |
||||||
|
|
||||||
|
next.roll.remove_mut(needed); |
||||||
|
next.player.to_place -= 1; |
||||||
|
|
||||||
|
if let Some(c) = old_color { |
||||||
|
if c != self.turn { |
||||||
|
println!("{:?} stone at position {} is hit.", c, pos); |
||||||
|
// hit opposite color
|
||||||
|
next.other.to_place += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
Move::BearOff(pos) => { |
||||||
|
println!("Bear off -> {}", pos); |
||||||
|
|
||||||
|
let needed = next.roll.get_equal_or_greater(pos + 1).unwrap(); |
||||||
|
next.roll.remove_mut(needed); |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
if next.roll.remaining_moves.is_empty() { |
||||||
|
next.switch_sides_mut(); |
||||||
|
next.roll_mut(); |
||||||
|
|
||||||
|
// TODO check if any moves are possible with this roll, if not, auto-switch again
|
||||||
|
} |
||||||
|
|
||||||
|
Ok(next) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn spoof_roll(&self, roll : Roll) -> Self { |
||||||
|
let mut next = self.clone(); |
||||||
|
next.roll = roll; |
||||||
|
next |
||||||
|
} |
||||||
|
|
||||||
|
fn roll_mut(&mut self) { |
||||||
|
self.roll = Roll::new(); |
||||||
|
} |
||||||
|
|
||||||
|
fn switch_sides_mut(&mut self) { |
||||||
|
self.board.0.reverse(); |
||||||
|
mem::swap(&mut self.player, &mut self.other); |
||||||
|
self.turn = self.turn.opposite(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for State { |
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
||||||
|
write!(f, "\n# {:?} turn, roll ({},{}), moves to play: [{}]\n", |
||||||
|
self.turn, |
||||||
|
self.roll.dice.0, |
||||||
|
self.roll.dice.1, |
||||||
|
self.roll.remaining_moves.iter().map(ToString::to_string).collect::<Vec<_>>().join(",") |
||||||
|
)?; |
||||||
|
|
||||||
|
write!(f, "# bearing off? {}, born off: {}, to place: {}\n", |
||||||
|
if self.board.can_bear_off(self.turn) { "YES" } else { "no" }, |
||||||
|
self.player.born_off, |
||||||
|
self.player.to_place |
||||||
|
)?; |
||||||
|
|
||||||
|
for n in 0..=23 { |
||||||
|
write!(f, "{:02} ", n)?; |
||||||
|
} |
||||||
|
f.write_char('\n')?; |
||||||
|
|
||||||
|
for n in 0..=23 { |
||||||
|
match self.board.0[n] { |
||||||
|
Bin::Empty => { |
||||||
|
write!(f, "--- ", )?; |
||||||
|
}, |
||||||
|
Bin::Black(i) => { |
||||||
|
write!(f, "K{:<3} ", i)?; |
||||||
|
}, |
||||||
|
Bin::White(i) => { |
||||||
|
write!(f, "W{:<3} ", i)?; |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
f.write_str("\n")?; |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// impl Display for Game {
|
||||||
|
// fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
// write!(f, "{}'s turn, roll {}+{}", self.turn, self.roll.0, self.roll.1)?;
|
||||||
|
//
|
||||||
|
// let pl = self.cur_player();
|
||||||
|
//
|
||||||
|
// if pl.bearing_off {
|
||||||
|
// write!(f, ", born off {}/15", pl.born_off)?;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if pl.on_bar > 0 {
|
||||||
|
// write!(f, ", {} on bar!", pl.on_bar)?;
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// write!(f, ", normal move")?;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// f.write_str("\n")?;
|
||||||
|
//
|
||||||
|
// for n in (13..=24) {
|
||||||
|
// write!(f, "{} ", ('m' as u8 + (n - 13) as u8) as char)?;
|
||||||
|
// }
|
||||||
|
// write!(f, "\n")?;
|
||||||
|
//
|
||||||
|
// for (i, b) in (&self.bins[12..=23]).iter().enumerate() {
|
||||||
|
// match *b {
|
||||||
|
// Bin::Empty => {
|
||||||
|
// if i%2==0 {
|
||||||
|
// write!(f, "░░")?
|
||||||
|
// } else {
|
||||||
|
// write!(f, "▒▒")?
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// Bin::Black(n) => write!(f, "{:X}#", n)?,
|
||||||
|
// Bin::White(n) => write!(f, "{:X}:", n)?
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// write!(f, "|:{}\n", self.players[Side::Black as usize].born_off)?;
|
||||||
|
//
|
||||||
|
// for (i, b) in (&self.bins[0..=11]).iter().rev().enumerate() {
|
||||||
|
// match *b {
|
||||||
|
// Bin::Empty => {
|
||||||
|
// if i%2==0 {
|
||||||
|
// write!(f, "░░")?
|
||||||
|
// } else {
|
||||||
|
// write!(f, "▒▒")?
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// Bin::Black(n) => write!(f, "{:X}#", n)?, //█
|
||||||
|
// Bin::White(n) => write!(f, "{:X}:", n)? //░
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// write!(f, "|#{}\n", self.players[Side::Black as usize].born_off)?;
|
||||||
|
// for n in (1..=12).rev() {
|
||||||
|
// write!(f, "{} ", ('a' as u8 + (n-1) as u8) as char)?;
|
||||||
|
// }
|
||||||
|
// write!(f, "\n")?;
|
||||||
|
//
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
@ -1,134 +1,31 @@ |
|||||||
use std::default::Default; |
use crate::game::{Move, Color, Roll}; |
||||||
use std::fmt; |
|
||||||
use std::fmt::Display; |
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy,Eq,PartialEq)] |
mod game; |
||||||
pub enum Side { |
|
||||||
Black = 0, |
|
||||||
White = 1, |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy)] |
|
||||||
pub enum Phase { |
|
||||||
PickSides, |
|
||||||
Turn(Side), |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug,Clone,Default)] |
|
||||||
pub struct Player { |
|
||||||
pub born_off: u32, |
|
||||||
pub on_bar: u32, |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy)] |
|
||||||
pub enum Bin { |
|
||||||
Empty, |
|
||||||
Black(u32), |
|
||||||
White(u32), |
|
||||||
} |
|
||||||
|
|
||||||
#[derive(Debug)] |
|
||||||
pub struct Game { |
|
||||||
pub phase : Phase, |
|
||||||
pub multiplier : u32, |
|
||||||
pub roll : [u8; 2], |
|
||||||
pub players : [Player; 2], |
|
||||||
pub bins : [Bin; 24], |
|
||||||
} |
|
||||||
|
|
||||||
impl Default for Game { |
fn main() { |
||||||
fn default() -> Self { |
let g = game::State::start(Some(Color::Black)); |
||||||
Game { |
|
||||||
phase: Phase::PickSides, |
|
||||||
multiplier: 1, |
|
||||||
roll: [0; 2], |
|
||||||
players: [ |
|
||||||
Player::default(), |
|
||||||
Player::default(), |
|
||||||
], |
|
||||||
bins: [Bin::Empty; 24], |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl Display for Game { |
|
||||||
fn fmt(&self, f : &mut fmt::Formatter<'_>) -> fmt::Result { |
|
||||||
for n in (13..=24) { |
|
||||||
write!(f, "{} ", ('D' as u8 + (n - 13) as u8) as char)?; |
|
||||||
} |
|
||||||
write!(f, "\n")?; |
|
||||||
|
|
||||||
for (i, b) in (&self.bins[12..=23]).iter().enumerate() { |
|
||||||
match *b { |
|
||||||
Bin::Empty => { |
|
||||||
if i%2==0 { |
|
||||||
write!(f, "░░")? |
|
||||||
} else { |
|
||||||
write!(f, "▒▒")? |
|
||||||
} |
|
||||||
}, |
|
||||||
Bin::Black(n) => write!(f, "{:X}#", n)?, |
|
||||||
Bin::White(n) => write!(f, "{:X}:", n)? |
|
||||||
} |
|
||||||
} |
|
||||||
write!(f, "|:{}\n", self.players[Side::Black as usize].born_off)?; |
|
||||||
|
|
||||||
for (i, b) in (&self.bins[0..=11]).iter().rev().enumerate() { |
|
||||||
match *b { |
|
||||||
Bin::Empty => { |
|
||||||
if i%2==0 { |
|
||||||
write!(f, "░░")? |
|
||||||
} else { |
|
||||||
write!(f, "▒▒")? |
|
||||||
} |
|
||||||
}, |
|
||||||
Bin::Black(n) => write!(f, "{:X}#", n)?, //█
|
|
||||||
Bin::White(n) => write!(f, "{:X}:", n)? //░
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
write!(f, "|#{}\n", self.players[Side::Black as usize].born_off)?; |
let g = g.spoof_roll(Roll::from_dice((3, 5))); |
||||||
for n in (1..=12).rev() { |
|
||||||
write!(f, "{:X} ", n)?; |
|
||||||
} |
|
||||||
write!(f, "\n")?; |
|
||||||
|
|
||||||
Ok(()) |
println!("{}", g); |
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
impl Game { |
let g = g.apply_move(Color::Black, Move::InBoard { from: 23, to: 20 }).unwrap(); |
||||||
fn clear(&mut self) { |
let g = g.apply_move(Color::Black, Move::InBoard { from: 12, to: 7 }).unwrap(); |
||||||
for i in 0..24 { |
|
||||||
self.bins[i] = Bin::Empty; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn set(&mut self, n : u32, val : Bin) { |
// White turn
|
||||||
assert!(n > 0 && n <= 24); |
let g = g.spoof_roll(Roll::from_dice((4, 6))); |
||||||
let i = n - 1; |
println!("{}", g); |
||||||
self.bins[i as usize] = val; |
|
||||||
} |
|
||||||
|
|
||||||
fn starting_position(&mut self) { |
let g = g.apply_move(Color::White, Move::InBoard { from: 23, to: 17 }).unwrap(); |
||||||
self.clear(); |
let g = g.apply_move(Color::White, Move::InBoard { from: 7, to: 3 }).unwrap(); |
||||||
self.set(1, Bin::White(2)); |
|
||||||
self.set(6, Bin::Black(5)); |
|
||||||
self.set(8, Bin::Black(3)); |
|
||||||
self.set(12, Bin::White(5)); |
|
||||||
self.set(13, Bin::Black(5)); |
|
||||||
self.set(17, Bin::White(3)); |
|
||||||
self.set(19, Bin::White(5)); |
|
||||||
self.set(24, Bin::Black(2)); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
fn main() { |
// Black turn, need to place the hit stone
|
||||||
let mut board = Game::default(); |
let g = g.spoof_roll(Roll::from_dice((1, 2))); |
||||||
|
println!("{}", g); |
||||||
board.starting_position(); |
|
||||||
|
|
||||||
println!("{}", board); |
let g = g.apply_move(Color::Black, Move::Enter(22)).unwrap(); |
||||||
|
|
||||||
|
let g = g.apply_move(Color::Black, Move::InBoard { from: 22, to: 21 }).unwrap(); |
||||||
|
|
||||||
|
println!("{}", g); |
||||||
} |
} |
||||||
|
Loading…
Reference in new issue