nonogram 0.3.0

Nonogram generation and solution package
Documentation
extern crate ndarray;

use ndarray::Array1;
use std::collections::{HashSet, VecDeque};

mod state_grid;
mod state_row;

pub use state_grid::StateGrid;
pub use state_row::StateRow;

#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
pub enum CellState {
    Unknown,
    Empty,
    Filled,
}

impl Default for CellState {
    fn default() -> Self {
        CellState::Unknown
    }
}

pub fn enumerate_row_states(size: usize, clue: &[usize]) -> Vec<StateRow> {
    let mut queue: VecDeque<StateRow> = VecDeque::new();
    let mut known_rows: HashSet<StateRow> = HashSet::new();

    queue.push_back(StateRow(Array1::default(size)));

    while let Some(row) = queue.pop_front() {
        if row.is_known() {
            known_rows.insert(row);
        } else {
            if let Some(seg_row) = row.new_w_appended_seg(clue) {
                queue.push_front(seg_row);
            }

            if let Some(zero_row) = row.new_w_appended_zero(clue) {
                queue.push_front(zero_row);
            }
        }
    }

    known_rows.iter().cloned().collect()
}

pub fn filter_invalid_row_states(
    known_row: &StateRow,
    previous_states: &[StateRow],
) -> Vec<StateRow> {
    previous_states
        .iter()
        .cloned()
        .filter(|state_row| {
            known_row
                .0
                .iter()
                .zip(state_row.0.iter())
                .all(|(known_cell, state_cell)| {
                    *known_cell == CellState::Unknown
                        || (*known_cell != CellState::Unknown && known_cell == state_cell)
                })
        })
        .collect()
}

pub fn common_row_indexes(states: &[StateRow]) -> Vec<(usize, CellState)> {
    let mut common_empty: Array1<usize> = Array1::zeros(states[0].0.len());
    let mut common_filled: Array1<usize> = Array1::zeros(states[0].0.len());

    for state in states {
        for i in 0..state.0.len() {
            match state.0[i] {
                CellState::Empty => common_empty[i] += 1,
                CellState::Filled => common_filled[i] += 1,
                _ => (),
            };
        }
    }

    (0..states[0].0.len())
        .filter_map(|index| {
            if common_empty[index] == states.len() {
                Some((index, CellState::Empty))
            } else if common_filled[index] == states.len() {
                Some((index, CellState::Filled))
            } else {
                None
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::{
        common_row_indexes, enumerate_row_states, filter_invalid_row_states, CellState, StateRow,
    };
    use ndarray::arr1;

    #[test]
    fn enumerate_row_states1() {
        let states = enumerate_row_states(5, &[1, 1, 1]);

        assert_eq!(states.len(), 1);
    }

    #[test]
    fn enumerate_row_states2() {
        let states = enumerate_row_states(5, &[3, 1]);

        assert_eq!(states.len(), 1);
    }

    #[test]
    fn enumerate_row_states3() {
        let states = enumerate_row_states(5, &[1, 2]);

        assert_eq!(states.len(), 3);
    }

    #[test]
    fn enumerate_row_states4() {
        let states = enumerate_row_states(5, &[1, 1]);

        assert_eq!(states.len(), 6);
    }

    #[test]
    fn enumerate_row_states5() {
        let states = enumerate_row_states(5, &[1]);

        assert_eq!(states.len(), 5);
    }

    #[test]
    fn filter_invalid_row_states1() {
        let known = StateRow(arr1(&[
            CellState::Filled,
            CellState::Empty,
            CellState::Unknown,
            CellState::Empty,
            CellState::Unknown,
        ]));
        let enumerated_states = enumerate_row_states(5, &[1, 1]);
        let filtered_states = filter_invalid_row_states(&known, &enumerated_states);

        assert_eq!(filtered_states.len(), 2);
    }

    #[test]
    fn filter_invalid_row_states2() {
        let known = StateRow(arr1(&[
            CellState::Empty,
            CellState::Unknown,
            CellState::Filled,
            CellState::Unknown,
            CellState::Empty,
        ]));
        let enumerated_states = enumerate_row_states(5, &[2]);
        let filtered_states = filter_invalid_row_states(&known, &enumerated_states);

        assert_eq!(filtered_states.len(), 2);
    }

    #[test]
    fn filter_invalid_row_states3() {
        let known = StateRow(arr1(&[
            CellState::Filled,
            CellState::Unknown,
            CellState::Filled,
            CellState::Unknown,
            CellState::Empty,
        ]));
        let enumerated_states = enumerate_row_states(5, &[3]);
        let filtered_states = filter_invalid_row_states(&known, &enumerated_states);

        assert_eq!(filtered_states.len(), 1);
    }

    #[test]
    fn common_row_indexes1() {
        let enumerated_row_states = enumerate_row_states(5, &[3]);
        let common_cells = common_row_indexes(&enumerated_row_states);

        assert_eq!(common_cells.len(), 1);
        assert_eq!(common_cells[0].0, 2);
        assert_eq!(common_cells[0].1, CellState::Filled);
    }

    #[test]
    fn common_row_indexes2() {
        let enumerated_row_states = enumerate_row_states(5, &[2, 1]);
        let common_cells = common_row_indexes(&enumerated_row_states);

        assert_eq!(common_cells.len(), 1);
        assert_eq!(common_cells[0].0, 1);
        assert_eq!(common_cells[0].1, CellState::Filled);
    }

    #[test]
    fn common_row_indexes3() {
        let enumerated_row_states = enumerate_row_states(5, &[1, 3]);
        let common_cells = common_row_indexes(&enumerated_row_states);

        assert_eq!(common_cells.len(), 5);
        assert_eq!(common_cells[0].0, 0);
        assert_eq!(common_cells[0].1, CellState::Filled);
        assert_eq!(common_cells[1].0, 1);
        assert_eq!(common_cells[1].1, CellState::Empty);
        assert_eq!(common_cells[2].0, 2);
        assert_eq!(common_cells[2].1, CellState::Filled);
        assert_eq!(common_cells[3].0, 3);
        assert_eq!(common_cells[3].1, CellState::Filled);
        assert_eq!(common_cells[4].0, 4);
        assert_eq!(common_cells[4].1, CellState::Filled);
    }
}