You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
martian-chess/src/game.rs

207 lines
5.3 KiB
Rust

use crate::prelude::*;
pub(crate) struct GamePlugin;
impl Plugin for GamePlugin {
fn build(&self, app: &mut App) {
app.add_event::<GameEvent>()
.init_resource::<ActiveTile>()
.add_systems(Startup, setup_board)
.add_systems(
PostUpdate,
(
debug_hovering
.run_if(resource_exists::<debug::DebugEnabled>())
.run_if(resource_changed::<ActiveTile>()),
debug_board.run_if(resource_exists::<debug::DebugEnabled>()),
),
);
}
}
#[derive(Debug, Component, Clone)]
pub(crate) enum Piece {
Pawn,
Drone,
Queen,
}
// manually for the type.
impl std::fmt::Display for Piece {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Piece::Queen => write!(f, "@"),
Piece::Drone => write!(f, "^"),
Piece::Pawn => write!(f, "*"),
}
}
}
#[derive(Debug)]
pub(crate) enum GameError {
NullMove,
InvalidMove,
}
#[derive(Debug, Event)]
pub(crate) enum GameEvent {
SelectPiece,
PlacePiece,
}
/// The board is setup like this:
/// ```text
/// 0 1 2 3 4 5 6 7
/// +--+--+--+--+--+--+--+--+
/// a | | | | I | d| Q| Q|
/// +--+--+--+--+--+--+--+--+
/// b |d |p |p | I | p| d| Q|
/// +--+--+--+--+--+--+--+--+
/// c |Q |d |p | I | p| p| d|
/// +--+--+--+--+--+--+--+--+
/// d |Q |Q |d | I | | | |
/// +--+--+--+--+--+--+--+--+
/// ````
#[derive(Debug, Resource)]
pub(crate) struct Board {
inner: Vec<Vec<Option<Piece>>>,
}
#[derive(Debug, Component, PartialEq, Clone)]
pub(crate) struct BoardIndex {
pub x: usize,
pub y: usize,
}
impl Board {
/// Returns the piece at the given location
pub(crate) fn at(&self, BoardIndex { x, y }: &BoardIndex) -> Option<Piece> {
self.inner[*y][*x].clone()
}
/// Returns a list of all pieces on the board with their location
pub(crate) fn pieces(&self) -> Vec<(BoardIndex, Piece)> {
self.inner
.iter()
.enumerate()
.flat_map(|(y, nested)| {
nested.iter().enumerate().filter_map(move |(x, p)| {
p.as_ref().map(|val| (BoardIndex { x, y }, val.clone()))
})
})
.collect()
}
pub(crate) fn move_piece(
&mut self,
from: &BoardIndex,
to: &BoardIndex,
) -> Result<(), GameError> {
if from == to {
Err(GameError::NullMove)
} else if self.at(to).is_none() {
self.at(from).map_or(Err(GameError::NullMove), |from_val| {
// TODO: We can self.inner.swap(to, from) if board is single vec
self.inner[to.y][to.x] = Some(from_val);
self.inner[from.y][from.x] = None;
Ok(())
})
} else {
Err(GameError::InvalidMove)
}
}
}
impl std::fmt::Display for Board {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.inner.iter().rev().for_each(|row| {
let _ = write!(f, "+--+--+--+--+--+--+--+--+\n");
let _ = write!(f, "|");
row.iter().for_each(|piece| {
let _ = match piece {
Some(p) => write!(f, "{} |", p),
None => write!(f, " |"),
};
});
let _ = write!(f, "\n");
});
let _ = write!(f, "+--+--+--+--+--+--+--+--+");
Ok(())
}
}
#[derive(Debug, Default, Resource)]
pub(crate) struct ActiveTile {
pub idx: Option<BoardIndex>,
}
#[derive(Debug, Default, Component)]
pub(crate) struct Selected;
fn setup_board(mut commands: Commands) {
use Piece::*;
commands.insert_resource(Board {
inner: vec![
vec![
Some(Queen),
Some(Queen),
Some(Drone),
None,
None,
None,
None,
None,
],
vec![
Some(Queen),
Some(Drone),
Some(Pawn),
None,
None,
Some(Pawn),
Some(Pawn),
Some(Drone),
],
vec![
Some(Drone),
Some(Pawn),
Some(Pawn),
None,
None,
Some(Pawn),
Some(Drone),
Some(Queen),
],
vec![
None,
None,
None,
None,
None,
Some(Drone),
Some(Queen),
Some(Queen),
],
],
});
}
/// TODO: only run_if debug enabled
fn debug_hovering(
selected: Res<ActiveTile>,
board: Res<Board>,
mut debug_info: ResMut<debug::DebugInfo>,
) {
match &selected.idx {
Some(idx) => debug_info.set(
"hovering".into(),
format!("{:?}@({},{})", board.at(&idx), idx.x, idx.y,),
),
None => debug_info.set("selected".into(), format!("N/A")),
};
}
fn debug_board(board: Res<Board>, mut debug_info: ResMut<debug::DebugInfo>) {
debug_info.set("board".into(), format!("\n{}", *board));
}