sudoku 0.8.0

A sudoku solver library
Documentation
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(())
}

//             goal_depth
// <degenerated>   1 (basically a naked/hidden single, not supported by this fn)
// x-wing          2
// swordfish       3
// jellyfish       4
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,
    // non-constant args
    line_set: Set<Line>,
    mut lines: SetIter<Line>,
    union_poss_pos: Set<Position<Line>>,
) -> bool {
    if line_set.len() == goal_depth {
        // nothing of interest found
        if union_poss_pos.len() != goal_depth {
            return false;
        }

        // found xwing, swordfish, or jellyfish
        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();

        // n_poss == 0 => solved row (or impossible)
        // n_poss == 1 => hidden single
        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
}