check corner moves
This commit is contained in:
parent
06aeed95a5
commit
4c8620fc2f
|
|
@ -3,7 +3,7 @@ use crate::Error;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
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;
|
pub type Field = usize;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Serialize, PartialEq, Deserialize)]
|
#[derive(Debug, Copy, Clone, Serialize, PartialEq, Deserialize)]
|
||||||
|
|
@ -14,12 +14,22 @@ pub struct CheckerMove {
|
||||||
|
|
||||||
impl CheckerMove {
|
impl CheckerMove {
|
||||||
pub fn new(from: Field, to: Field) -> Result<Self, Error> {
|
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);
|
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 })
|
Ok(CheckerMove { from, to })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_from(&self) -> Field {
|
||||||
|
self.from
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_to(&self) -> Field {
|
pub fn get_to(&self) -> Field {
|
||||||
self.to
|
self.to
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +116,11 @@ impl Board {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the exit : no checker added to the board
|
||||||
|
if field == 0 {
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
if self.blocked(color, field)? {
|
if self.blocked(color, field)? {
|
||||||
return Err(Error::FieldBlocked);
|
return Err(Error::FieldBlocked);
|
||||||
}
|
}
|
||||||
|
|
@ -134,10 +149,15 @@ impl Board {
|
||||||
|
|
||||||
/// Check if a field is blocked for a player
|
/// Check if a field is blocked for a player
|
||||||
pub fn blocked(&self, color: &Color, field: Field) -> Result<bool, Error> {
|
pub fn blocked(&self, color: &Color, field: Field) -> Result<bool, Error> {
|
||||||
if field < 1 || 24 < field {
|
if 24 < field {
|
||||||
return Err(Error::FieldInvalid);
|
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
|
// the square is blocked on the opponent rest corner or if there are opponent's men on the square
|
||||||
match color {
|
match color {
|
||||||
Color::White => {
|
Color::White => {
|
||||||
|
|
@ -157,7 +177,7 @@ 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 {
|
if field < 1 || field > 24 {
|
||||||
return Err(Error::FieldInvalid);
|
return Err(Error::FieldInvalid);
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +189,16 @@ impl Board {
|
||||||
} else {
|
} else {
|
||||||
None
|
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 {
|
pub fn move_possible(&self, color: &Color, cmove: CheckerMove) -> bool {
|
||||||
|
|
@ -231,7 +260,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_err());
|
||||||
assert!(board.blocked( &Color::White, 28).is_err());
|
assert!(board.blocked( &Color::White, 28).is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -255,7 +284,7 @@ mod tests {
|
||||||
fn set_field_blocked() {
|
fn set_field_blocked() {
|
||||||
let mut board = Board::new();
|
let mut board = Board::new();
|
||||||
assert!(
|
assert!(
|
||||||
board.set( &Color::White, 0, 24)
|
board.set( &Color::White, 24, 2)
|
||||||
.is_err()
|
.is_err()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -229,13 +229,16 @@ impl GameState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check move is physically possible
|
// Check move is physically possible
|
||||||
if !self.board.move_possible(&self.players[player_id].color, moves.0){
|
let color = &self.players[player_id].color;
|
||||||
return false;
|
if !self.board.move_possible(color, moves.0) ||
|
||||||
}
|
!self.board.move_possible(color, moves.1) {
|
||||||
if !self.board.move_possible(&self.players[player_id].color, moves.1){
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check move is allowed by the rules (to desactivate when playing with schools)
|
// 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
|
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
|
/// 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
|
/// 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) {
|
pub fn consume(&mut self, valid_event: &GameEvent) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue