300 lines
7.6 KiB
Rust
300 lines
7.6 KiB
Rust
use crate::{
|
|
Board, Move,
|
|
piece::{ColoredPiece, Piece},
|
|
};
|
|
|
|
impl<const N: usize> Board<N> {
|
|
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();
|
|
if top_piece == ColoredPiece::None {
|
|
// possible moves are place moves
|
|
insert_placement_moves(x, y, can_place_caps)
|
|
} else if top_piece.color() == to_move {
|
|
get_movement_moves(x, y, tiles[x as usize][y as usize].stack_len())
|
|
} else {
|
|
Vec::with_capacity(0)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
fn insert_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 {
|
|
piece: Piece::Flat,
|
|
at: (x, y),
|
|
});
|
|
moves.push(Move::Place {
|
|
piece: Piece::Wall,
|
|
at: (x, y),
|
|
});
|
|
if can_place_caps {
|
|
moves.push(Move::Place {
|
|
piece: Piece::Caps,
|
|
at: (x, y),
|
|
});
|
|
}
|
|
moves
|
|
}
|
|
|
|
fn get_movement_moves<const N: usize>(x: u8, y: u8, stack_len: u8) -> Vec<Move<N>> {
|
|
let max_grab = u8::min(N as u8, stack_len + 1);
|
|
|
|
// East
|
|
let max_dist_east = max_grab.min(N as u8 - x - 1);
|
|
// North
|
|
let max_dist_north = max_grab.min(N as u8 - y - 1);
|
|
// West
|
|
let max_dist_west = max_grab.min(x);
|
|
// South
|
|
let max_dist_south = max_grab.min(y);
|
|
todo!()
|
|
}
|
|
|
|
fn get_drop_amounts<const N: usize>(
|
|
// shared state
|
|
buf: &mut [u8; N],
|
|
i: usize,
|
|
insert_fn: &mut impl FnMut([u8; N]),
|
|
// recursive parameters
|
|
pieces: u8,
|
|
len: u8,
|
|
end_w_capstone: bool,
|
|
) {
|
|
debug_assert!(len > 0 || pieces == 0);
|
|
// base cases
|
|
if pieces == 0 {
|
|
insert_fn(buf.clone());
|
|
return;
|
|
}
|
|
if pieces == 1 {
|
|
buf[i] = 1;
|
|
insert_fn(buf.clone());
|
|
buf[i] = 0;
|
|
return;
|
|
}
|
|
if len == 1 {
|
|
buf[i] = pieces;
|
|
insert_fn(buf.clone());
|
|
buf[i] = 0;
|
|
return;
|
|
}
|
|
|
|
// recursive case
|
|
for n in 1..=pieces {
|
|
buf[i] = n;
|
|
get_drop_amounts(buf, i + 1, insert_fn, pieces - n, len - 1, end_w_capstone);
|
|
}
|
|
buf[i] = 0;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn expect_get_drop_ammounts<const N: usize>(
|
|
pieces: u8,
|
|
len: u8,
|
|
end_w_capstone: bool,
|
|
expected_drop_list: Vec<[u8; N]>,
|
|
) {
|
|
use std::collections::HashSet;
|
|
|
|
let mut buf = [0; N];
|
|
let mut drop_list = Vec::new();
|
|
let mut insert_fn = |drops: [u8; N]| {
|
|
drop_list.push(drops);
|
|
};
|
|
get_drop_amounts(&mut buf, 0, &mut insert_fn, pieces, len, end_w_capstone);
|
|
|
|
let expected_drop_set: HashSet<_> = expected_drop_list.iter().cloned().collect();
|
|
let drop_set: HashSet<_> = drop_list.iter().cloned().collect();
|
|
if drop_set.len() != drop_list.len() {
|
|
panic!("drop list had duplicate values");
|
|
}
|
|
if expected_drop_set != drop_set {
|
|
let mut err_str = String::new();
|
|
for entry in expected_drop_list {
|
|
if drop_set.contains(&entry) {
|
|
err_str += &format!("\t{:?} {:?}\n", entry, entry);
|
|
} else {
|
|
err_str += &format!("\t{:?}\n", entry);
|
|
}
|
|
}
|
|
for entry in drop_list {
|
|
if expected_drop_set.contains(&entry) {
|
|
continue;
|
|
}
|
|
let blank = format!("{:?}", entry)
|
|
.chars()
|
|
.map(|_| ' ')
|
|
.collect::<String>();
|
|
err_str += &format!("\t{} {:?}\n", blank, entry);
|
|
}
|
|
panic!("incorrect drop list!\n{}", err_str);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_full_len_drop_amounts() {
|
|
expect_get_drop_ammounts(1, 1, false, vec![[1]]);
|
|
expect_get_drop_ammounts(1, 1, false, vec![[1, 0]]);
|
|
expect_get_drop_ammounts(1, 1, false, vec![[1, 0, 0]]);
|
|
expect_get_drop_ammounts(2, 2, false, vec![[1, 1, 0, 0, 0], [2, 0, 0, 0, 0]]);
|
|
expect_get_drop_ammounts(
|
|
3,
|
|
3,
|
|
false,
|
|
vec![
|
|
[1, 1, 1, 0, 0],
|
|
[1, 2, 0, 0, 0],
|
|
[2, 1, 0, 0, 0],
|
|
[3, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
4,
|
|
4,
|
|
false,
|
|
vec![
|
|
[1, 1, 1, 1, 0],
|
|
[1, 1, 2, 0, 0],
|
|
[1, 2, 1, 0, 0],
|
|
[1, 3, 0, 0, 0],
|
|
[2, 1, 1, 0, 0],
|
|
[2, 2, 0, 0, 0],
|
|
[3, 1, 0, 0, 0],
|
|
[4, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
5,
|
|
5,
|
|
false,
|
|
vec![
|
|
[1, 1, 1, 1, 1],
|
|
[1, 1, 1, 2, 0],
|
|
[1, 1, 2, 1, 0],
|
|
[1, 1, 3, 0, 0],
|
|
[1, 2, 1, 1, 0],
|
|
[1, 2, 2, 0, 0],
|
|
[1, 3, 1, 0, 0],
|
|
[1, 4, 0, 0, 0],
|
|
[2, 1, 1, 1, 0],
|
|
[2, 1, 2, 0, 0],
|
|
[2, 2, 1, 0, 0],
|
|
[2, 3, 0, 0, 0],
|
|
[3, 1, 1, 0, 0],
|
|
[3, 2, 0, 0, 0],
|
|
[4, 1, 0, 0, 0],
|
|
[5, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_truncated_drop_amounts() {
|
|
expect_get_drop_ammounts(
|
|
3,
|
|
2,
|
|
false,
|
|
vec![[1, 2, 0, 0, 0], [2, 1, 0, 0, 0], [3, 0, 0, 0, 0]],
|
|
);
|
|
expect_get_drop_ammounts(3, 1, false, vec![[3, 0, 0, 0, 0]]);
|
|
expect_get_drop_ammounts(
|
|
4,
|
|
3,
|
|
false,
|
|
vec![
|
|
[1, 1, 2, 0, 0],
|
|
[1, 2, 1, 0, 0],
|
|
[1, 3, 0, 0, 0],
|
|
[2, 1, 1, 0, 0],
|
|
[2, 2, 0, 0, 0],
|
|
[3, 1, 0, 0, 0],
|
|
[4, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
4,
|
|
2,
|
|
false,
|
|
vec![
|
|
[1, 3, 0, 0, 0],
|
|
[2, 2, 0, 0, 0],
|
|
[3, 1, 0, 0, 0],
|
|
[4, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
5,
|
|
4,
|
|
false,
|
|
vec![
|
|
[1, 1, 1, 2, 0],
|
|
[1, 1, 2, 1, 0],
|
|
[1, 1, 3, 0, 0],
|
|
[1, 2, 1, 1, 0],
|
|
[1, 2, 2, 0, 0],
|
|
[1, 3, 1, 0, 0],
|
|
[1, 4, 0, 0, 0],
|
|
[2, 1, 1, 1, 0],
|
|
[2, 1, 2, 0, 0],
|
|
[2, 2, 1, 0, 0],
|
|
[2, 3, 0, 0, 0],
|
|
[3, 1, 1, 0, 0],
|
|
[3, 2, 0, 0, 0],
|
|
[4, 1, 0, 0, 0],
|
|
[5, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
5,
|
|
3,
|
|
false,
|
|
vec![
|
|
[1, 1, 3, 0, 0],
|
|
[1, 2, 2, 0, 0],
|
|
[1, 3, 1, 0, 0],
|
|
[1, 4, 0, 0, 0],
|
|
[2, 1, 2, 0, 0],
|
|
[2, 2, 1, 0, 0],
|
|
[2, 3, 0, 0, 0],
|
|
[3, 1, 1, 0, 0],
|
|
[3, 2, 0, 0, 0],
|
|
[4, 1, 0, 0, 0],
|
|
[5, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(
|
|
5,
|
|
2,
|
|
false,
|
|
vec![
|
|
[1, 4, 0, 0, 0],
|
|
[2, 3, 0, 0, 0],
|
|
[3, 2, 0, 0, 0],
|
|
[4, 1, 0, 0, 0],
|
|
[5, 0, 0, 0, 0],
|
|
],
|
|
);
|
|
expect_get_drop_ammounts(5, 1, false, vec![[5, 0, 0, 0, 0]]);
|
|
}
|