From 80e891268a85d73b85690f4bf0ba81eee53726bb Mon Sep 17 00:00:00 2001 From: mehbark Date: Wed, 4 Jan 2023 22:34:46 -0500 Subject: [PATCH] initial --- Cargo.lock | 7 +++++ Cargo.toml | 8 +++++ src/board.rs | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 11 +++++++ src/solver.rs | 63 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/board.rs create mode 100644 src/main.rs create mode 100644 src/solver.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..70c2396 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "most-chips-needed-for-bingo-board" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7063b0e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "most-chips-needed-for-bingo-board" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/board.rs b/src/board.rs new file mode 100644 index 0000000..15aa398 --- /dev/null +++ b/src/board.rs @@ -0,0 +1,84 @@ +use std::array; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Board { + checked: [bool; 25], +} + +// a u32 would be much more efficient (only need 25 bits) + +impl Board { + pub fn new() -> Self { + let checked = array::from_fn(|i| i == Self::idx(2, 2)); + + Self { checked } + } + + pub fn new_without_center_dot() -> Self { + let checked = array::from_fn(|_| false); + + Self { checked } + } + + fn idx(x: usize, y: usize) -> usize { + x + y * 5 + } + + fn checked_at(&self, x: usize, y: usize) -> bool { + self.checked[Self::idx(x, y)] + } + + fn checked_at_idx(&self, idx: usize) -> bool { + self.checked[idx] + } + + pub fn num_chips(&self) -> usize { + self.checked.iter().filter(|b| **b).count() + } + + pub fn is_done(&self) -> bool { + self.rows() + .chain(self.cols()) + .chain(self.diags()) + .any(and_all) + } + + fn rows(&self) -> impl Iterator + '_ { + (0..5).map(|y| array::from_fn(|x| self.checked_at(x, y))) + } + + fn cols(&self) -> impl Iterator + '_ { + (0..5).map(|x| array::from_fn(|y| self.checked_at(x, y))) + } + + fn diags(&self) -> impl Iterator + '_ { + [ + array::from_fn(|i| self.checked_at(i, i)), + array::from_fn(|i| self.checked_at(i, 4 - i)), + ] + .into_iter() + } + + fn with_checked(&self, at: usize) -> Self { + let mut new = *self; + new.checked[at] = true; + new + } + + pub fn successors(&self) -> impl Iterator + '_ { + self.checked + .into_iter() + .enumerate() + .filter_map(|(i, checked)| { + if checked { + None + } else { + Some(self.with_checked(i)) + } + }) + } +} + +fn and_all(bs: [bool; N]) -> bool { + bs.iter().all(|b| *b) +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..354cf16 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,11 @@ +mod board; +mod solver; + +use crate::solver::solution; + +fn main() { + let solution = solution(); + eprintln!("The maximum number of chips needed for a 5x5 bingo board that starts with the center filled is:"); + println!("{solution}"); + eprintln!("(This assumes that people want to fill their winning square, which does make sense for showing that you won I suppose)"); +} diff --git a/src/solver.rs b/src/solver.rs new file mode 100644 index 0000000..533aed8 --- /dev/null +++ b/src/solver.rs @@ -0,0 +1,63 @@ +use crate::board::Board; +use std::collections::HashSet; + +pub fn solution() -> usize { + let mut solver = Solver::new(); + solver.step_until_pointless(); + solver.most_checked() +} + +// there is definitely reflectional/rotational symmetry that could be utilized + +#[derive(Debug, Clone)] +struct Solver { + end_states: HashSet, + in_progress_states: HashSet, + handled_states: HashSet, +} + +impl Solver { + fn new() -> Self { + Self { + end_states: HashSet::new(), + in_progress_states: HashSet::from([Board::new()]), + handled_states: HashSet::new(), + } + } + + fn most_checked(&self) -> usize { + self.end_states.iter().map(Board::num_chips).max().unwrap() + } + + fn step_until_pointless(&mut self) { + loop { + let prev_end_state_count = self.end_states.len(); + dbg!(prev_end_state_count); + + self.step(); + + if !self.end_states.is_empty() && self.end_states.len() == prev_end_state_count { + break; + } + } + } + + fn step(&mut self) { + let mut new_end_states = HashSet::new(); + let mut new_ip_states = HashSet::new(); + + for state in &self.in_progress_states { + for succ in state.successors() { + if succ.is_done() { + new_end_states.insert(succ); + } else if !self.handled_states.contains(&succ) { + new_ip_states.insert(succ); + } + } + } + + self.end_states.extend(new_end_states); + self.handled_states.extend(self.in_progress_states.iter()); + self.in_progress_states = new_ip_states; + } +}