use crate::{ Board, Move, piece::{ColoredPiece, Piece}, }; impl Board { pub fn moves(&self) -> impl Iterator> { 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(x: u8, y: u8, can_place_caps: bool) -> Vec> { 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(x: u8, y: u8, stack_len: u8) -> Vec> { 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( // 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( 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::(); 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]]); }