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