use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct Point2 {
pub x: f32,
pub y: f32,
}
impl Point2 {
#[must_use]
pub const fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Landscape2DSnapshot {
pub bounds_x: (f32, f32),
pub bounds_y: (f32, f32),
pub current: Point2,
pub best: Option<Point2>,
pub trail: Vec<Point2>,
pub label: String,
}
pub trait Landscape2DPayloadSource {
fn landscape2d_snapshot(&self) -> Landscape2DSnapshot;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum BodyKind {
Hull,
Wheel,
Leg,
Wing,
Ground,
Goal,
Other,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RigidBody2D {
pub vertices: Vec<Point2>,
pub position: Point2,
pub rotation_rad: f32,
pub kind: BodyKind,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Box2dSnapshot {
pub world_bounds: (Point2, Point2),
pub bodies: Vec<RigidBody2D>,
pub contacts: Vec<Point2>,
}
pub trait Box2dPayloadSource {
fn box2d_snapshot(&self) -> Box2dSnapshot;
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Locomotion2DSnapshot {
pub joints: Vec<Point2>,
pub bones: Vec<(u32, u32)>,
pub ground_y: f32,
pub com: Option<Point2>,
pub contacts: Vec<Point2>,
}
pub trait Locomotion2DPayloadSource {
fn locomotion2d_snapshot(&self) -> Locomotion2DSnapshot;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GridDir {
East,
South,
West,
North,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GridColor {
Red,
Green,
Blue,
Purple,
Yellow,
Grey,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum GridDoorState {
Open,
Closed,
Locked,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GridTile {
Empty,
Floor,
Wall,
Goal,
Lava,
Door(GridColor, GridDoorState),
Key(GridColor),
Ball(GridColor),
Box(GridColor),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct GridAgentMarker {
pub x: u16,
pub y: u16,
pub dir: GridDir,
pub carrying: Option<GridTile>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GridSnapshot {
pub width: u16,
pub height: u16,
pub tiles: Vec<GridTile>,
pub agent: GridAgentMarker,
}
pub trait GridPayloadSource {
fn grid_snapshot(&self) -> GridSnapshot;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TabularCell {
Empty,
Frozen,
Start,
Goal,
Hazard,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TabularMarkerKind {
Agent,
Passenger,
Destination,
Location,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TabularMarker {
pub x: u16,
pub y: u16,
pub kind: TabularMarkerKind,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TabularGrid {
pub width: u16,
pub height: u16,
pub cells: Vec<TabularCell>,
pub markers: Vec<TabularMarker>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CardTable {
pub player_cards: Vec<u8>,
pub player_total: u8,
pub usable_ace: bool,
pub dealer_cards: Vec<u8>,
pub dealer_showing: u8,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum TabularLayout {
Grid(TabularGrid),
Cards(CardTable),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TabularSnapshot {
pub layout: TabularLayout,
}
pub trait TabularPayloadSource {
fn tabular_snapshot(&self) -> TabularSnapshot;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Classic2DRole {
Track,
Cart,
Pole,
Link,
Car,
Hinge,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Classic2DBody {
pub points: Vec<Point2>,
pub role: Classic2DRole,
pub closed: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Classic2DSnapshot {
pub bodies: Vec<Classic2DBody>,
pub bounds: (Point2, Point2),
}
pub trait Classic2DPayloadSource {
fn classic2d_snapshot(&self) -> Classic2DSnapshot;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn point2_const_constructor() {
const P: Point2 = Point2::new(1.5, -2.5);
assert!((P.x - 1.5).abs() < f32::EPSILON);
assert!((P.y + 2.5).abs() < f32::EPSILON);
}
#[test]
fn landscape_snapshot_constructs_and_compares() {
let snap = Landscape2DSnapshot {
bounds_x: (-5.0, 5.0),
bounds_y: (-5.0, 5.0),
current: Point2::new(0.5, -0.25),
best: Some(Point2::new(0.0, 0.0)),
trail: vec![Point2::new(0.1, 0.2), Point2::new(0.3, 0.4)],
label: "sphere".into(),
};
assert_eq!(snap.trail.len(), 2);
assert_eq!(snap.label, "sphere");
assert_eq!(snap.clone(), snap);
}
#[test]
fn box2d_snapshot_carries_typed_body_kinds() {
let snap = Box2dSnapshot {
world_bounds: (Point2::new(-10.0, -1.0), Point2::new(10.0, 8.0)),
bodies: vec![
RigidBody2D {
vertices: vec![
Point2::new(-0.5, -0.5),
Point2::new(0.5, -0.5),
Point2::new(0.5, 0.5),
Point2::new(-0.5, 0.5),
],
position: Point2::new(1.0, 2.0),
rotation_rad: 0.25,
kind: BodyKind::Hull,
},
RigidBody2D {
vertices: vec![Point2::new(0.0, 0.0)],
position: Point2::new(0.0, 0.0),
rotation_rad: 0.0,
kind: BodyKind::Ground,
},
],
contacts: vec![Point2::new(0.0, 0.0)],
};
assert_eq!(snap.bodies.len(), 2);
assert_eq!(snap.bodies[0].kind, BodyKind::Hull);
assert_eq!(snap.bodies[1].kind, BodyKind::Ground);
}
#[test]
fn locomotion_snapshot_default_ground_and_optional_com() {
let snap = Locomotion2DSnapshot {
joints: vec![Point2::new(0.0, 1.0), Point2::new(0.5, 1.5)],
bones: vec![(0, 1)],
ground_y: 0.0,
com: None,
contacts: vec![],
};
assert_eq!(snap.bones, vec![(0u32, 1u32)]);
assert!(snap.com.is_none());
}
struct Stub;
impl Landscape2DPayloadSource for Stub {
fn landscape2d_snapshot(&self) -> Landscape2DSnapshot {
Landscape2DSnapshot {
bounds_x: (0.0, 1.0),
bounds_y: (0.0, 1.0),
current: Point2::default(),
best: None,
trail: vec![],
label: "stub".into(),
}
}
}
impl Box2dPayloadSource for Stub {
fn box2d_snapshot(&self) -> Box2dSnapshot {
Box2dSnapshot {
world_bounds: (Point2::default(), Point2::new(1.0, 1.0)),
bodies: vec![],
contacts: vec![],
}
}
}
impl Locomotion2DPayloadSource for Stub {
fn locomotion2d_snapshot(&self) -> Locomotion2DSnapshot {
Locomotion2DSnapshot {
joints: vec![],
bones: vec![],
ground_y: 0.0,
com: None,
contacts: vec![],
}
}
}
#[test]
fn payload_source_traits_compose_via_stub() {
let stub = Stub;
assert_eq!(stub.landscape2d_snapshot().label, "stub");
assert_eq!(stub.box2d_snapshot().bodies.len(), 0);
assert_eq!(stub.locomotion2d_snapshot().joints.len(), 0);
}
}