refact
This commit is contained in:
parent
6a0dc9395a
commit
2139de2fcd
|
|
@ -44,7 +44,7 @@ impl Bot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume(&mut self, event: &GameEvent) -> Option<GameEvent> {
|
pub fn handle_event(&mut self, event: &GameEvent) -> Option<GameEvent> {
|
||||||
self.game.consume(event);
|
self.game.consume(event);
|
||||||
// println!("bot game {:?}", self.game);
|
// println!("bot game {:?}", self.game);
|
||||||
// println!("bot player_id {:?}", self.player_id);
|
// println!("bot player_id {:?}", self.player_id);
|
||||||
|
|
@ -98,13 +98,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_consume() {
|
fn test_consume() {
|
||||||
let mut bot = Bot::new(Color::Black);
|
let mut bot = Bot::new(Color::Black);
|
||||||
let mut event = bot.consume(&GameEvent::BeginGame { goes_first: 2 });
|
let mut event = bot.handle_event(&GameEvent::BeginGame { goes_first: 2 });
|
||||||
assert_eq!(event, Some(GameEvent::Roll { player_id: 2 }));
|
assert_eq!(event, Some(GameEvent::Roll { player_id: 2 }));
|
||||||
|
|
||||||
event = bot.consume(&GameEvent::BeginGame { goes_first: 1 });
|
event = bot.handle_event(&GameEvent::BeginGame { goes_first: 1 });
|
||||||
assert_eq!(event, None);
|
assert_eq!(event, None);
|
||||||
|
|
||||||
event = bot.consume(&GameEvent::RollResult {
|
bot.handle_event(&GameEvent::RollResult {
|
||||||
player_id: 2,
|
player_id: 2,
|
||||||
dice: Dice { values: (2, 3) },
|
dice: Dice { values: (2, 3) },
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ impl Game {
|
||||||
player_id,
|
player_id,
|
||||||
bot,
|
bot,
|
||||||
};
|
};
|
||||||
game.consume(&GameEvent::BeginGame {
|
game.handle_event(&GameEvent::BeginGame {
|
||||||
goes_first: player_id.unwrap(),
|
goes_first: player_id.unwrap(),
|
||||||
});
|
});
|
||||||
game
|
game
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn consume(&mut self, event: &GameEvent) -> Option<GameEvent> {
|
pub fn handle_event(&mut self, event: &GameEvent) -> Option<GameEvent> {
|
||||||
if !self.state.validate(event) {
|
if !self.state.validate(event) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
@ -50,13 +50,12 @@ impl Game {
|
||||||
// chain all successive bot actions
|
// chain all successive bot actions
|
||||||
let bot_event = self
|
let bot_event = self
|
||||||
.bot
|
.bot
|
||||||
.consume(event)
|
.handle_event(event)
|
||||||
.map(|evt| self.consume(&evt))
|
.and_then(|evt| self.handle_event(&evt));
|
||||||
.flatten();
|
|
||||||
// roll dice for bot if needed
|
// roll dice for bot if needed
|
||||||
if self.bot_needs_dice_roll() {
|
if self.bot_needs_dice_roll() {
|
||||||
let dice = self.dice_roller.roll();
|
let dice = self.dice_roller.roll();
|
||||||
self.consume(&GameEvent::RollResult {
|
self.handle_event(&GameEvent::RollResult {
|
||||||
player_id: self.bot.player_id,
|
player_id: self.bot.player_id,
|
||||||
dice,
|
dice,
|
||||||
})
|
})
|
||||||
|
|
@ -127,7 +126,7 @@ impl App {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let dice = self.game.dice_roller.roll();
|
let dice = self.game.dice_roller.roll();
|
||||||
self.game.consume(&GameEvent::RollResult {
|
self.game.handle_event(&GameEvent::RollResult {
|
||||||
player_id: self.game.player_id.unwrap(),
|
player_id: self.game.player_id.unwrap(),
|
||||||
dice,
|
dice,
|
||||||
});
|
});
|
||||||
|
|
@ -143,22 +142,22 @@ impl App {
|
||||||
.map(|str| str.parse().unwrap_or(0))
|
.map(|str| str.parse().unwrap_or(0))
|
||||||
.collect();
|
.collect();
|
||||||
if positions.len() == 2 && positions[0] != 0 && positions[1] != 0 {
|
if positions.len() == 2 && positions[0] != 0 && positions[1] != 0 {
|
||||||
let checker_move = CheckerMove::new(positions[0], positions[1]);
|
if let Ok(checker_move) = CheckerMove::new(positions[0], positions[1]) {
|
||||||
if checker_move.is_ok() {
|
// if checker_move.is_ok() {
|
||||||
if self.game.first_move.is_some() {
|
if self.game.first_move.is_some() {
|
||||||
let move_event = GameEvent::Move {
|
let move_event = GameEvent::Move {
|
||||||
player_id: self.game.player_id.unwrap(),
|
player_id: self.game.player_id.unwrap(),
|
||||||
moves: (self.game.first_move.unwrap(), checker_move.unwrap()),
|
moves: (self.game.first_move.unwrap(), checker_move),
|
||||||
};
|
};
|
||||||
if !self.game.state.validate(&move_event) {
|
if !self.game.state.validate(&move_event) {
|
||||||
println!("Move invalid");
|
println!("Move invalid");
|
||||||
self.game.first_move = None;
|
self.game.first_move = None;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.game.consume(&move_event);
|
self.game.handle_event(&move_event);
|
||||||
self.game.first_move = None;
|
self.game.first_move = None;
|
||||||
} else {
|
} else {
|
||||||
self.game.first_move = Some(checker_move.unwrap());
|
self.game.first_move = Some(checker_move);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::player::{Color, Player};
|
use crate::player::Color;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
@ -31,7 +31,7 @@ impl CheckerMove {
|
||||||
// println!("from {} to {}", from, to);
|
// println!("from {} to {}", from, to);
|
||||||
// check if the field is on the board
|
// check if the field is on the board
|
||||||
// we allow 0 for 'to', which represents the exit of a checker
|
// we allow 0 for 'to', which represents the exit of a checker
|
||||||
if from < 1 || 24 < from || 24 < to {
|
if !(1..25).contains(&from) || 24 < to {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
// check that the destination is after the origin field
|
// check that the destination is after the origin field
|
||||||
|
|
@ -98,7 +98,7 @@ impl Board {
|
||||||
// Pieces placement -> 77bits (24 + 23 + 30 max)
|
// Pieces placement -> 77bits (24 + 23 + 30 max)
|
||||||
// inspired by https://www.gnu.org/software/gnubg/manual/html_node/A-technical-description-of-the-Position-ID.html
|
// inspired by https://www.gnu.org/software/gnubg/manual/html_node/A-technical-description-of-the-Position-ID.html
|
||||||
// - white positions
|
// - white positions
|
||||||
let white_board = self.positions.clone();
|
let white_board = self.positions;
|
||||||
let mut pos_bits = white_board.iter().fold(vec![], |acc, nb| {
|
let mut pos_bits = white_board.iter().fold(vec![], |acc, nb| {
|
||||||
let mut new_acc = acc.clone();
|
let mut new_acc = acc.clone();
|
||||||
if *nb > 0 {
|
if *nb > 0 {
|
||||||
|
|
@ -110,7 +110,7 @@ impl Board {
|
||||||
});
|
});
|
||||||
|
|
||||||
// - black positions
|
// - black positions
|
||||||
let mut black_board = self.positions.clone();
|
let mut black_board = self.positions;
|
||||||
black_board.reverse();
|
black_board.reverse();
|
||||||
let mut pos_black_bits = black_board.iter().fold(vec![], |acc, nb| {
|
let mut pos_black_bits = black_board.iter().fold(vec![], |acc, nb| {
|
||||||
let mut new_acc = acc.clone();
|
let mut new_acc = acc.clone();
|
||||||
|
|
@ -192,14 +192,13 @@ impl Board {
|
||||||
line.replace_range(31..31, "| |");
|
line.replace_range(31..31, "| |");
|
||||||
output = output + " |" + &line + " |\n";
|
output = output + " |" + &line + " |\n";
|
||||||
}
|
}
|
||||||
output = output + " |------------------------------ | | -----------------------------|\n";
|
output += " |------------------------------ | | -----------------------------|\n";
|
||||||
for mut line in lower {
|
for mut line in lower {
|
||||||
// add middle bar
|
// add middle bar
|
||||||
line.replace_range(31..31, "| |");
|
line.replace_range(31..31, "| |");
|
||||||
output = output + " |" + &line + " |\n";
|
output = output + " |" + &line + " |\n";
|
||||||
}
|
}
|
||||||
output = output
|
output += " ----------------------------------------------------------------
|
||||||
+ " ----------------------------------------------------------------
|
|
||||||
12 11 10 9 8 7 6 5 4 3 2 1 \n";
|
12 11 10 9 8 7 6 5 4 3 2 1 \n";
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
@ -280,22 +279,20 @@ impl Board {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_field_checkers(&self, field: Field) -> Result<(u8, Option<&Color>), Error> {
|
pub fn get_field_checkers(&self, field: Field) -> Result<(u8, Option<&Color>), Error> {
|
||||||
if field < 1 || field > 24 {
|
if !(1..25).contains(&field) {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
let checkers_count = self.positions[field - 1];
|
let checkers_count = self.positions[field - 1];
|
||||||
let color = if checkers_count < 0 {
|
let color = match checkers_count.cmp(&0) {
|
||||||
Some(&Color::Black)
|
cmp::Ordering::Less => Some(&Color::Black),
|
||||||
} else if checkers_count > 0 {
|
cmp::Ordering::Greater => Some(&Color::White),
|
||||||
Some(&Color::White)
|
cmp::Ordering::Equal => None,
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
Ok((checkers_count.abs() as u8, color))
|
Ok((checkers_count.unsigned_abs(), color))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_checkers_color(&self, field: Field) -> Result<Option<&Color>, Error> {
|
pub fn get_checkers_color(&self, field: Field) -> Result<Option<&Color>, Error> {
|
||||||
self.get_field_checkers(field).map(|(count, color)| color)
|
self.get_field_checkers(field).map(|(_ount, color)| color)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the list of Fields containing Checkers of the Color
|
/// returns the list of Fields containing Checkers of the Color
|
||||||
|
|
@ -357,7 +354,7 @@ impl Board {
|
||||||
pub fn add_checker(&mut self, color: &Color, field: Field) -> Result<(), Error> {
|
pub fn add_checker(&mut self, color: &Color, field: Field) -> Result<(), Error> {
|
||||||
let checker_color = self.get_checkers_color(field)?;
|
let checker_color = self.get_checkers_color(field)?;
|
||||||
// error if the case contains the other color
|
// error if the case contains the other color
|
||||||
if None != checker_color && Some(color) != checker_color {
|
if checker_color.is_some() && Some(color) != checker_color {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
let unit = match color {
|
let unit = match color {
|
||||||
|
|
@ -369,19 +366,6 @@ impl Board {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait to move checkers
|
|
||||||
pub trait Move {
|
|
||||||
/// Move a checker
|
|
||||||
fn move_checker(&mut self, player: &Player, dice: u8, from: Field) -> Result<&mut Self, Error>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// Move permitted
|
|
||||||
fn move_permitted(&mut self, player: &Player, dice: u8) -> Result<&mut Self, Error>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unit Tests
|
// Unit Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
@ -395,7 +379,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn blocked_outofrange() -> Result<(), Error> {
|
fn blocked_outofrange() -> Result<(), Error> {
|
||||||
let board = Board::new();
|
let board = Board::new();
|
||||||
assert!(!board.blocked(&Color::White, 0).is_err());
|
assert!(board.blocked(&Color::White, 0).is_ok());
|
||||||
assert!(board.blocked(&Color::White, 28).is_err());
|
assert!(board.blocked(&Color::White, 28).is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -435,7 +419,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn set_wrong_amount1() {
|
fn set_wrong_amount1() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
let player = Player::new("".into(), Color::White);
|
|
||||||
assert!(board.set(&Color::White, 23, -3).is_err());
|
assert!(board.set(&Color::White, 23, -3).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! # Play a TricTrac Game
|
//! # Play a TricTrac Game
|
||||||
use crate::board::{Board, CheckerMove, Field, Move};
|
use crate::board::{Board, CheckerMove, Field};
|
||||||
use crate::dice::{Dice, DiceRoller, Roll};
|
use crate::dice::{Dice, DiceRoller, Roll};
|
||||||
use crate::player::{Color, Player, PlayerId};
|
use crate::player::{Color, Player, PlayerId};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
@ -502,56 +502,6 @@ pub enum GameEvent {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Move for GameState {
|
|
||||||
fn move_checker(&mut self, player: &Player, dice: u8, from: usize) -> Result<&mut Self, Error> {
|
|
||||||
// check if move is permitted
|
|
||||||
let _ = self.move_permitted(player, dice)?;
|
|
||||||
|
|
||||||
// remove checker from old position
|
|
||||||
self.board.set(&player.color, from, -1)?;
|
|
||||||
|
|
||||||
// move checker to new position, in case it is reaching the off position, set it off
|
|
||||||
let new_position = from as i8 - dice as i8;
|
|
||||||
if new_position < 0 {
|
|
||||||
// self.board.set_off(player, 1)?;
|
|
||||||
} else {
|
|
||||||
// self.board.set(player, new_position as usize, 1)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// switch to other player if all dice have been consumed
|
|
||||||
self.switch_active_player();
|
|
||||||
self.roll_first = true;
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implements checks to validate if the player is allowed to move
|
|
||||||
fn move_permitted(&mut self, player: &Player, dice: u8) -> Result<&mut Self, Error> {
|
|
||||||
let maybe_player_id = self.player_id(&player);
|
|
||||||
// check if player is allowed to move
|
|
||||||
if maybe_player_id != Some(&self.active_player_id) {
|
|
||||||
return Err(Error::NotYourTurn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if player is nobody, you can not play and have to roll first
|
|
||||||
if maybe_player_id.is_none() {
|
|
||||||
return Err(Error::RollFirst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if player has to roll first
|
|
||||||
if self.roll_first {
|
|
||||||
return Err(Error::RollFirst);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if dice value has actually been rolled
|
|
||||||
if dice != self.dice.values.0 && dice != self.dice.values.1 {
|
|
||||||
return Err(Error::DiceInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue