use super::prelude::*;
pub(crate) fn find_fish(
house_poss_positions: &HouseArray<DigitArray<Set<Position<House>>>>,
max_size: u8,
stop_after_first: bool,
mut on_fish: impl FnMut(
Set<Line>, // all rows or all cols
Digit, //
Set<Line>, // base
Set<Position<Line>>, // cover
) -> bool,
) -> Result<(), Unsolvable> {
for digit in (1..10).map(Digit::new) {
for &lines in &[Line::ALL_ROWS, Line::ALL_COLS] {
if basic_fish_walk_combinations(
house_poss_positions,
digit,
max_size,
lines,
&mut on_fish,
stop_after_first,
Set::NONE,
lines.into_iter(),
Set::NONE,
) {
return Ok(());
};
}
}
Ok(())
}
fn basic_fish_walk_combinations(
house_poss_positions: &HouseArray<DigitArray<Set<Position<House>>>>,
digit: Digit,
goal_depth: u8,
all_lines: Set<Line>,
on_fish: &mut impl FnMut(Set<Line>, Digit, Set<Line>, Set<Position<Line>>) -> bool,
stop_after_first: bool,
line_set: Set<Line>,
mut lines: SetIter<Line>,
union_poss_pos: Set<Position<Line>>,
) -> bool {
if line_set.len() == goal_depth {
if union_poss_pos.len() != goal_depth {
return false;
}
if on_fish(all_lines, digit, line_set, union_poss_pos) {
return stop_after_first;
}
}
while let Some(line) = lines.next() {
let possible_pos = house_poss_positions[line][digit];
let n_poss = possible_pos.len();
let new_union_poss_pos = union_poss_pos | possible_pos.as_line_set();
if n_poss < 2 || new_union_poss_pos.len() > goal_depth {
continue;
}
let new_line_set = line_set | line.as_set();
if basic_fish_walk_combinations(
house_poss_positions,
digit,
goal_depth,
all_lines,
on_fish,
stop_after_first,
new_line_set,
lines.clone(),
new_union_poss_pos,
) {
return true;
};
}
false
}