gamestate

Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
This commit is contained in:
Jana Dönszelmann 2025-09-10 20:00:50 -07:00 committed by Julia Ryan
commit 672f9e09bf
No known key found for this signature in database
5 changed files with 230 additions and 0 deletions

211
src/gamestate.rs Normal file
View file

@ -0,0 +1,211 @@
use std::fmt::Display;
#[derive(Clone, Copy, PartialEq)]
struct PlayerState {
xy: u8,
walls_left: u8,
}
impl PlayerState {
pub const P1_START: Self = Self::new(4, 0, 9);
pub const P2_START: Self = Self::new(4, 8, 9);
pub const fn new(x: u8, y: u8, walls_left: u8) -> Self {
let mut res = Self { xy: 0, walls_left };
res.set_x(x);
res.set_y(y);
res
}
}
impl PlayerState {
pub const fn x(&self) -> u8 {
self.xy & 0b00001111
}
pub const fn y(&self) -> u8 {
(self.xy & 0b11110000) >> 4
}
pub const fn set_x(&mut self, x: u8) {
// zero out x
self.xy &= 0b11110000;
// write the x part
self.xy |= x & 0b00001111;
}
pub const fn set_y(&mut self, y: u8) {
// zero out y
self.xy &= 0b00001111;
// write the y part
self.xy |= (y & 0b00001111) << 4;
}
}
#[derive(Copy, Clone)]
struct WallState {
verticals: [u8; 9],
horizontals: [u8; 9],
}
impl WallState {
#[inline]
pub fn block_cleaned_hori(&mut self, byte_idx: u8, bit: u8) {
self.horizontals[byte_idx as usize] |= 1 << bit;
}
#[inline]
pub fn block_cleaned_verti(&mut self, byte_idx: u8, bit: u8) {
self.verticals[byte_idx as usize] |= 1 << bit;
}
#[inline]
pub fn can_walk_between_cleaned_hori(&self, byte_idx: u8, bit: u8) -> bool {
(self.horizontals[byte_idx as usize] >> bit) & 1 != 0
}
#[inline]
pub fn can_walk_between_cleaned_verti(&self, byte_idx: u8, bit: u8) -> bool {
(self.verticals[byte_idx as usize] >> bit) & 1 != 0
}
pub fn block(&mut self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) {
match (from_x.wrapping_sub(to_x), from_y.wrapping_sub(to_y)) {
(1, 0) => self.block_cleaned_verti(to_y, to_x),
(0xff, 0) => self.block_cleaned_verti(from_y, from_x),
(0, 1) => self.block_cleaned_hori(to_x, to_y),
(0, 0xff) => self.block_cleaned_hori(from_x, from_y),
_ => unreachable!(),
}
}
pub fn can_walk_between(&self, from_x: u8, from_y: u8, to_x: u8, to_y: u8) -> bool {
!match (from_x.wrapping_sub(to_x), from_y.wrapping_sub(to_y)) {
(1, 0) => self.can_walk_between_cleaned_verti(to_y, to_x),
(0xff, 0) => self.can_walk_between_cleaned_verti(from_y, from_x),
(0, 1) => self.can_walk_between_cleaned_hori(to_x, to_y),
(0, 0xff) => self.can_walk_between_cleaned_hori(from_x, from_y),
_ => unreachable!(),
}
}
}
impl Default for WallState {
fn default() -> Self {
Self {
verticals: Default::default(),
horizontals: Default::default(),
}
}
}
struct GameState {
p1: PlayerState,
p2: PlayerState,
walls: WallState,
}
impl Default for GameState {
fn default() -> Self {
Self {
p1: PlayerState::P1_START,
p2: PlayerState::P2_START,
walls: Default::default(),
}
}
}
/*
what we're going for:
-- -- --
| | |P2|
-- -- --
|P1| | |
-- -- --
*/
impl Display for GameState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"P1: {}, P2: {}\n",
self.p1.walls_left, self.p2.walls_left
)?;
for y in 0..9 {
for x in 0..9 {
if y > 0 {
let wall = if !self.walls.can_walk_between(x, y - 1, x, y) {
' '
} else {
'-'
};
write!(f, "{wall}{wall}")?;
}
if x != 8 {
write!(f, " ")?;
}
}
writeln!(f, "")?;
for x in 0..9 {
if x > 0 {
let wall = if !self.walls.can_walk_between(x - 1, y, x, y) {
' '
} else {
'|'
};
write!(f, "{wall}")?;
}
let player = if self.p1.x() == x && self.p1.y() == y {
"P1"
} else if self.p2.x() == x && self.p2.y() == y {
"P2"
} else {
" "
};
write!(f, "{player}")?;
}
writeln!(f, "")?;
}
writeln!(f, "")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::gamestate::{GameState, PlayerState, WallState};
#[test]
fn test_blocking() {
let mut w = WallState::default();
assert!(w.can_walk_between(0, 0, 1, 0));
w.block(0, 0, 1, 0);
assert!(!w.can_walk_between(0, 0, 1, 0));
w.block(8, 7, 8, 8);
w.block(0, 8, 1, 8);
assert!(w.can_walk_between(0, 0, 0, 1));
w.block(0, 0, 0, 1);
assert!(!w.can_walk_between(0, 0, 0, 1));
assert!(w.can_walk_between(0, 7, 0, 8));
w.block(0, 7, 0, 8);
assert!(!w.can_walk_between(0, 7, 0, 8));
assert!(w.can_walk_between(7, 0, 8, 0));
w.block(7, 0, 8, 0);
assert!(!w.can_walk_between(7, 0, 8, 0));
println!(
"{}",
GameState {
p1: PlayerState::P1_START,
p2: PlayerState::P2_START,
walls: w,
}
);
}
}