use crate::algo::line::Line;
use crate::algo::{Error, PartCell};
use crate::{Item, Nonogram};
#[derive(Clone)]
pub struct Grid<T> {
lines: Vec<Line<T>>,
}
impl<T: Copy + PartialEq> Grid<T> {
pub fn build(numbers: Vec<Vec<Item<T>>>, length: usize) -> Self {
let lines = numbers
.into_iter()
.map(|col| Line::build(col, length))
.collect();
Self { lines }
}
pub fn flagged(&self) -> bool {
self.lines
.iter()
.map(Line::flagged)
.fold(false, |a, b| a | b)
}
pub fn update(&mut self) -> Result<(), Error> {
for line in self.lines.iter_mut() {
line.update()?;
}
Ok(())
}
pub fn get(&self, line: usize, cell: usize) -> PartCell<T> {
self.lines[line].get(cell)
}
pub fn set(&mut self, line: usize, cell: usize, value: PartCell<T>) -> Result<(), Error> {
self.lines[line].set(cell, value)
}
pub fn len(&self) -> (usize, usize) {
let inner = self.lines.first().map(Line::len).unwrap_or(0);
(self.lines.len(), inner)
}
pub fn write_to(&self, other: &mut Grid<T>) -> Result<(), Error> {
let (lines, cells) = self.len();
for line in 0..lines {
for cell in 0..cells {
other.set(cell, line, self.get(line, cell))?;
}
}
Ok(())
}
pub fn find_unsolved(&self) -> Option<(usize, usize, T)> {
self.lines
.iter()
.enumerate()
.filter_map(|(line, data)| {
data.find_unsolved()
.map(|(cell, color)| (line, cell, color))
})
.next()
}
}
impl<T: Copy + PartialEq> TryFrom<Grid<T>> for Nonogram<T> {
type Error = ();
fn try_from(grid: Grid<T>) -> Result<Self, Self::Error> {
let (cols, rows) = grid.len();
let mut nonogram = Nonogram::new(cols, rows);
for col in 0..cols {
for row in 0..rows {
nonogram[(col, row)] = grid.get(col, row).try_into()?;
}
}
Ok(nonogram)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::Cell;
#[test]
fn grid_set() {
let cols = vec![Vec::new(), Vec::new(), Vec::new(), Vec::new()];
let mut grid = Grid::build(cols, 6);
grid.set(1, 5, PartCell::Box { color: 2 }).unwrap();
assert!(matches!(grid.get(1, 5), PartCell::Box { color: 2 }));
}
#[test]
fn grid_flagged() {
let cols = vec![Vec::new(), Vec::new(), Vec::new(), Vec::new()];
let mut grid = Grid::build(cols, 6);
grid.set(2, 4, PartCell::Box { color: 4 }).unwrap();
assert!(grid.flagged());
}
#[test]
fn grid_update() {
let cols = vec![vec![Item::new(6, 2)], vec![]];
let mut grid = Grid::build(cols, 2);
grid.update().unwrap();
assert!(matches!(grid.get(0, 0), PartCell::Box { color: 6 }));
assert!(matches!(grid.get(0, 1), PartCell::Box { color: 6 }));
assert!(matches!(grid.get(1, 0), PartCell::Space));
assert!(matches!(grid.get(1, 1), PartCell::Space));
}
#[test]
fn grid_update_not_flagged() {
let cols = vec![vec![Item::new(6, 2)], vec![]];
let mut grid = Grid::build(cols, 2);
grid.update().unwrap();
assert!(!grid.flagged());
}
#[test]
fn grid_len() {
let cols = vec![Vec::new(), Vec::new()];
let grid: Grid<()> = Grid::build(cols, 5);
assert_eq!((2, 5), grid.len())
}
#[test]
fn grid_write_to() {
let cols = vec![vec![Item::new(6, 2)], vec![]];
let rows = vec![vec![Item::new(6, 1)], vec![Item::new(6, 1)]];
let mut cols = Grid::build(cols, 2);
let mut rows = Grid::build(rows, 2);
cols.update().unwrap();
cols.write_to(&mut rows).unwrap();
assert!(matches!(rows.get(0, 0), PartCell::Box { color: 6 }));
assert!(matches!(rows.get(0, 1), PartCell::Space));
assert!(matches!(rows.get(1, 0), PartCell::Box { color: 6 }));
assert!(matches!(rows.get(1, 1), PartCell::Space));
}
#[test]
fn grid_find_unsolved_some() {
let cols = vec![vec![Item::new(5, 1)], vec![]];
let mut grid = Grid::build(cols, 3);
grid.set(0, 0, PartCell::Space).unwrap();
grid.update().unwrap();
assert!(matches!(grid.find_unsolved(), Some((0, 1, 5))));
}
#[test]
fn grid_find_unsolved_none() {
let cols = vec![vec![Item::new(5, 1)], vec![]];
let mut grid = Grid::build(cols, 2);
grid.set(0, 0, PartCell::Space).unwrap();
grid.set(0, 1, PartCell::Box { color: 5 }).unwrap();
grid.set(1, 0, PartCell::Space).unwrap();
grid.set(1, 1, PartCell::Space).unwrap();
grid.update().unwrap();
assert!(matches!(grid.find_unsolved(), None));
}
#[test]
fn nonogram_try_from_grid() {
let cols = vec![vec![Item::new(6, 2)], vec![]];
let mut cols = Grid::build(cols, 2);
cols.update().unwrap();
let nonogram: Nonogram<i32> = cols.try_into().unwrap();
assert!(matches!(nonogram[(0, 0)], Cell::Box { color: 6 }));
assert!(matches!(nonogram[(0, 1)], Cell::Box { color: 6 }));
assert!(matches!(nonogram[(1, 0)], Cell::Space));
assert!(matches!(nonogram[(1, 1)], Cell::Space));
}
#[test]
fn nonogram_try_from_grid_err() {
let cols = vec![vec![Item::new(6, 1)]];
let mut cols = Grid::build(cols, 2);
cols.update().unwrap();
let nonogram: Result<Nonogram<i32>, ()> = cols.try_into();
assert!(nonogram.is_err());
}
}