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() {
|
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]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
|
|
||||||
|
|||||||
@ -4,77 +4,130 @@ use crate::{
|
|||||||
tile::Tile,
|
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> {
|
impl<const N: usize> Board<N> {
|
||||||
/// Gets an iterator for the moves that can be made by
|
/// Gets the possible moves for the given tile
|
||||||
/// the active player on this board.
|
pub fn get_moves_for_tile(&self, x: u8, y: u8, buf: &mut Vec<Move<N>>) {
|
||||||
pub fn moves(&self) -> impl Iterator<Item = Move<N>> {
|
let to_move = self.to_move();
|
||||||
let to_move = self.to_move;
|
let can_place_caps = self.caps_left[to_move as usize] != 0;
|
||||||
let tiles = &self.tiles;
|
let top_piece = self.tiles[x as usize][y as usize].top_piece();
|
||||||
let can_place_caps = if Self::N_CAPS == 0 {
|
if top_piece == ColoredPiece::None {
|
||||||
false
|
// possible moves are place moves
|
||||||
} else {
|
get_placement_moves(x, y, can_place_caps, buf)
|
||||||
let caps_on_board = self
|
} else if top_piece.color() == to_move {
|
||||||
.tiles
|
get_movement_moves(x, y, &self.tiles, buf)
|
||||||
.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)));
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
chords.flat_map(move |(x, y)| {
|
// TODO: update result
|
||||||
let top_piece = tiles[x as usize][y as usize].top_piece();
|
pub fn check_move(&self, m: Move<N>) -> Result<(), ()> {
|
||||||
if top_piece == ColoredPiece::None {
|
let _ = m;
|
||||||
// possible moves are place moves
|
todo!()
|
||||||
get_placement_moves(x, y, can_place_caps)
|
}
|
||||||
} else if top_piece.color() == to_move {
|
|
||||||
get_movement_moves(x, y, tiles)
|
pub fn unchecked_apply_move(mut self, m: Move<N>) -> Self {
|
||||||
} else {
|
match m {
|
||||||
Vec::with_capacity(0)
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_placement_moves<const N: usize>(x: u8, y: u8, can_place_caps: bool) -> Vec<Move<N>> {
|
impl<'b, const N: usize> Iterator for BoardIter<'b, N> {
|
||||||
let mut moves = Vec::with_capacity(2 + can_place_caps as usize);
|
type Item = Move<N>;
|
||||||
moves.push(Move::Place {
|
|
||||||
|
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,
|
piece: Piece::Flat,
|
||||||
at: (x, y),
|
at: (x, y),
|
||||||
});
|
});
|
||||||
moves.push(Move::Place {
|
buf.push(Move::Place {
|
||||||
piece: Piece::Wall,
|
piece: Piece::Wall,
|
||||||
at: (x, y),
|
at: (x, y),
|
||||||
});
|
});
|
||||||
if can_place_caps {
|
if can_place_caps {
|
||||||
moves.push(Move::Place {
|
buf.push(Move::Place {
|
||||||
piece: Piece::Caps,
|
piece: Piece::Caps,
|
||||||
at: (x, y),
|
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>(
|
pub(crate) fn get_movement_moves<const N: usize>(
|
||||||
x: u8,
|
x: u8,
|
||||||
y: u8,
|
y: u8,
|
||||||
tiles: &[[Tile; N]; N],
|
tiles: &[[Tile; N]; N],
|
||||||
) -> Vec<Move<N>> {
|
buf: &mut Vec<Move<N>>,
|
||||||
let mut moves = Vec::new();
|
) {
|
||||||
let tile = tiles[x as usize][y as usize];
|
let tile = tiles[x as usize][y as usize];
|
||||||
let is_caps = tile.top_piece().piece() == Piece::Caps;
|
let is_caps = tile.top_piece().piece() == Piece::Caps;
|
||||||
let max_grab = u8::min(N as u8, tile.stack_len() + 1);
|
let max_grab = u8::min(N as u8, tile.stack_len() + 1);
|
||||||
|
|
||||||
for dir in Direction::ALL {
|
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`.
|
/// 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)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
pub struct Board<const N: usize> {
|
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],
|
tiles: [[Tile; N]; N],
|
||||||
}
|
}
|
||||||
pub type Board3x3 = Board<3>;
|
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"),
|
_ => panic!("N must be 3, 4, 5, 6, or 8"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Constructs a new `Board` in the starting state.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Board {
|
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],
|
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)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
@ -107,3 +126,15 @@ pub enum Move<const N: usize> {
|
|||||||
drop: [u8; N],
|
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 {
|
impl ColoredPiece {
|
||||||
/// Constructs a new `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);
|
let mut piece_val = (piece as u8) | ((color as u8) << 2);
|
||||||
if piece_val == 4 {
|
if piece_val == 4 {
|
||||||
piece_val = 0;
|
piece_val = 0;
|
||||||
|
|||||||
@ -290,7 +290,8 @@ fn test_get_moves() {
|
|||||||
ColoredPiece::WhiteFlat,
|
ColoredPiece::WhiteFlat,
|
||||||
&[Color::White, Color::Black, Color::White, Color::Black],
|
&[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] = [
|
const LEN1_DROPS: [(u8, [u8; 5]); 5] = [
|
||||||
(1, [1, 0, 0, 0, 0]),
|
(1, [1, 0, 0, 0, 0]),
|
||||||
@ -417,7 +418,8 @@ fn test_get_caps_moves() {
|
|||||||
tiles[1][0] = Tile::new(ColoredPiece::WhiteWall, &[]);
|
tiles[1][0] = Tile::new(ColoredPiece::WhiteWall, &[]);
|
||||||
tiles[1][3] = Tile::new(ColoredPiece::BlackWall, &[]);
|
tiles[1][3] = Tile::new(ColoredPiece::BlackWall, &[]);
|
||||||
tiles[4][2] = 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] = [
|
const LEN1_DROPS: [(u8, [u8; 5]); 5] = [
|
||||||
(1, [1, 0, 0, 0, 0]),
|
(1, [1, 0, 0, 0, 0]),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user