init
This commit is contained in:
commit
9e74d12329
4 changed files with 209 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
|
@ -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"
|
||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "nonogram"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
195
src/main.rs
Normal file
195
src/main.rs
Normal file
|
|
@ -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<Row> 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<Self, Self::Err> {
|
||||
#[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<Item = Self> {
|
||||
(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<Board> 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<Item = &'a str>) -> Result<[Clue; 5], &'static str> {
|
||||
i.map(str::parse::<Clue>)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|()| "bad clue")?
|
||||
.try_into()
|
||||
.map_err(|_| "five clues please")
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
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")?
|
||||
}
|
||||
Loading…
Reference in a new issue