@ -66,6 +66,60 @@ pub(crate) enum Piece {
Queen ,
Queen ,
}
}
impl Piece {
fn moves < ' a > ( & self ) -> std ::slice ::Iter < ' _ , ( isize , isize ) > {
match self {
Piece ::Pawn = > [ ( 1 , 1 ) , ( 1 , - 1 ) , ( - 1 , 1 ) , ( - 1 , - 1 ) ] . iter ( ) ,
Piece ::Drone = > [
( 0 , 2 ) ,
( 0 , 1 ) ,
( - 2 , 0 ) ,
( - 1 , 0 ) ,
( 1 , 0 ) ,
( 2 , 0 ) ,
( 0 , - 1 ) ,
( 0 , - 2 ) ,
]
. iter ( ) ,
Piece ::Queen = > [
( - 3 , 3 ) ,
( 0 , 3 ) ,
( 3 , 3 ) ,
( - 2 , 2 ) ,
( 0 , 2 ) ,
( 2 , 2 ) ,
( - 1 , 1 ) ,
( 0 , 1 ) ,
( 1 , 1 ) ,
( - 7 , 0 ) ,
( - 6 , 0 ) ,
( - 5 , 0 ) ,
( - 4 , 0 ) ,
( - 3 , 0 ) ,
( - 2 , 0 ) ,
( - 1 , 0 ) ,
( 1 , 0 ) ,
( 2 , 0 ) ,
( 3 , 0 ) ,
( 4 , 0 ) ,
( 5 , 0 ) ,
( 6 , 0 ) ,
( 7 , 0 ) ,
( - 1 , - 1 ) ,
( 0 , - 1 ) ,
( 1 , - 1 ) ,
( - 2 , - 2 ) ,
( 0 , - 2 ) ,
( 2 , - 2 ) ,
( - 3 , - 3 ) ,
( 0 , - 3 ) ,
( 3 , - 3 ) ,
]
. iter ( ) ,
}
}
}
#[ derive(Debug, Component, Clone, PartialEq) ]
#[ derive(Debug, Component, Clone, PartialEq) ]
pub ( crate ) enum Tile {
pub ( crate ) enum Tile {
Dark ,
Dark ,
@ -210,6 +264,35 @@ pub(crate) struct BoardIndex {
pub y : usize ,
pub y : usize ,
}
}
impl std ::ops ::Add for BoardIndex {
type Output = BoardIndex ;
fn add ( self , other : Self ) -> Self {
BoardIndex {
x : self . x + other . x ,
y : self . y + other . y ,
}
}
}
impl std ::ops ::Add < ( isize , isize ) > for BoardIndex {
type Output = BoardIndex ;
fn add ( self , ( a , b ) : ( isize , isize ) ) -> Self {
BoardIndex {
x : self . x . saturating_add_signed ( a ) ,
y : self . y . saturating_add_signed ( b ) ,
}
}
}
#[ derive(Debug) ]
pub ( crate ) enum MoveType {
Valid ,
Invalid ,
Capture ,
}
#[ derive(Debug, Component, PartialEq, Clone, Copy, Eq, Hash, Default) ]
#[ derive(Debug, Component, PartialEq, Clone, Copy, Eq, Hash, Default) ]
pub ( crate ) enum Side {
pub ( crate ) enum Side {
A ,
A ,
@ -397,167 +480,128 @@ impl Board {
}
}
}
}
/// Returns the possible moves the piece at this tile can make.
// Returns a list of indexes between two points, excluding the start and end
/// TODO: Implement "no jumping" over pieces
fn line ( & self , from : BoardIndex , to : BoardIndex ) -> impl Iterator < Item = BoardIndex > {
pub ( crate ) fn valid_moves ( & self , current_board_index : BoardIndex ) -> HashSet < BoardIndex > {
let mut curr = from ;
let BoardIndex { x , y } = current_board_index ;
// Longest possible move is 10, so we create a generator over 11
let f = | ( a , b ) : ( Option < usize > , Option < usize > ) | {
( 0 .. 11 )
if let ( Some ( this_x ) , Some ( this_y ) ) = ( a , b ) {
. map ( move | _ | {
// This has a valid x position
let x = if curr . x > to . x {
let valid_x = ( 0 ..= 7 ) . contains ( & this_x ) ;
curr . x . saturating_sub ( 1 )
if valid_x {
} else if curr . x < to . x {
// It has a valid y position
curr . x . saturating_add ( 1 )
let valid_y = ( 0 ..= 3 ) . contains ( & this_y ) ;
} else {
if valid_y {
to . x
// The checked board index
let this_board_index = BoardIndex {
x : this_x ,
y : this_y ,
} ;
} ;
// Only propose tiles that are empty or capture a piece on the other side
let valid_capture = {
let y = if curr . y > to . y {
match self . at ( this_board_index ) {
curr . y . saturating_sub ( 1 )
Some ( _ ) = > {
} else if curr . y < to . y {
Board ::side ( this_board_index )
curr . y . saturating_add ( 1 )
! = Board ::side ( current_board_index )
} else {
}
to . y
None = > true ,
}
} ;
} ;
if valid_capture {
// You cannot move a piece from A to B and then back to A when it was just moved from SideA->SideB
curr = BoardIndex { x , y } ;
// Move rejection is not allowed
let rejection = {
( curr ! = to ) . then_some ( curr )
if let Some ( Move {
} )
from : last_from ,
. filter_map ( | bi | bi )
to : Some ( last_to ) ,
}
..
} ) = self . moves . last ( )
/// Determine given a piece, a to, and a from, what type of move this would be
pub ( crate ) fn move_type (
& self ,
piece : Piece ,
from : BoardIndex ,
to : BoardIndex ,
) -> Option < MoveType > {
// Iterate over the piece's moves
piece
. moves ( )
// Find if the given `to` move is one of those
. find ( | ( x , y ) | to = = from + ( * x , * y ) )
// Determine if this is valid/legal in this situation
. and_then ( | _ | {
let dest_at = self . at ( to ) ;
let curr_side = Board ::side ( from ) . unwrap ( ) ;
let dest_side = Board ::side ( to ) . unwrap ( ) ;
match ( curr_side , dest_side ) {
( Side ::A , Side ::A ) | ( Side ::B , Side ::B ) = > {
match dest_at {
// Cannot move on top of a friendly
Some ( _ ) = > Some ( MoveType ::Invalid ) ,
// Any other spot is valid
None = > Some ( MoveType ::Valid ) ,
}
}
// Check for moving across the canal
( Side ::A , Side ::B ) | ( Side ::B , Side ::A ) = > {
match dest_at {
Some ( _ ) = > {
// If there is another piece between A and B
if self
. line ( from , to )
. any ( | board_index | self . at ( board_index ) . is_some ( ) )
{
{
// TODO: I think this is more logic than we need to express
// Invalid move, there is a piece between A and B
// the sentiment...
Some ( MoveType ::Invalid )
* last_from = = this_board_index
& & * last_to = = current_board_index
& & Board ::side ( this_board_index )
! = Board ::side ( current_board_index )
} else {
} else {
false
// Otherwise it's a capture
Some ( MoveType ::Capture )
}
}
} ;
// If all tests pass, this is a valid move
( ! rejection ) . then_some ( this_board_index )
} else {
None
}
}
None = > {
// move is valid if it does not un-do the previous move
match self . moves . last ( ) {
Some ( previous ) = > {
// If the last `from` is the current destination
// The move is not valid
if previous . from = = to {
Some ( MoveType ::Invalid )
// Otherwise we're good to move there
} else {
} else {
None
Some ( MoveType ::Valid )
}
}
} else {
None
}
}
// First move in the game, this is valid (and impossible)
None = > {
// If there is another piece between A and B
if self
. line ( from , to )
. any ( | board_index | self . at ( board_index ) . is_some ( ) )
{
// Invalid move, there is a piece between A and B
Some ( MoveType ::Invalid )
} else {
} else {
None
// Otherwise it's a valid
Some ( MoveType ::Valid )
}
}
}
}
}
}
}
} )
}
}
} ;
match self . at ( BoardIndex { x , y } ) {
/// Returns the possible moves the piece at this tile can make.
// One space in any diagonal
/// TODO: Implement "no jumping" over pieces
Some ( Piece ::Pawn ) = > std ::iter ::empty ( )
pub ( crate ) fn valid_moves ( & self , current_board_index : BoardIndex ) -> HashSet < BoardIndex > {
. chain (
tiles ( )
( - 1 ..= 1 )
. filter_map ( | ( board_index , _ ) | {
. zip ( - 1 ..= 1 )
// Get the move type (or none if totally invalid)
. map ( move | ( a , b ) | ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) ,
self . at ( current_board_index ) . and_then ( | piece | {
)
match self . move_type ( * piece , current_board_index , board_index ) {
. chain (
None | Some ( MoveType ::Invalid ) = > None ,
( - 1 ..= 1 )
_ = > Some ( board_index ) ,
. zip ( ( - 1 ..= 1 ) . rev ( ) )
. map ( move | ( a , b ) | ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) ,
)
. filter_map ( f )
. collect ( ) ,
// One or two spaces in either horizontal
Some ( Piece ::Drone ) = > std ::iter ::empty ( )
// Checking moves on the X axis
. chain (
( - 2 ..= - 1 )
. rev ( )
. map ( | i | f ( ( x . checked_add_signed ( i ) , Some ( y ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( 1 ..= 2 )
. map ( | i | f ( ( x . checked_add_signed ( i ) , Some ( y ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
// Checking moves on the Y axis
. chain (
( 1 ..= 2 )
. map ( | i | f ( ( Some ( x ) , y . checked_add_signed ( i ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( - 2 ..= - 1 )
. rev ( )
. map ( | i | f ( ( Some ( x ) , y . checked_add_signed ( i ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. filter_map ( | x | x )
. collect ( ) ,
// Any distance in any straight line
Some ( Piece ::Queen ) = > std ::iter ::empty ( )
. chain (
( - 7 ..= - 1 )
. rev ( )
. map ( | i | f ( ( x . checked_add_signed ( i ) , Some ( y ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( 1 ..= 7 )
. map ( | i | f ( ( x . checked_add_signed ( i ) , Some ( y ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( - 3 ..= - 1 )
. rev ( )
. map ( | i | f ( ( Some ( x ) , y . checked_add_signed ( i ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( 1 ..= 3 )
. map ( | i | f ( ( Some ( x ) , y . checked_add_signed ( i ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( - 3 ..= - 1 )
. rev ( )
. zip ( ( - 3 ..= - 1 ) . rev ( ) )
. map ( move | ( a , b ) | f ( ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( - 3 ..= - 1 )
. rev ( )
. zip ( 1 ..= 3 )
. map ( move | ( a , b ) | f ( ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( 1 ..= 3 )
. zip ( ( - 3 ..= - 1 ) . rev ( ) )
. map ( move | ( a , b ) | f ( ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. chain (
( 1 ..= 3 )
. zip ( 1 ..= 3 )
. map ( move | ( a , b ) | f ( ( x . checked_add_signed ( a ) , y . checked_add_signed ( b ) ) ) )
. take_while ( | x | x . is_some ( ) ) ,
)
. filter_map ( | x | x )
. collect ( ) ,
None = > std ::iter ::empty ( ) . collect ( ) ,
}
}
} )
} )
. collect ( )
}
}
pub ( crate ) fn current_epoch ( & self ) -> usize {
pub ( crate ) fn current_epoch ( & self ) -> usize {