mod state;
extern crate crc;
extern crate ndarray;
extern crate ndarray_rand;
extern crate rand;
extern crate serde;
extern crate serde_json;
use crc::crc64::checksum_ecma;
use ndarray::{iter::Lanes, ArrayView1, Ix1};
use ndarray_rand::RandomExt;
use rand::distributions::Uniform;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Error as JsonError;
use std::hash::{Hash, Hasher};
pub use ndarray::{arr1, arr2, Array1, Array2};
use state::{
common_row_indexes, enumerate_row_states, filter_invalid_row_states, StateGrid, StateRow,
};
fn build_clue(row: ArrayView1<u8>) -> Vec<usize> {
let mut clue: Vec<usize> = Vec::new();
row.into_iter()
.cloned()
.collect::<Vec<u8>>()
.split(|cell| *cell == 0u8)
.for_each(|segment| {
if !segment.is_empty() {
clue.push(segment.len());
}
});
clue
}
fn build_clues(grid: Lanes<u8, Ix1>) -> Array1<Vec<usize>> {
grid.into_iter().map(build_clue).collect()
}
#[derive(Debug)]
pub struct Nonogram {
pub row_segments: Array1<Vec<usize>>,
pub column_segments: Array1<Vec<usize>>,
pub completed_grid: Array2<u8>,
}
impl Nonogram {
pub fn generate(width: usize, height: usize) -> Nonogram {
let completed_grid = Array2::random((height, width), Uniform::new_inclusive(0, 1));
Nonogram {
row_segments: build_clues(completed_grid.genrows()),
column_segments: build_clues(completed_grid.gencolumns()),
completed_grid,
}
}
pub fn height(&self) -> usize {
self.completed_grid.dim().0
}
pub fn width(&self) -> usize {
self.completed_grid.dim().1
}
pub fn solvable(&self) -> bool {
let mut row_possibilities: Vec<Vec<StateRow>> = self
.row_segments
.iter()
.map(|clue| enumerate_row_states(self.width(), &clue))
.collect();
let mut column_possibilities: Vec<Vec<StateRow>> = self
.column_segments
.iter()
.map(|clue| enumerate_row_states(self.height(), &clue))
.collect();
let mut grid = StateGrid::new(self.height(), self.width());
loop {
let mut changes = 0;
for (index, possibilities) in row_possibilities.iter_mut().enumerate() {
let common = common_row_indexes(&possibilities);
for cell in &common {
if let Some(current_cell) = grid.get(index, cell.0) {
if *current_cell != cell.1 {
grid.set(index, cell.0, cell.1);
changes += 1;
}
}
}
let filtered_possibilities =
filter_invalid_row_states(&grid.get_row(index), &possibilities);
changes += possibilities.len() - filtered_possibilities.len();
*possibilities = filtered_possibilities;
}
for (index, possibilities) in column_possibilities.iter_mut().enumerate() {
let common = common_row_indexes(&possibilities);
for cell in &common {
if let Some(current_cell) = grid.get(cell.0, index) {
if *current_cell != cell.1 {
grid.set(cell.0, index, cell.1);
changes += 1;
}
}
}
let filtered_possibilities =
filter_invalid_row_states(&grid.get_column(index), &possibilities);
changes += possibilities.len() - filtered_possibilities.len();
*possibilities = filtered_possibilities;
}
if changes == 0 {
break false;
}
if grid.is_known() {
break true;
}
}
}
pub fn generate_checksum(&self) -> u64 {
let mut aggregate: Vec<u8> = Vec::new();
self.completed_grid
.iter()
.for_each(|cell| aggregate.push(*cell));
checksum_ecma(aggregate.as_slice())
}
pub fn as_json(&self) -> Result<String, JsonError> {
serde_json::to_string(&SerializedNonogram::from_nonogram(self))
}
pub fn from_json(serialized: &str) -> Result<Nonogram, String> {
match serde_json::from_str::<SerializedNonogram>(serialized) {
Ok(deserialized) => match deserialized.to_nonogram() {
Ok(nonogram) => Ok(nonogram),
Err(e) => Err(e.to_string()),
},
Err(e) => Err(e.to_string()),
}
}
}
impl Hash for Nonogram {
fn hash<H: Hasher>(&self, state: &mut H) {
self.height().hash(state);
self.width().hash(state);
self.generate_checksum().hash(state);
}
}
impl PartialEq for Nonogram {
fn eq(&self, other: &Self) -> bool {
self.height() == other.height()
&& self.width() == other.width()
&& self.generate_checksum() == other.generate_checksum()
}
}
impl Eq for Nonogram {}
impl Serialize for Nonogram {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
SerializedNonogram::from_nonogram(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Nonogram {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
match SerializedNonogram::deserialize(deserializer) {
Ok(deserialized) => Ok(deserialized.to_nonogram().unwrap()),
Err(e) => Err(e),
}
}
}
#[derive(Serialize, Deserialize)]
struct SerializedNonogram {
checksum: u64,
height: usize,
width: usize,
row_segments: Vec<Vec<usize>>,
column_segments: Vec<Vec<usize>>,
completed_grid: Vec<Vec<u8>>,
}
impl SerializedNonogram {
fn from_nonogram(original: &Nonogram) -> SerializedNonogram {
SerializedNonogram {
checksum: original.generate_checksum(),
height: original.height(),
width: original.width(),
row_segments: original.row_segments.iter().cloned().collect(),
column_segments: original.column_segments.iter().cloned().collect(),
completed_grid: original
.completed_grid
.genrows()
.into_iter()
.map(|row| row.iter().cloned().collect())
.collect(),
}
}
fn to_nonogram(&self) -> Result<Nonogram, String> {
let grid = Array2::from_shape_vec(
(self.height, self.width),
self.completed_grid.iter().flatten().cloned().collect(),
);
match grid {
Ok(grid_array) => Ok(Nonogram {
row_segments: arr1(self.row_segments.as_slice()),
column_segments: arr1(self.column_segments.as_slice()),
completed_grid: grid_array,
}),
Err(e) => Err(e.to_string()),
}
}
}