@ -3,6 +3,7 @@ use std::{fmt, mem};
use std ::fmt ::{ Display , Formatter , Write } ;
use rand ::{ Rng , rngs ::OsRng } ;
use std ::borrow ::Borrow ;
use permutator ::copy ::k_permutation ;
#[ derive(Debug,Clone) ]
struct Board ( [ Bin ; 24 ] ) ;
@ -176,7 +177,7 @@ struct Player {
#[ derive(Debug, Clone) ]
pub struct State {
move_number : usize ,
turn_number : usize ,
turn : Color ,
board : Board ,
roll : Roll ,
@ -186,9 +187,9 @@ pub struct State {
#[ derive(Debug,Clone,Copy) ]
pub enum Move {
InBoard ( u8 , u8 ) ,
Enter ( u8 ) ,
BearOff ( u8 )
InBoard ( /* from */ u8 , /* to */ u8 ) ,
Enter ( /* pos */ u8 ) ,
BearOff ( /* pos */ u8 )
}
#[ derive(Debug) ]
@ -283,7 +284,7 @@ impl State {
pub fn start ( turn : Option < Color > ) -> Self {
// The state is laid out for the white player
let mut state = State {
move _number : 0 ,
turn _number : 0 ,
turn : Color ::White ,
board : Board ( [
// 1 .. 12 (bottom row, right to left)
@ -325,10 +326,14 @@ impl State {
state
}
/// Get turn info. Returns a tuple of `(turn_number, turn_color)`, where
/// `turn_number` is incremented after each turn ends (can be used to detect
/// automatic turn skips that result in the same player playing again.
pub fn turn ( & self ) -> ( usize , Color ) {
( self . move_number , self . turn )
( self . turn _number, self . turn )
}
/// Get reference to own player struct
fn player ( & self ) -> & Player {
match self . turn {
Color ::Black = > & self . black ,
@ -336,6 +341,7 @@ impl State {
}
}
/// Get mutable reference to own player struct
fn player_mut ( & mut self ) -> & mut Player {
match self . turn {
Color ::Black = > & mut self . black ,
@ -343,6 +349,7 @@ impl State {
}
}
/// Get reference to the other player's struct
fn other ( & self ) -> & Player {
match self . turn {
Color ::Black = > & self . white ,
@ -350,6 +357,7 @@ impl State {
}
}
/// Get mutable reference to the other player's struct
fn other_mut ( & mut self ) -> & mut Player {
match self . turn {
Color ::Black = > & mut self . white ,
@ -357,19 +365,21 @@ impl State {
}
}
/// Apply a move to the board.
///
/// turn must match the current turn's color, move must be valid for that player.
pub fn apply_move ( & self , turn : Color , mv : Move ) -> Result < Self , Error > {
if turn ! = self . turn {
return Err ( Error ::NotYourTurn ) ;
}
print! ( "{} plays move: " , turn ) ;
// print!("{} plays move: ", turn);
let mut next = self . clone ( ) ;
match mv {
Move ::InBoard ( from , to ) = > {
println! ( "In-board {} -> {}" , from , to ) ;
// println!("In-board {} -> {}", from, to);
if ( self . turn = = Color ::White & & to > = from ) | | ( self . turn = = Color ::Black & & to < = from ) {
return Err ( Error ::MalformedMove ) ;
@ -397,7 +407,7 @@ impl State {
if let Some ( c ) = old_color {
if c ! = self . turn {
println! ( "{} stone at position {} is hit." , c , to ) ;
// println!("{} stone at position {} is hit.", c, to);
// hit opposite color
next . other_mut ( ) . to_place + = 1 ;
@ -405,7 +415,7 @@ impl State {
}
} ,
Move ::Enter ( pos ) = > {
println! ( "Enter -> {}" , pos ) ;
// println!("Enter -> {}", pos);
if self . player ( ) . to_place = = 0 {
return Err ( Error ::NothingToPlace ) ;
@ -436,14 +446,14 @@ impl State {
if let Some ( c ) = old_color {
if c ! = self . turn {
println! ( "{} stone at position {} is hit." , c , pos ) ;
// println!("{} stone at position {} is hit.", c, pos);
// hit opposite color
next . other_mut ( ) . to_place + = 1 ;
}
}
} ,
Move ::BearOff ( pos ) = > {
println! ( "Bear off -> {}" , pos ) ;
// println!("Bear off -> {}", pos);
if ( self . turn = = Color ::White & & pos > HOME_MAX ) | | ( self . turn = = Color ::Black & & pos < 18 ) {
return Err ( Error ::MalformedMove ) ;
@ -469,25 +479,128 @@ impl State {
}
if next . roll . remaining_moves . is_empty ( ) {
next . turn = self . turn . opposite ( ) ;
next . move_number + = 1 ;
next . roll_mut ( ) ;
// TODO check if any moves are possible with this roll, if not, auto-switch again
next = next . yield_turn ( ) ;
}
Ok ( next )
}
pub fn yield_turn ( & self ) -> Self {
let mut next = self . clone ( ) ;
next . turn = self . turn . opposite ( ) ;
next . turn_number + = 1 ;
next . roll_mut ( ) ;
next
}
/// Inject a faked dice roll
pub fn spoof_roll ( & self , roll : Roll ) -> Self {
let mut next = self . clone ( ) ;
next . roll = roll ;
next
}
/// Rull dice (in place)
fn roll_mut ( & mut self ) {
self . roll = Roll ::new ( ) ;
}
pub fn get_max_number_of_possible_moves_with_current_roll ( & self ) -> usize {
let mut max = 0 ;
let color = self . turn ;
let turn_number = self . turn_number ;
k_permutation ( & self . roll . remaining_moves , self . roll . remaining_moves . len ( ) , | permuted | {
if max = = self . roll . remaining_moves . len ( ) {
// There is nothing more to look for, discard the remaining iterations (sadly, can't break;)
return ;
}
let mut to_try = vec! [ self . clone ( ) ] ;
let mut to_try_next_roll = vec! [ ] ;
for ( n , roll ) in permuted . iter ( ) . copied ( ) . enumerate ( ) {
let depth = n + 1 ;
to_try_next_roll . clear ( ) ;
for state in to_try . drain ( .. ) {
for mv in PossibleMovesOfLen ::new ( & state , roll ) {
if let Ok ( next ) = state . apply_move ( color , mv ) {
max = max . max ( depth ) ;
if next . turn_number = = turn_number {
to_try_next_roll . push ( next ) ;
}
}
}
}
std ::mem ::swap ( & mut to_try , & mut to_try_next_roll )
}
} ) ;
max
}
}
struct PossibleMovesOfLen < ' a > {
state : & ' a State ,
pos : i8 ,
step : i8 ,
len : u8
}
impl < ' a > PossibleMovesOfLen < ' a > {
pub fn new ( state : & ' a State , len : u8 ) -> Self {
Self {
state ,
pos : - 1 ,
step : if state . turn = = Color ::White {
- ( len as i8 )
} else {
len as i8
} ,
len
}
}
}
impl < ' a > Iterator for PossibleMovesOfLen < ' a > {
type Item = Move ;
fn next ( & mut self ) -> Option < Move > {
if self . pos > 23 {
return None ;
}
let state = self . state ;
if state . player ( ) . to_place > 0 {
let p = if state . turn = = Color ::White {
24 - self . len
} else {
self . len
} ;
if state . board . can_place ( p , state . turn ) {
self . pos = 24 ; // no more moves
return Some ( Move ::Enter ( p ) ) ;
}
}
' next : loop {
self . pos + = 1 ;
if self . pos > 23 {
return None ;
}
if state . board . can_move_from ( self . pos as u8 , state . turn ) {
let dest = self . pos + self . step ;
if dest < 0 | | dest > 23 {
return Some ( Move ::BearOff ( self . pos as u8 ) ) ;
} else {
if state . board . can_place ( dest as u8 , state . turn ) {
return Some ( Move ::InBoard ( self . pos as u8 , dest as u8 ) ) ;
}
}
}
}
}
}
impl Display for State {
@ -505,11 +618,17 @@ impl Display for State {
self . player ( ) . to_place
) ? ;
f . write_str ( "White <- " ) ? ;
if self . turn = = Color ::White {
f . write_str ( "\x1b[1m" ) ? ;
}
f . write_str ( "White <- \x1b[m" ) ? ;
for n in 0 ..= 23 {
write! ( f , "{:02} " , n + 1 ) ? ;
}
f . write_str ( " -> Black\n" ) ? ;
if self . turn = = Color ::Black {
f . write_str ( "\x1b[1m" ) ? ;
}
f . write_str ( " -> Black\x1b[m\n" ) ? ;
f . write_str ( " " ) ? ;
for n in 0 ..= 23 {