#[cfg(feature = "serialization")]
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use std::fmt::{Display, Formatter, Result};
use weasel::rules::ability::SimpleAbility;
use weasel::{
battle_rules, rules::empty::*, Action, ActorRules, BattleRules, BattleState, Entropy,
EventQueue, EventTrigger, MoveEntity, PositionClaim, SpaceRules, WeaselError, WeaselResult,
WriteMetrics,
};
const BATTLEFIELD_LENGTH: usize = 5;
pub const WALK: u32 = 1;
battle_rules! {
EmptyTeamRules,
EmptyCharacterRules,
CustomActorRules,
EmptyFightRules,
EmptyUserRules,
CustomSpaceRules,
EmptyRoundsRules,
EmptyEntropyRules
}
#[derive(Default)]
pub struct CustomSpaceRules {}
impl SpaceRules<CustomRules> for CustomSpaceRules {
type Position = Square;
type SpaceSeed = ();
type SpaceModel = Battlefield;
type SpaceAlteration = ();
fn generate_model(&self, _seed: &Option<Self::SpaceSeed>) -> Self::SpaceModel {
Battlefield::new()
}
fn check_move(
&self,
_model: &Self::SpaceModel,
_claim: PositionClaim<CustomRules>,
position: &Self::Position,
) -> WeaselResult<(), CustomRules> {
if position.valid() {
Ok(())
} else {
Err(WeaselError::UserError(format!(
"invalid position: {}",
position
)))
}
}
fn move_entity(
&self,
model: &mut Self::SpaceModel,
claim: PositionClaim<CustomRules>,
position: Option<&Self::Position>,
_metrics: &mut WriteMetrics<CustomRules>,
) {
if let Some(position) = position {
match claim {
PositionClaim::Spawn(_) => model.insert(*position),
PositionClaim::Movement(entity) => model.change(*entity.position(), *position),
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub struct Square {
pub x: i8,
pub y: i8,
}
impl Square {
fn one_step_towards(self, dir: Direction) -> Self {
match dir {
Direction::Up => Self {
x: self.x,
y: self.y + 1,
},
Direction::Down => Self {
x: self.x,
y: self.y - 1,
},
Direction::Right => Self {
x: self.x + 1,
y: self.y,
},
Direction::Left => Self {
x: self.x - 1,
y: self.y,
},
}
}
fn valid(self) -> bool {
let max = BATTLEFIELD_LENGTH.try_into().unwrap();
let min = 0;
self.x < max && self.x >= min && self.y < max && self.y >= min
}
}
impl Display for Square {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, "x: {}, y: {}", self.x, self.y)
}
}
pub struct Battlefield {
squares: [[bool; BATTLEFIELD_LENGTH]; BATTLEFIELD_LENGTH],
}
impl Battlefield {
fn new() -> Self {
Self {
squares: [[false; BATTLEFIELD_LENGTH]; BATTLEFIELD_LENGTH],
}
}
fn insert(&mut self, square: Square) {
self.squares[square.y as usize][square.x as usize] = true;
}
fn change(&mut self, old: Square, new: Square) {
self.squares[old.y as usize][old.x as usize] = false;
self.insert(new);
}
}
impl Display for Battlefield {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
for (_, row) in self.squares.iter().rev().enumerate() {
for (_, col) in row.iter().enumerate() {
write!(f, "|")?;
if *col {
write!(f, "X")?;
} else {
write!(f, " ")?;
}
}
write!(f, "|")?;
writeln!(f)?;
}
Ok(())
}
}
#[derive(Default)]
pub struct CustomActorRules {}
impl ActorRules<CustomRules> for CustomActorRules {
type Ability = SimpleAbility<u32, ()>;
type AbilitiesSeed = ();
type Activation = Direction;
type AbilitiesAlteration = ();
fn generate_abilities(
&self,
_seed: &Option<Self::AbilitiesSeed>,
_entropy: &mut Entropy<CustomRules>,
_metrics: &mut WriteMetrics<CustomRules>,
) -> Box<dyn Iterator<Item = Self::Ability>> {
let v = vec![SimpleAbility::new(WALK, ())];
Box::new(v.into_iter())
}
fn activable(
&self,
_state: &BattleState<CustomRules>,
action: Action<CustomRules>,
) -> WeaselResult<(), CustomRules> {
if let Some(dir) = action.activation {
let destination = action.actor.position().one_step_towards(*dir);
if destination.valid() {
Ok(())
} else {
Err(WeaselError::UserError(format!(
"invalid destination: {}",
destination
)))
}
} else {
Err(WeaselError::UserError("missing activation!".to_string()))
}
}
fn activate(
&self,
_state: &BattleState<CustomRules>,
action: Action<CustomRules>,
event_queue: &mut Option<EventQueue<CustomRules>>,
_entropy: &mut Entropy<CustomRules>,
_metrics: &mut WriteMetrics<CustomRules>,
) {
let entity_id = *action.actor.entity_id();
let direction = action.activation.unwrap();
let position = action.actor.position().one_step_towards(direction);
MoveEntity::trigger(event_queue, entity_id, position).fire();
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
pub enum Direction {
Up,
Down,
Right,
Left,
}