initial
This commit is contained in:
commit
80e891268a
5 changed files with 173 additions and 0 deletions
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 = 3
|
||||
|
||||
[[package]]
|
||||
name = "most-chips-needed-for-bingo-board"
|
||||
version = "0.1.0"
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
|
@ -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]
|
84
src/board.rs
Normal file
84
src/board.rs
Normal file
|
@ -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<Item = [bool; 5]> + '_ {
|
||||
(0..5).map(|y| array::from_fn(|x| self.checked_at(x, y)))
|
||||
}
|
||||
|
||||
fn cols(&self) -> impl Iterator<Item = [bool; 5]> + '_ {
|
||||
(0..5).map(|x| array::from_fn(|y| self.checked_at(x, y)))
|
||||
}
|
||||
|
||||
fn diags(&self) -> impl Iterator<Item = [bool; 5]> + '_ {
|
||||
[
|
||||
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<Item = Self> + '_ {
|
||||
self.checked
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, checked)| {
|
||||
if checked {
|
||||
None
|
||||
} else {
|
||||
Some(self.with_checked(i))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn and_all<const N: usize>(bs: [bool; N]) -> bool {
|
||||
bs.iter().all(|b| *b)
|
||||
}
|
11
src/main.rs
Normal file
11
src/main.rs
Normal file
|
@ -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)");
|
||||
}
|
63
src/solver.rs
Normal file
63
src/solver.rs
Normal file
|
@ -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<Board>,
|
||||
in_progress_states: HashSet<Board>,
|
||||
handled_states: HashSet<Board>,
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue