use crate::{Stone, StoneId};
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub struct GameDelta {
pub new_stone: Option<Stone>,
pub captured_stone_ids: Vec<StoneId>,
}
impl GameDelta {
#[must_use]
pub const fn placement(stone: Stone) -> Self {
Self {
new_stone: Some(stone),
captured_stone_ids: Vec::new(),
}
}
#[must_use]
pub fn capture(stone: Stone, captured_stone_ids: Vec<StoneId>) -> Self {
Self {
new_stone: Some(stone),
captured_stone_ids,
}
}
#[must_use]
pub const fn pass() -> Self {
Self {
new_stone: None,
captured_stone_ids: Vec::new(),
}
}
#[must_use]
pub const fn is_pass(&self) -> bool {
self.new_stone.is_none()
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::GameDelta;
use crate::{Color, Point, Stone, StoneId};
fn stone() -> Stone {
Stone::new(StoneId::new(4), Color::White, Point::new(1.5, -2.0))
}
#[test]
fn a_pass_places_nothing_and_captures_nothing() {
let delta = GameDelta::pass();
assert!(delta.is_pass());
assert!(delta.captured_stone_ids.is_empty());
assert_eq!(delta, GameDelta::default());
}
#[test]
fn a_placement_is_not_a_pass() {
let delta = GameDelta::placement(stone());
assert!(!delta.is_pass());
assert_eq!(delta.new_stone, Some(stone()));
}
#[test]
fn a_capture_carries_its_victims() {
let delta = GameDelta::capture(stone(), vec![StoneId::new(1), StoneId::new(2)]);
assert_eq!(delta.captured_stone_ids, [StoneId::new(1), StoneId::new(2)]);
}
}