From 82f2713dca77831e6ed41575defae3053af6b7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Mon, 4 May 2020 00:09:31 +0200 Subject: [PATCH] more game logic --- Cargo.lock | 319 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/game.rs | 221 +++++++++++++++++++++--------------- src/main.rs | 158 +++++++++++++++++++++++--- 4 files changed, 595 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54ca023..dcc830c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,148 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + +[[package]] +name = "backtrace" +version = "0.3.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +dependencies = [ + "backtrace-sys", + "cfg-if", + "libc", + "rustc-demangle", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fbebbe1c9d1f383a9cc7e8ccdb471b91c8d024ee9c2ca5b5346121fe8b4399" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2b_simd" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "cc" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" + [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" +dependencies = [ + "cfg-if", + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "gammon" version = "0.1.0" dependencies = [ + "failure", "rand", + "rustyline", ] [[package]] @@ -24,18 +156,70 @@ dependencies = [ "wasi", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + [[package]] name = "ppv-lite86" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.7.3" @@ -77,8 +261,143 @@ dependencies = [ "rand_core", ] +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "redox_users" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" +dependencies = [ + "getrandom", + "redox_syscall", + "rust-argon2", +] + +[[package]] +name = "rust-argon2" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" +dependencies = [ + "base64", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "rustyline" +version = "6.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd20b28d972040c627e209eb29f19c24a71a19d661cc5a220089176e20ee202" +dependencies = [ + "cfg-if", + "dirs", + "libc", + "log", + "memchr", + "nix", + "scopeguard", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "syn" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 5d488e9..324d722 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,5 @@ edition = "2018" [dependencies] rand = "0.7.3" +rustyline = "6.1.2" +failure = "0.1.8" diff --git a/src/game.rs b/src/game.rs index 7292789..f26511f 100644 --- a/src/game.rs +++ b/src/game.rs @@ -9,7 +9,16 @@ struct Board([Bin; 24]); impl Board { fn can_bear_off(&self, color : Color) -> bool { - for i in (HOME_MAX)+1..=BOARD_MAX { + // this range covers the non-home area of the board + let range = if color == Color::White { + // White's home is low indices + 6..=23 + } else { + // Black's home is high indices + 0..=17 + }; + + for i in range { match (self.0)[i as usize].as_color() { Some(c) if c == color => { return false; @@ -67,10 +76,18 @@ impl Board { #[derive(Debug,Clone,Copy,PartialEq,Eq)] pub enum Color { + /// Black, home area 18..=23 Black, + /// White, home area 0..=5 White, } +impl Display for Color { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) // re-use the debug renderer + } +} + impl Color { fn opposite(self) -> Color { match self { @@ -111,8 +128,20 @@ impl Bin { Color::White => Bin::White(1), } }, - Bin::Black(n) => Bin::Black(*n + 1), - Bin::White(n) => Bin::White(*n + 1), + Bin::Black(n) => { + if color == Color::Black { + Bin::Black(*n + 1) + } else { + Bin::White(1) + } + }, + Bin::White(n) => { + if color == Color::White { + Bin::White(*n + 1) + } else { + Bin::Black(1) + } + } }); } @@ -147,19 +176,17 @@ struct Player { #[derive(Debug, Clone)] pub struct State { + move_number : usize, turn : Color, board: Board, roll: Roll, - player : Player, - other : Player, + black : Player, + white : Player, } #[derive(Debug,Clone,Copy)] pub enum Move { - InBoard { - from: u8, - to : u8 - }, + InBoard(u8, u8), Enter(u8), BearOff(u8) } @@ -256,6 +283,7 @@ impl State { pub fn start(turn : Option) -> Self { // The state is laid out for the white player let mut state = State { + move_number: 0, turn: Color::White, board: Board([ // 1 .. 12 (bottom row, right to left) @@ -286,26 +314,68 @@ impl State { 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 } + black: Player { born_off: 0, to_place: 0 }, + white: 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.turn = Color::Black; } state } - pub fn check_move(&self, mv : Move) -> Result<(), Error> { + pub fn turn(&self) -> (usize, Color) { + (self.move_number, self.turn) + } + + fn player(&self) -> &Player { + match self.turn { + Color::Black => &self.black, + Color::White => &self.white, + } + } + + fn player_mut(&mut self) -> &mut Player { + match self.turn { + Color::Black => &mut self.black, + Color::White => &mut self.white, + } + } + + fn other(&self) -> &Player { + match self.turn { + Color::Black => &self.white, + Color::White => &self.black, + } + } + + fn other_mut(&mut self) -> &mut Player { + match self.turn { + Color::Black => &mut self.white, + Color::White => &mut self.black, + } + } + + + pub fn apply_move(&self, turn : Color, mv : Move) -> Result { + if turn != self.turn { + return Err(Error::NotYourTurn); + } + + print!("{} plays move: ", turn); + + let mut next = self.clone(); + match mv { - Move::InBoard{from, to} => { - if to > from { - // can only go lower + Move::InBoard(from, to) => { + println!("In-board {} -> {}", from, to); + + if (self.turn == Color::White && to >= from) || (self.turn == Color::Black && to <= from) { return Err(Error::MalformedMove); } - if self.player.to_place != 0 { + if self.player().to_place != 0 { return Err(Error::NotAllPlaced); } @@ -317,15 +387,27 @@ impl State { return Err(Error::TargetOccupied); } - let needed = from - to; + let needed = (from as i8 - to as i8).abs() as u8; if !self.roll.have_equal(needed) { return Err(Error::NoMatchingRoll); } - Ok(()) + let old_color = next.board.apply_move_mut(from, to); + 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_mut().to_place += 1; + } + } }, Move::Enter(pos) => { - if self.player.to_place == 0 { + println!("Enter -> {}", pos); + + if self.player().to_place == 0 { return Err(Error::NothingToPlace); } @@ -333,7 +415,11 @@ impl State { return Err(Error::MalformedMove); } - let needed = 24 - pos; + let needed = if self.turn == Color::White { + 24 - pos + } else { + pos + 1 + }; if !self.roll.have_equal(needed) { return Err(Error::NoMatchingRoll); @@ -343,14 +429,27 @@ impl State { return Err(Error::TargetOccupied); } - Ok(()) + let old_color = next.board.apply_enter_mut(pos, self.turn); + + next.roll.remove_mut(needed); + next.player_mut().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_mut().to_place += 1; + } + } }, Move::BearOff(pos) => { - if pos > HOME_MAX { + println!("Bear off -> {}", pos); + + if (self.turn == Color::White && pos > HOME_MAX) || (self.turn == Color::Black && pos < 18) { return Err(Error::MalformedMove); } - if self.player.to_place != 0 { + if self.player().to_place != 0 { return Err(Error::NotAllPlaced); } @@ -364,66 +463,14 @@ impl State { // TODO must always bear off the highest possible, if multiple checkers are available } - Ok(()) - }, - } - } - - pub fn apply_move(&self, turn : Color, mv : Move) -> Result { - 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.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 @@ -441,17 +488,11 @@ impl State { 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", + write!(f, "\n# {} turn, roll ({},{}), moves to play: [{}]\n", self.turn, self.roll.dice.0, self.roll.dice.1, @@ -460,14 +501,16 @@ impl Display for State { 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 + self.player().born_off, + self.player().to_place )?; + f.write_str("White <- ")?; for n in 0..=23 { - write!(f, "{:02} ", n)?; + write!(f, "{:02} ", n+1)?; } - f.write_char('\n')?; + f.write_str(" -> Black\n")?; + f.write_str(" ")?; for n in 0..=23 { match self.board.0[n] { diff --git a/src/main.rs b/src/main.rs index 2dbe74f..38394fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,157 @@ use crate::game::{Move, Color, Roll}; +use rustyline::error::ReadlineError; +use failure::{Fallible, format_err}; mod game; fn main() { - let g = game::State::start(Some(Color::Black)); + let mut game = game::State::start(None); - let g = g.spoof_roll(Roll::from_dice((3, 5))); + let mut rl = rustyline::Editor::<()>::new(); - println!("{}", g); + let mut retry = false; + let mut filled = String::new(); + 'input: loop { + if !retry { + filled.clear(); + } - 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(); + println!("BOARD:\n{}", game); - // White turn - let g = g.spoof_roll(Roll::from_dice((4, 6))); - println!("{}", g); + let (sn, color) = game.turn(); + let cmd = rl.readline_with_initial(&format!("{}> ", color), (&filled, "")); + retry = false; + match cmd { + Ok(line) => { + filled = line; - 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(); + let line = filled.trim(); + if line.is_empty() { + retry = true; + continue 'input; + } - // Black turn, need to place the hit stone - let g = g.spoof_roll(Roll::from_dice((1, 2))); - println!("{}", g); + match parse_input(line) { + Ok(moves) => { + println!("{:?}", moves); + let mut next = game.clone(); + for m in moves { + match next.apply_move(color, m) { + Ok(n) => { + next = n; + println!("{}", next); + } + Err(e) => { + println!("Move failed: {:?}", e); + retry = true; + continue 'input; + } + } + } + if next.turn().0 != sn { + game = next; + } else { + println!("Move NOT complete, reverting."); + retry = true; + } + }, + Err(e) => { + println!("Parsing failed: {}", e); + retry = true; + continue 'input; + }, + } + }, + // Err(ReadlineError::Interrupted) => { + // // just clear the line and retry + // retry = true; + // continue 'input; + // } + Err(_) => { + break 'input; + }, + } + } - let g = g.apply_move(Color::Black, Move::Enter(22)).unwrap(); + // let g = g.spoof_roll(Roll::from_dice((3, 5))); + // + // println!("{}", g); + // + // let g = g.apply_move(Color::Black, Move::InBoard { from: 0, to: 3 }).unwrap(); + // let g = g.apply_move(Color::Black, Move::InBoard { from: 11, to: 16 }).unwrap(); + // + // // White turn + // let g = g.spoof_roll(Roll::from_dice((4, 6))); + // println!("{}", g); + // + // 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(); + // + // // Black turn, need to place the hit stone + // let g = g.spoof_roll(Roll::from_dice((1, 2))); + // println!("{}", g); + // + // let g = g.apply_move(Color::Black, Move::Enter(1)).unwrap(); + // + // let g = g.apply_move(Color::Black, Move::InBoard { from: 3, to: 4 }).unwrap(); + // + // println!("{}", g); +} + + +fn parse_input(line : &str) -> Fallible> { + let line = line.trim(); + + let mut moves = vec![]; + let fragments = line.split(','); + + // let mut moves = vec![]; + for f in fragments { + if !f.contains('/') { + return Err(format_err!("Bad syntax: \"{}\"", f)); + } + + let halves : Vec<&str> = f.split('/').collect(); + + if halves[0].is_empty() && halves[1].is_empty() { + return Err(format_err!("Bad syntax: \"{}\"", f)); + } + + if halves[0].is_empty() { + let to : u8 = halves[1].parse()?; + + if to == 0 || to > 24 { + return Err(format_err!("Out of range: {}", to)); + } + + moves.push(Move::Enter(to - 1)); + continue; + } + + if halves[1].is_empty() { + let to : u8 = halves[0].parse()?; + + if to == 0 || to > 24 { + return Err(format_err!("Out of range: {}", to)); + } + + moves.push(Move::BearOff(to - 1)); + continue; + } + + let from : u8 = halves[0].parse()?; + let to : u8 = halves[1].parse()?; + + if from == 0 || from > 24 { + return Err(format_err!("Out of range: {}", to)); + } + + if to == 0 || to > 24 { + return Err(format_err!("Out of range: {}", to)); + } - let g = g.apply_move(Color::Black, Move::InBoard { from: 22, to: 21 }).unwrap(); + moves.push(Move::InBoard(from - 1, to - 1)); + } - println!("{}", g); + Ok(moves) }