check corner moves

This commit is contained in:
Henri Bourcereau 2024-01-29 21:42:40 +01:00
parent 06aeed95a5
commit 4c8620fc2f
2 changed files with 69 additions and 14 deletions

View file

@ -3,7 +3,7 @@ use crate::Error;
use serde::{Deserialize, Serialize};
use std::fmt;
/// field (aka 'point') position on the board (from 1 to 24)
/// field (aka 'point') position on the board (from 0 to 24, 0 being 'outside')
pub type Field = usize;
#[derive(Debug, Copy, Clone, Serialize, PartialEq, Deserialize)]
@ -14,12 +14,22 @@ pub struct CheckerMove {
impl CheckerMove {
pub fn new(from: Field, to: Field) -> Result<Self, Error> {
if from < 1 || 24 < from || to < 1 || 24 < to {
// check if the field is on the board
// we allow 0 for 'to', which represents the exit of a checker
if from < 1 || 24 < from || 24 < to{
return Err(Error::FieldInvalid);
}
// check that the destination is after the origin field
if to < from && to != 0 {
return Err(Error::MoveInvalid);
}
Ok(CheckerMove { from, to })
}
pub fn get_from(&self) -> Field {
self.from
}
pub fn get_to(&self) -> Field {
self.to
}
@ -106,6 +116,11 @@ impl Board {
return Err(Error::FieldInvalid);
}
// the exit : no checker added to the board
if field == 0 {
return Ok(())
}
if self.blocked(color, field)? {
return Err(Error::FieldBlocked);
}
@ -134,10 +149,15 @@ impl Board {
/// Check if a field is blocked for a player
pub fn blocked(&self, color: &Color, field: Field) -> Result<bool, Error> {
if field < 1 || 24 < field {
if 24 < field {
return Err(Error::FieldInvalid);
}
// the exit is never 'blocked'
if field == 0 {
return Ok(false)
}
// the square is blocked on the opponent rest corner or if there are opponent's men on the square
match color {
Color::White => {
@ -157,11 +177,11 @@ impl Board {
}
}
pub fn get_checkers_color(&self, field: Field) -> Result<Option<&Color>, Error> {
pub fn get_field_checkers(&self, field: Field) -> Result<(u8, Option<&Color>), Error> {
if field < 1 || field > 24 {
return Err(Error::FieldInvalid);
}
let checkers_count = self.positions[field - 1];
let checkers_count = self.positions[field - 1];
let color = if checkers_count < 0 {
Some(&Color::Black)
} else if checkers_count > 0 {
@ -169,7 +189,16 @@ impl Board {
} else {
None
};
Ok(color)
Ok((checkers_count.abs() as u8, color))
}
pub fn get_checkers_color(&self, field: Field) -> Result<Option<&Color>, Error> {
self.get_field_checkers(field).map(|(count, color)| color)
}
// Get the corner field for the color
pub fn get_color_corner(&self, color: &Color) -> Field {
if color == &Color::White { 12 } else { 13 }
}
pub fn move_possible(&self, color: &Color, cmove: CheckerMove) -> bool {
@ -231,7 +260,7 @@ mod tests {
#[test]
fn blocked_outofrange() -> Result<(), Error> {
let board = Board::new();
assert!(board.blocked( &Color::White, 0).is_err());
assert!(!board.blocked( &Color::White, 0).is_err());
assert!(board.blocked( &Color::White, 28).is_err());
Ok(())
}
@ -255,7 +284,7 @@ mod tests {
fn set_field_blocked() {
let mut board = Board::new();
assert!(
board.set( &Color::White, 0, 24)
board.set( &Color::White, 24, 2)
.is_err()
);
}

View file

@ -229,13 +229,16 @@ impl GameState {
}
// Check move is physically possible
if !self.board.move_possible(&self.players[player_id].color, moves.0){
return false;
}
if !self.board.move_possible(&self.players[player_id].color, moves.1){
return false;
}
let color = &self.players[player_id].color;
if !self.board.move_possible(color, moves.0) ||
!self.board.move_possible(color, moves.1) {
return false;
}
// Check move is allowed by the rules (to desactivate when playing with schools)
if !self.moves_allowed(color, moves) {
return false;
}
}
}
@ -243,6 +246,29 @@ impl GameState {
true
}
fn moves_allowed(&self, color: &Color, moves: &(CheckerMove, CheckerMove)) -> bool {
// ------- corner rules ----------
let corner_field: Field = self.board.get_color_corner(color);
let (corner_count, _color) = self.board.get_field_checkers(corner_field).unwrap();
let (from0, to0, from1, to1) = (moves.0.get_from(), moves.0.get_to(), moves.1.get_from(), moves.1.get_to());
// 2 checkers must go at the same time on an empty corner
if (to0 == corner_field || to1 == corner_field) &&
(to0 != to1) && corner_count == 0 {
return false;
}
// the lat 2 checkers of a corner must leave at the same time
if (from0 == corner_field || from1 == corner_field) &&
(from0 != from1) && corner_count == 2 {
return false;
}
// ------- exit rules ----------
// no rule was broken
true
}
/// Consumes an event, modifying the GameState and adding the event to its history
/// NOTE: consume assumes the event to have already been validated and will accept *any* event passed to it
pub fn consume(&mut self, valid_event: &GameEvent) {