From 9e74d12329464ea1fd3247fb2a4e5e79482d4e19 Mon Sep 17 00:00:00 2001 From: mehbark Date: Wed, 9 Jul 2025 16:21:00 -0400 Subject: [PATCH] init --- .gitignore | 1 + Cargo.lock | 7 ++ Cargo.toml | 6 ++ src/main.rs | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2d5cf51 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "nonogram" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..43ab04b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "nonogram" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..8e6f880 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,195 @@ +use std::{array, env, error::Error, fmt, str::FromStr}; + +// TODO: we need <4 bits +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +enum Clue { + C0, + C1, + C2, + C3, + C4, + C5, + C1_1, + C1_2, + C2_1, + C1_3, + C3_1, + C2_2, + C1_1_1, +} + +impl From for Clue { + fn from(row: Row) -> Self { + match row.0 { + 0b00000 => Clue::C0, + 0b10000 | 0b01000 | 0b00100 | 0b00010 | 0b00001 => Clue::C1, + 0b11000 | 0b01100 | 0b00110 | 0b00011 => Clue::C2, + 0b11100 | 0b01110 | 0b00111 => Clue::C3, + 0b11110 | 0b01111 => Clue::C4, + 0b11111 => Clue::C5, + 0b10100 | 0b10010 | 0b10001 | 0b01010 | 0b01001 | 0b00101 => Clue::C1_1, + 0b10110 | 0b10011 | 0b01011 => Clue::C1_2, + 0b11010 | 0b11001 | 0b01101 => Clue::C2_1, + 0b10111 => Clue::C1_3, + 0b11101 => Clue::C3_1, + 0b11011 => Clue::C2_2, + 0b10101 => Clue::C1_1_1, + _ => unreachable!(), + } + } +} + +impl FromStr for Clue { + type Err = (); + + fn from_str(s: &str) -> Result { + #[allow(clippy::enum_glob_use)] + use Clue::*; + + Ok(match s.trim() { + "0" => C0, + "1" => C1, + "2" => C2, + "3" => C3, + "4" => C4, + "5" => C5, + "1 1" => C1_1, + "1 2" => C1_2, + "2 1" => C2_1, + "1 3" => C1_3, + "3 1" => C3_1, + "2 2" => C2_2, + "1 1 1" => C1_1_1, + _ => return Err(()), + }) + } +} + +impl fmt::Display for Clue { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{:>5}", + match self { + Clue::C0 => "0", + Clue::C1 => "1", + Clue::C2 => "2", + Clue::C3 => "3", + Clue::C4 => "4", + Clue::C5 => "5", + Clue::C1_1 => "1 1", + Clue::C1_2 => "1 2", + Clue::C2_1 => "2 1", + Clue::C1_3 => "1 3", + Clue::C3_1 => "3 1", + Clue::C2_2 => "2 2", + Clue::C1_1_1 => "1 1 1", + } + ) + } +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +struct Row(u8); + +impl fmt::Debug for Row { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:05b}", self.0) + } +} + +impl fmt::Display for Row { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for i in (0..5).rev() { + write!(f, "{}", if (self.0 >> i) & 1 == 1 { '#' } else { '.' })?; + } + Ok(()) + } +} + +#[derive(Clone, Copy)] +#[repr(transparent)] +struct Board(u32); + +impl Board { + const fn cardinality() -> u32 { + 1 << 25 + } + + fn universe() -> impl Iterator { + (0..Self::cardinality()).map(Self) + } + + fn rows(self) -> [Row; 5] { + array::from_fn(|i| Row((self.0 >> (i * 5) & 0b11111) as u8)) + } + + fn columns(self) -> [Row; 5] { + let [r1, r2, r3, r4, r5] = self.rows(); + array::from_fn(|i| { + let i = 4 - i; + Row(((r1.0 >> i) & 1) << 4 + | ((r2.0 >> i) & 1) << 3 + | ((r3.0 >> i) & 1) << 2 + | ((r4.0 >> i) & 1) << 1 + | ((r5.0 >> i) & 1)) + }) + } +} + +impl fmt::Debug for Board { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for row in self.rows() { + writeln!(f, "{row:?}")?; + } + Ok(()) + } +} + +impl fmt::Display for Board { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for row in self.rows() { + writeln!(f, "{}|{}", Clue::from(row), row)?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Clues { + rows: [Clue; 5], + columns: [Clue; 5], +} + +impl From for Clues { + fn from(board: Board) -> Self { + let rows = board.rows().map(Clue::from); + let columns = board.columns().map(Clue::from); + Clues { rows, columns } + } +} + +fn parse_clues<'a>(i: impl Iterator) -> Result<[Clue; 5], &'static str> { + i.map(str::parse::) + .collect::, _>>() + .map_err(|()| "bad clue")? + .try_into() + .map_err(|_| "five clues please") +} + +#[allow(clippy::cast_possible_truncation)] +fn main() -> Result<(), Box> { + let rows = parse_clues(env::args().nth(1).ok_or("give me row clues")?.split(','))?; + let columns = parse_clues(env::args().nth(2).ok_or("give me column clues")?.split(','))?; + let clues = Clues { rows, columns }; + println!("{clues:#?}"); + for board in Board::universe() { + if Clues::from(board) == clues { + println!("{board}"); + return Ok(()); + } + } + Err("couldn't solve")? +}