possible moves detection, auto yield if none, accpet less than max used if not possible

master
Ondřej Hruška 4 years ago
parent 82f2713dca
commit ab4f2b0f1c
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 87
      Cargo.lock
  2. 1
      Cargo.toml
  3. BIN
      out.png
  4. BIN
      src/DOSVGA.ttf
  5. BIN
      src/DejaVuSans.ttf
  6. BIN
      src/coders_crux.ttf
  7. BIN
      src/fixedsys.ttf
  8. 159
      src/game.rs
  9. BIN
      src/lilliput steps.ttf
  10. 24
      src/main.rs

87
Cargo.lock generated

@ -141,6 +141,7 @@ name = "gammon"
version = "0.1.0"
dependencies = [
"failure",
"permutator",
"rand",
"rustyline",
]
@ -196,6 +197,92 @@ dependencies = [
"void",
]
[[package]]
name = "num"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
dependencies = [
"autocfg",
]
[[package]]
name = "permutator"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa5132833890643cdaeb330ae7cf7da92e0e11c8729f6f529140666213876a0b"
dependencies = [
"num",
]
[[package]]
name = "ppv-lite86"
version = "0.2.6"

@ -10,3 +10,4 @@ edition = "2018"
rand = "0.7.3"
rustyline = "6.1.2"
failure = "0.1.8"
permutator = "0.4.0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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 {

Binary file not shown.

@ -18,6 +18,16 @@ fn main() {
println!("BOARD:\n{}", game);
// TODO
let pos_moves = game.get_max_number_of_possible_moves_with_current_roll();
println!("Possible moves with current roll = {}", pos_moves);
if pos_moves == 0 {
println!("NO MOVES - YIELD!");
game = game.yield_turn();
continue 'input;
}
let (sn, color) = game.turn();
let cmd = rl.readline_with_initial(&format!("{}> ", color), (&filled, ""));
retry = false;
@ -34,15 +44,21 @@ fn main() {
match parse_input(line) {
Ok(moves) => {
println!("{:?}", moves);
if moves.len() < pos_moves {
println!("It is possible to use {} moves (given {}), please try again.", pos_moves, moves.len());
retry = true;
continue 'input;
}
let mut next = game.clone();
for m in moves {
for (nth, m) in moves.into_iter().enumerate() {
match next.apply_move(color, m) {
Ok(n) => {
next = n;
println!("{}", next);
}
Err(e) => {
println!("Move failed: {:?}", e);
println!("Move #{} ({:?}) failed: {:?}", nth+1, m, e);
retry = true;
continue 'input;
}
@ -103,7 +119,7 @@ fn parse_input(line : &str) -> Fallible<Vec<Move>> {
let line = line.trim();
let mut moves = vec![];
let fragments = line.split(',');
let fragments = line.split(|c: char| c==','||c==';'||c==' ');
// let mut moves = vec![];
for f in fragments {

Loading…
Cancel
Save