move generation
This commit is contained in:
parent
047c5fc02e
commit
4510141ce5
@ -1,3 +1,14 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use tak_lib::Board5x5;
|
||||
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
let board = Board5x5::new();
|
||||
// for b_move in board.moves_iter() {
|
||||
// println!("{:?}", b_move);
|
||||
// }
|
||||
let before = Instant::now();
|
||||
let moves: Vec<_> = board.moves_iter().collect();
|
||||
let dur = before.elapsed();
|
||||
println!("{:?}", dur);
|
||||
}
|
||||
|
||||
@ -7,3 +7,4 @@ edition = "2024"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.1"
|
||||
|
||||
|
||||
@ -4,77 +4,130 @@ use crate::{
|
||||
tile::Tile,
|
||||
};
|
||||
|
||||
pub struct BoardIter<'b, const N: usize> {
|
||||
/// The board we're iterating over.
|
||||
board: &'b Board<N>,
|
||||
/// The most recent coordinate that we have finished with.
|
||||
coord: (u8, u8),
|
||||
/// The remaining move buffer.
|
||||
///
|
||||
/// This contains all the moves that havent been returned yet,
|
||||
/// and are in the tiles <= `coord`.
|
||||
residual: Vec<Move<N>>,
|
||||
}
|
||||
|
||||
impl<const N: usize> Board<N> {
|
||||
/// Gets an iterator for the moves that can be made by
|
||||
/// the active player on this board.
|
||||
pub fn moves(&self) -> impl Iterator<Item = Move<N>> {
|
||||
let to_move = self.to_move;
|
||||
let tiles = &self.tiles;
|
||||
let can_place_caps = if Self::N_CAPS == 0 {
|
||||
false
|
||||
} else {
|
||||
let caps_on_board = self
|
||||
.tiles
|
||||
.iter()
|
||||
.flat_map(|col| col.iter())
|
||||
.filter(|t| {
|
||||
let piece = t.top_piece();
|
||||
piece.color_opt() == Some(to_move) && piece.piece() == Piece::Caps
|
||||
})
|
||||
.count() as u8;
|
||||
caps_on_board < Self::N_CAPS
|
||||
};
|
||||
|
||||
let chords = (0..(N as u8)).flat_map(|x| (0..(N as u8)).map(move |y| (x, y)));
|
||||
|
||||
chords.flat_map(move |(x, y)| {
|
||||
let top_piece = tiles[x as usize][y as usize].top_piece();
|
||||
/// Gets the possible moves for the given tile
|
||||
pub fn get_moves_for_tile(&self, x: u8, y: u8, buf: &mut Vec<Move<N>>) {
|
||||
let to_move = self.to_move();
|
||||
let can_place_caps = self.caps_left[to_move as usize] != 0;
|
||||
let top_piece = self.tiles[x as usize][y as usize].top_piece();
|
||||
if top_piece == ColoredPiece::None {
|
||||
// possible moves are place moves
|
||||
get_placement_moves(x, y, can_place_caps)
|
||||
get_placement_moves(x, y, can_place_caps, buf)
|
||||
} else if top_piece.color() == to_move {
|
||||
get_movement_moves(x, y, tiles)
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
})
|
||||
get_movement_moves(x, y, &self.tiles, buf)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_placement_moves<const N: usize>(x: u8, y: u8, can_place_caps: bool) -> Vec<Move<N>> {
|
||||
let mut moves = Vec::with_capacity(2 + can_place_caps as usize);
|
||||
moves.push(Move::Place {
|
||||
pub fn moves_iter<'b>(&'b self) -> BoardIter<'b, N> {
|
||||
let mut residual = Vec::new();
|
||||
self.get_moves_for_tile(0, 0, &mut residual);
|
||||
BoardIter {
|
||||
board: self,
|
||||
coord: (0, 0),
|
||||
residual,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: update result
|
||||
pub fn check_move(&self, m: Move<N>) -> Result<(), ()> {
|
||||
let _ = m;
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn unchecked_apply_move(mut self, m: Move<N>) -> Self {
|
||||
match m {
|
||||
Move::Place { piece, at } => {
|
||||
self.tiles[at.0 as usize][at.1 as usize]
|
||||
.set_top_piece(ColoredPiece::new(self.to_move(), piece));
|
||||
self
|
||||
}
|
||||
Move::Move {
|
||||
from,
|
||||
count,
|
||||
dir,
|
||||
drop,
|
||||
} => {
|
||||
let from_tile = self.tiles[from.0 as usize][from.1 as usize];
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b, const N: usize> Iterator for BoardIter<'b, N> {
|
||||
type Item = Move<N>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(v) = self.residual.pop() {
|
||||
return Some(v);
|
||||
}
|
||||
|
||||
if self.coord == (N as u8 - 1, N as u8 - 1) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// increment the coord
|
||||
self.coord.1 += 1;
|
||||
self.coord.0 += self.coord.1 / N as u8;
|
||||
self.coord.1 %= N as u8;
|
||||
|
||||
self.board
|
||||
.get_moves_for_tile(self.coord.0, self.coord.1, &mut self.residual);
|
||||
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the possible placement moves on (`x`, `y`).
|
||||
///
|
||||
/// This does no checks on the tile, it just adds the moves.
|
||||
fn get_placement_moves<const N: usize>(x: u8, y: u8, can_place_caps: bool, buf: &mut Vec<Move<N>>) {
|
||||
buf.push(Move::Place {
|
||||
piece: Piece::Flat,
|
||||
at: (x, y),
|
||||
});
|
||||
moves.push(Move::Place {
|
||||
buf.push(Move::Place {
|
||||
piece: Piece::Wall,
|
||||
at: (x, y),
|
||||
});
|
||||
if can_place_caps {
|
||||
moves.push(Move::Place {
|
||||
buf.push(Move::Place {
|
||||
piece: Piece::Caps,
|
||||
at: (x, y),
|
||||
});
|
||||
}
|
||||
moves
|
||||
}
|
||||
|
||||
/// Gets the possible moves by moving from (`x`, `y`).
|
||||
///
|
||||
/// This function does not check the colors of anything, so the caller must make sure
|
||||
/// the active player owns the piece on this tile, if you want to get only valid moves
|
||||
/// for the active player.
|
||||
pub(crate) fn get_movement_moves<const N: usize>(
|
||||
x: u8,
|
||||
y: u8,
|
||||
tiles: &[[Tile; N]; N],
|
||||
) -> Vec<Move<N>> {
|
||||
let mut moves = Vec::new();
|
||||
buf: &mut Vec<Move<N>>,
|
||||
) {
|
||||
let tile = tiles[x as usize][y as usize];
|
||||
let is_caps = tile.top_piece().piece() == Piece::Caps;
|
||||
let max_grab = u8::min(N as u8, tile.stack_len() + 1);
|
||||
|
||||
for dir in Direction::ALL {
|
||||
get_moves_in_dir(x, y, dir, tiles, &mut moves, is_caps, max_grab);
|
||||
get_moves_in_dir(x, y, dir, tiles, buf, is_caps, max_grab);
|
||||
}
|
||||
|
||||
moves
|
||||
}
|
||||
|
||||
/// Gets the possible moves moving from (`x`, `y`) in the given direction `dir`.
|
||||
|
||||
@ -57,7 +57,12 @@ impl Direction {
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
pub struct Board<const N: usize> {
|
||||
to_move: Color,
|
||||
half_moves: u32,
|
||||
/// The amount of stones that white and black have left.
|
||||
stones_left: [u8; 2],
|
||||
/// The amount of capstones that white and black have left.
|
||||
caps_left: [u8; 2],
|
||||
play_state: PlayState,
|
||||
tiles: [[Tile; N]; N],
|
||||
}
|
||||
pub type Board3x3 = Board<3>;
|
||||
@ -86,12 +91,26 @@ impl<const N: usize> Board<N> {
|
||||
_ => panic!("N must be 3, 4, 5, 6, or 8"),
|
||||
};
|
||||
|
||||
/// Constructs a new `Board` in the starting state.
|
||||
pub fn new() -> Self {
|
||||
Board {
|
||||
to_move: Color::White,
|
||||
half_moves: 0,
|
||||
stones_left: [Self::N_STONES, Self::N_STONES],
|
||||
caps_left: [Self::N_CAPS, Self::N_CAPS],
|
||||
play_state: PlayState::WhitePlaceBlack,
|
||||
tiles: [[Tile::EMPTY; N]; N],
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the player who is to move
|
||||
#[inline]
|
||||
pub fn to_move(&self) -> Color {
|
||||
if self.half_moves % 2 == 0 {
|
||||
Color::White
|
||||
} else {
|
||||
Color::Black
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||
@ -107,3 +126,15 @@ pub enum Move<const N: usize> {
|
||||
drop: [u8; N],
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum PlayState {
|
||||
Normal,
|
||||
WhitePlaceBlack,
|
||||
BlackPlaceWhite,
|
||||
WhiteWinRoad,
|
||||
WhiteWinFlat,
|
||||
BlackWinRoad,
|
||||
BlackWinFlat,
|
||||
Tie,
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ pub enum ColoredPiece {
|
||||
|
||||
impl ColoredPiece {
|
||||
/// Constructs a new `ColoredPiece`.
|
||||
pub fn new(piece: Piece, color: Color) -> Self {
|
||||
pub fn new(color: Color, piece: Piece) -> Self {
|
||||
let mut piece_val = (piece as u8) | ((color as u8) << 2);
|
||||
if piece_val == 4 {
|
||||
piece_val = 0;
|
||||
|
||||
@ -290,7 +290,8 @@ fn test_get_moves() {
|
||||
ColoredPiece::WhiteFlat,
|
||||
&[Color::White, Color::Black, Color::White, Color::Black],
|
||||
);
|
||||
let actual = get_movement_moves(from.0, from.1, &tiles);
|
||||
let mut actual = Vec::new();
|
||||
get_movement_moves(from.0, from.1, &tiles, &mut actual);
|
||||
|
||||
const LEN1_DROPS: [(u8, [u8; 5]); 5] = [
|
||||
(1, [1, 0, 0, 0, 0]),
|
||||
@ -417,7 +418,8 @@ fn test_get_caps_moves() {
|
||||
tiles[1][0] = Tile::new(ColoredPiece::WhiteWall, &[]);
|
||||
tiles[1][3] = Tile::new(ColoredPiece::BlackWall, &[]);
|
||||
tiles[4][2] = Tile::new(ColoredPiece::BlackWall, &[]);
|
||||
let actual = get_movement_moves(from.0, from.1, &tiles);
|
||||
let mut actual = Vec::new();
|
||||
get_movement_moves(from.0, from.1, &tiles, &mut actual);
|
||||
|
||||
const LEN1_DROPS: [(u8, [u8; 5]); 5] = [
|
||||
(1, [1, 0, 0, 0, 0]),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user