fog-of-chess

[toy] Chess, but with the fog of war.
Log | Files | Refs

commit bdd4da585126568ad452656f48b5d08d9d78a64b
parent 5478565fb3d790c0a0d9553562e79217c66207a8
Author: = <=>
Date:   Sat, 28 Dec 2019 22:15:59 +1300

Feat: Multiple selection.

Diffstat:
Msrc/main.rs | 315++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
1 file changed, 183 insertions(+), 132 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -48,28 +48,42 @@ impl EventHandler for Game { let (w_size, h_size) = (w / 8.0, h / 8.0); let (col, row) = ((x / w_size).floor() as i32, (y / h_size).floor() as i32); if is_key_pressed(ctx, KeyCode::LShift) { - // Add to seleciton. - } - match self.board.get((col, row)) { - None => { - if let Some((x, y)) = self.selected_piece { - if self.moves().contains(&(col, row)) { - self.move_turn((x, y), (col, row)); + if self.contains_ally((col, row)) { + self.selected.push((col, row)); + } + } else { + match self.board.get((col, row)) { + // Move. + None => { + // Multi selection is a compound move. + // Given the only compound move in standard chess is the + // "castle", we directly call into it. + if self.selected.len() > 1 { + self.castle_move() + } else { + if let Some((x, y)) = self.first_selected() { + if self.moves((x, y)).contains(&(col, row)) { + self.move_turn((x, y), (col, row)); + } + } } } - } - Some(Piece { player, .. }) => { - if *player != self.turn { - if let Some((x, y)) = self.selected_piece { - if self.moves().contains(&(col, row)) { - self.move_turn((x, y), (col, row)); + // Attack move. + Some(Piece { player, .. }) => { + if *player != self.turn && self.selected.len() == 1 { + if let Some((x, y)) = self.first_selected() { + if self.moves((x, y)).contains(&(col, row)) { + self.move_turn((x, y), (col, row)); + } + } + } else { + if self.contains_ally((col, row)) { + self.selected = vec![(col as i32, row as i32)]; } } - } else { - self.selected_piece = Some((col as i32, row as i32)); } - } - }; + }; + } } fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { @@ -106,15 +120,10 @@ impl EventHandler for Game { Player::Black => graphics::BLACK, }; // Highlight if selected piece. - let color = match self.selected_piece { - Some((xx, yy)) => { - if xx as usize == x && yy as usize == y { - PURE_APPLE - } else { - color - } - } - _ => color, + let color = if self.selected.contains(&(x as i32, y as i32)) { + PURE_APPLE + } else { + color }; // Render each unit as a text fragment. // TODO: Use nice textures. @@ -185,7 +194,8 @@ pub struct Board([[Option<Piece>; 8]; 8]); pub struct Game { pub board: Board, pub turn: Player, - pub selected_piece: Option<(i32, i32)>, + // Track selected pieces. + pub selected: Vec<(i32, i32)>, } impl Game { @@ -194,114 +204,113 @@ impl Game { Game { board: Board::new(), turn: Player::White, - selected_piece: None, + selected: vec![], } } /// Moves calculates all valid moves for the currently selected piece. // TODO: Finish movement logic. - pub fn moves(&self) -> Vec<(i32, i32)> { + pub fn moves(&self, pos: (i32, i32)) -> Vec<(i32, i32)> { + let (x, y) = pos; + // How do we handle a vec of selected pieces while satisfying the BC. use Unit::*; - match self.selected_piece { - Some((x, y)) => match self.board.get((x, y)) { - Some(Piece { - unit, - player, - moved, - }) => match unit { - // Pawn can move in the direction of the player by 1 square. - // For the first move, a pawn can move up to 2 squares. - // Pawns can only attack diagonally in the direction of the - // player. - // Cannot attack straight ahead. - Pawn => { - let mut moves = vec![]; - match player { - // Clean: The only difference between these two - // blocks is the direction. - Player::White => { - if self.contains_enemy((x - 1, y + 1)) { - moves.push((x - 1, y + 1)); - } - if self.contains_enemy((x + 1, y + 1)) { - moves.push((x + 1, y + 1)); - } - if self.board.0[y as usize + 1][x as usize].is_none() { - moves.push((x, y + 1)); - if *moved == 0 { - moves.push((x, y + 2)); - } - } + match self.board.get((x, y)) { + Some(Piece { + unit, + player, + moved, + }) => match unit { + // Pawn can move in the direction of the player by 1 square. + // For the first move, a pawn can move up to 2 squares. + // Pawns can only attack diagonally in the direction of the + // player. + // Cannot attack straight ahead. + Pawn => { + let mut moves = vec![]; + match player { + // Clean: The only difference between these two + // blocks is the direction. + Player::White => { + if self.contains_enemy((x - 1, y + 1)) { + moves.push((x - 1, y + 1)); } - Player::Black => { - if self.contains_enemy((x - 1, y - 1)) { - moves.push((x - 1, y - 1)); - } - if self.contains_enemy((x + 1, y - 1)) { - moves.push((x + 1, y - 1)); + if self.contains_enemy((x + 1, y + 1)) { + moves.push((x + 1, y + 1)); + } + if self.board.0[y as usize + 1][x as usize].is_none() { + moves.push((x, y + 1)); + if *moved == 0 { + moves.push((x, y + 2)); } - if self.board.0[y as usize - 1][x as usize].is_none() { - moves.push((x, y - 1)); - if *moved == 0 { - moves.push((x, y - 2)); - } + } + } + Player::Black => { + if self.contains_enemy((x - 1, y - 1)) { + moves.push((x - 1, y - 1)); + } + if self.contains_enemy((x + 1, y - 1)) { + moves.push((x + 1, y - 1)); + } + if self.board.0[y as usize - 1][x as usize].is_none() { + moves.push((x, y - 1)); + if *moved == 0 { + moves.push((x, y - 2)); } } - }; - moves - } - // Knight moves in an L shape: two out, one across. - Knight => vec![ - (x + 2, y - 1), - (x + 2, y + 1), - (x - 2, y - 1), - (x - 2, y + 1), - (x + 1, y + 2), - (x - 1, y + 2), - (x + 1, y - 2), - (x - 1, y - 2), - ], - // Rook moves in all non diagonal directions. - Rook => vec![] - .into_iter() - .chain((1..8).map(|ii| (x + ii, y))) - .chain((1..8).map(|ii| (x - ii, y))) - .chain((1..8).map(|ii| (x, y + ii))) - .chain((1..8).map(|ii| (x, y - ii))) - .collect(), - // Bishop moves all diagonal directions. - Bishop => vec![] - .into_iter() - .chain((1..8).map(|ii| (x + ii, y + ii))) - .chain((1..8).map(|ii| (x - ii, y - ii))) - .chain((1..8).map(|ii| (x - ii, y + ii))) - .chain((1..8).map(|ii| (x + ii, y - ii))) - .collect(), - // Queen moves in all eight directions. - Queen => vec![] - .into_iter() - .chain((1..8).map(|ii| (x + ii, y))) - .chain((1..8).map(|ii| (x - ii, y))) - .chain((1..8).map(|ii| (x, y + ii))) - .chain((1..8).map(|ii| (x, y - ii))) - .chain((1..8).map(|ii| (x + ii, y + ii))) - .chain((1..8).map(|ii| (x - ii, y - ii))) - .chain((1..8).map(|ii| (x - ii, y + ii))) - .chain((1..8).map(|ii| (x + ii, y - ii))) - .collect(), - // King can move to any adjacent cell that isn't occupied by - // a piece of the same player. - King => vec![ - (x + 1, y + 1), - (x - 1, y - 1), - (x + 1, y - 1), - (x - 1, y + 1), - (x + 1, y), - (x - 1, y), - (x, y + 1), - (x, y - 1), - ], - }, - None => vec![], + } + }; + moves + } + // Knight moves in an L shape: two out, one across. + Knight => vec![ + (x + 2, y - 1), + (x + 2, y + 1), + (x - 2, y - 1), + (x - 2, y + 1), + (x + 1, y + 2), + (x - 1, y + 2), + (x + 1, y - 2), + (x - 1, y - 2), + ], + // Rook moves in all non diagonal directions. + Rook => vec![] + .into_iter() + .chain((1..8).map(|ii| (x + ii, y))) + .chain((1..8).map(|ii| (x - ii, y))) + .chain((1..8).map(|ii| (x, y + ii))) + .chain((1..8).map(|ii| (x, y - ii))) + .collect(), + // Bishop moves all diagonal directions. + Bishop => vec![] + .into_iter() + .chain((1..8).map(|ii| (x + ii, y + ii))) + .chain((1..8).map(|ii| (x - ii, y - ii))) + .chain((1..8).map(|ii| (x - ii, y + ii))) + .chain((1..8).map(|ii| (x + ii, y - ii))) + .collect(), + // Queen moves in all eight directions. + Queen => vec![] + .into_iter() + .chain((1..8).map(|ii| (x + ii, y))) + .chain((1..8).map(|ii| (x - ii, y))) + .chain((1..8).map(|ii| (x, y + ii))) + .chain((1..8).map(|ii| (x, y - ii))) + .chain((1..8).map(|ii| (x + ii, y + ii))) + .chain((1..8).map(|ii| (x - ii, y - ii))) + .chain((1..8).map(|ii| (x - ii, y + ii))) + .chain((1..8).map(|ii| (x + ii, y - ii))) + .collect(), + // King can move to any adjacent cell that isn't occupied by + // a piece of the same player. + King => vec![ + (x + 1, y + 1), + (x - 1, y - 1), + (x + 1, y - 1), + (x - 1, y + 1), + (x + 1, y), + (x - 1, y), + (x, y + 1), + (x, y - 1), + ], }, None => vec![], } @@ -311,12 +320,14 @@ impl Game { } /// Move a piece and conclude the turn. pub fn move_turn(&mut self, from: (i32, i32), to: (i32, i32)) { - self.board.move_piece((from.0, from.1), (to.0, to.1)); - self.turn = match self.turn { - Player::Black => Player::White, - Player::White => Player::Black, - }; - self.selected_piece = None; + if self.contains_ally(from) { + self.board.move_piece((from.0, from.1), (to.0, to.1)); + self.turn = match self.turn { + Player::Black => Player::White, + Player::White => Player::Black, + }; + self.selected = vec![]; + } } /// Contains enemy if the specified position is occupied by a piece owned /// by the other player. @@ -344,6 +355,46 @@ impl Game { false } } + /// Coordinate of the first selected piece. + fn first_selected(&mut self) -> Option<(i32, i32)> { + if self.selected.len() > 0 { + Some(self.selected[0].clone()) + } else { + None + } + } + // TODO: impl castle move. + fn castle_move(&mut self) { + // Consider the first two moves of the selection as king and rook. + // Attempt the castle: + // - King and Rook must in original positions. + // - The two spaces between them must be empty. + // Clone out the first two selected coordinates. + // let pieces = self + // .selected + // .iter() + // .cloned() + // .take(2) + // .collect::<Vec<(i32, i32)>>(); + // // Check for King and Rook. + // if let ( + // Some(Piece { + // unit: Unit::King, .. + // }), + // Some(Piece { + // unit: Unit::Rook, .. + // }), + // ) = (self.board.get(pieces[0]), self.board.get(pieces[1])) + // { + // let (king, rook) = (pieces[0], pieces[1]); + // if (king.0 - rook.0).abs() == 2 { + // // correct distance + + // } + // // - Original positions + // // - Empty between them + // } + } } impl Board {