2024-12-13 00:52:18 +01:00

197 lines
5.3 KiB
Rust

use std::{
fmt::Display,
sync::{Arc, Mutex},
};
pub const DEFAULT_SEED: u64 = 2024;
use axum::response::IntoResponse;
use rand::{rngs::StdRng, Rng, SeedableRng};
use super::{super::error::GameError, column::Column, Slot, EMPTY, WALL};
#[derive(Clone, Debug)]
pub struct Board {
columns: Arc<Mutex<[[Option<Slot>; 4]; 4]>>,
seed: Arc<Mutex<StdRng>>,
}
impl Board {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
columns: Arc::new(Mutex::new([[None; 4]; 4])),
seed: Arc::new(Mutex::new(StdRng::seed_from_u64(DEFAULT_SEED))),
}
}
pub fn display(&self) -> String {
format!(
"{}\n",
self.to_string()
.split_terminator('\n')
.take(5)
.collect::<Vec<_>>()
.join("\n")
)
}
pub fn get_seed(&self) -> Arc<Mutex<StdRng>> {
self.seed.clone()
}
pub fn random(&self, random: &mut StdRng) {
let mut columns: [[Option<Slot>; 4]; 4] = [[None; 4]; 4];
// let mut seed = self.seed.lock().unwrap();
for i in (0..4).rev() {
(0..4).for_each(|j| {
let random = random.gen::<bool>();
let slot = if random {
Some(Slot::Cookie)
} else {
Some(Slot::Milk)
};
columns[j][i] = slot;
});
}
// for column in &mut columns.iter_mut() {
// for slot in column.iter_mut() {
// let random = random.gen::<bool>();
// if random {
// *slot = Some(Slot::Cookie);
// } else {
// *slot = Some(Slot::Milk);
// }
// }
// }
// drop(seed);
{
let mut cols = self.columns.lock().unwrap();
*cols = columns;
}
}
pub fn is_full(&self) -> bool {
let columns = self.columns.lock().unwrap();
columns
.iter()
.all(|column| column.iter().all(std::option::Option::is_some))
}
pub fn check(&self) -> Option<Slot> {
let columns = self.columns.lock().unwrap();
// Check rows and columns
for i in 0..4 {
if columns[i][0].is_some()
&& columns[i][0] == columns[i][1]
&& columns[i][0] == columns[i][2]
&& columns[i][0] == columns[i][3]
{
return columns[i][0];
}
if columns[0][i].is_some()
&& columns[0][i] == columns[1][i]
&& columns[0][i] == columns[2][i]
&& columns[0][i] == columns[3][i]
{
return columns[0][i];
}
}
// Check diagonals
if columns[0][0].is_some()
&& columns[0][0] == columns[1][1]
&& columns[0][0] == columns[2][2]
&& columns[0][0] == columns[3][3]
{
return columns[0][0];
}
if columns[0][3].is_some()
&& columns[0][3] == columns[1][2]
&& columns[0][3] == columns[2][1]
&& columns[0][3] == columns[3][0]
{
return columns[0][3];
}
None
}
#[allow(dead_code)]
pub fn reset(&self) {
{
let mut columns = self.columns.lock().unwrap();
for i in 0..4 {
for j in 0..4 {
columns[i][j] = None;
}
}
}
{
let mut seed = self.seed.lock().unwrap();
*seed = StdRng::seed_from_u64(DEFAULT_SEED);
}
}
#[allow(dead_code)]
pub fn insert(&self, column: Column, slot: Slot) -> Result<(), GameError> {
if self.check().is_some() {
return Err(GameError::GameOver);
}
{
let column: usize = column.into();
let column = column - 1;
let mut columns = self.columns.lock().unwrap();
if column > columns.len() - 1 {
return Err(GameError::InvalidColumn);
}
for i in 0..4 {
if columns[column][i].is_none() {
columns[column][i] = Some(slot);
return Ok(());
}
}
drop(columns);
}
Err(GameError::ColumnFull)
}
}
impl Display for Board {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
{
let columns = self.columns.lock().unwrap();
for j in (0..4).rev() {
write!(f, "{WALL}")?;
for i in 0..4 {
match columns[i][j] {
Some(slot) => write!(f, "{slot}")?,
None => write!(f, "{EMPTY}")?,
}
}
writeln!(f, "{WALL}")?;
}
}
for _ in 0..6 {
write!(f, "{WALL}")?;
}
writeln!(f)?;
if let Some(winner) = self.check() {
writeln!(f, "{winner} wins!")?;
} else if self.is_full() {
writeln!(f, "No winner.")?;
}
Ok(())
}
}
impl IntoResponse for Board {
fn into_response(self) -> axum::response::Response {
(axum::http::StatusCode::OK, self.to_string()).into_response()
}
}