parent
							
								
									4c795da09b
								
							
						
					
					
						commit
						109220a3f9
					
				@ -1,5 +1,84 @@ | 
				
			||||
# This file is automatically @generated by Cargo. | 
				
			||||
# 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]] | 
				
			||||
name = "gammon" | 
				
			||||
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 std::fmt; | 
				
			||||
use std::fmt::Display; | 
				
			||||
use crate::game::{Move, Color, Roll}; | 
				
			||||
 | 
				
			||||
#[derive(Debug,Clone,Copy,Eq,PartialEq)] | 
				
			||||
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], | 
				
			||||
} | 
				
			||||
mod game; | 
				
			||||
 | 
				
			||||
impl Default for Game { | 
				
			||||
    fn default() -> Self { | 
				
			||||
        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)? //░
 | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
fn main() { | 
				
			||||
    let g = game::State::start(Some(Color::Black)); | 
				
			||||
 | 
				
			||||
        write!(f, "|#{}\n", self.players[Side::Black as usize].born_off)?; | 
				
			||||
        for n in (1..=12).rev() { | 
				
			||||
            write!(f, "{:X} ", n)?; | 
				
			||||
        } | 
				
			||||
        write!(f, "\n")?; | 
				
			||||
    let g = g.spoof_roll(Roll::from_dice((3, 5))); | 
				
			||||
 | 
				
			||||
        Ok(()) | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
    println!("{}", g); | 
				
			||||
 | 
				
			||||
impl Game { | 
				
			||||
    fn clear(&mut self) { | 
				
			||||
        for i in 0..24 { | 
				
			||||
            self.bins[i] = Bin::Empty; | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
    let g = g.apply_move(Color::Black, Move::InBoard { from: 23, to: 20 }).unwrap(); | 
				
			||||
    let g = g.apply_move(Color::Black, Move::InBoard { from: 12, to: 7 }).unwrap(); | 
				
			||||
 | 
				
			||||
    fn set(&mut self, n : u32, val : Bin) { | 
				
			||||
        assert!(n > 0 && n <= 24); | 
				
			||||
        let i = n - 1; | 
				
			||||
        self.bins[i as usize] = val; | 
				
			||||
    } | 
				
			||||
    // White turn
 | 
				
			||||
    let g = g.spoof_roll(Roll::from_dice((4, 6))); | 
				
			||||
    println!("{}", g); | 
				
			||||
 | 
				
			||||
    fn starting_position(&mut self) { | 
				
			||||
        self.clear(); | 
				
			||||
        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)); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
    let g = g.apply_move(Color::White, Move::InBoard { from: 23, to: 17 }).unwrap(); | 
				
			||||
    let g = g.apply_move(Color::White, Move::InBoard { from: 7, to: 3 }).unwrap(); | 
				
			||||
 | 
				
			||||
fn main() { | 
				
			||||
    let mut board = Game::default(); | 
				
			||||
    
 | 
				
			||||
    board.starting_position(); | 
				
			||||
    // Black turn, need to place the hit stone
 | 
				
			||||
    let g = g.spoof_roll(Roll::from_dice((1, 2))); | 
				
			||||
    println!("{}", g); | 
				
			||||
 | 
				
			||||
    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