gamestate
Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
This commit is contained in:
commit
672f9e09bf
5 changed files with 230 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
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 = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quoridor"
|
||||||
|
version = "0.1.0"
|
||||||
6
Cargo.toml
Normal file
6
Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[package]
|
||||||
|
name = "quoridor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
211
src/gamestate.rs
Normal file
211
src/gamestate.rs
Normal 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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/main.rs
Normal file
5
src/main.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
mod gamestate;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue