wasmdome_domain/
lib.rs

1use std::collections::HashSet;
2
3#[macro_use]
4extern crate serde_derive;
5
6#[macro_use]
7extern crate eventsourcing_derive;
8pub extern crate eventsourcing;
9
10pub use radar::RadarPing;
11
12pub mod commands;
13pub mod events;
14pub mod leaderboard;
15mod radar;
16pub mod state;
17
18pub(crate) const DOMAIN_VERSION: &str = "1.0";
19
20const DEFAULT_BOARD_HEIGHT: u32 = 100;
21const DEFAULT_BOARD_WIDTH: u32 = 100;
22
23/// The primary accumulator register
24pub const EAX: u32 = 0;
25/// The count register
26pub const ECX: u32 = 1;
27/// The base register
28pub const EBX: u32 = 2;
29
30/// A 2-dimensional coordinate within an arena
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
32pub struct Point {
33    pub x: i32,
34    pub y: i32,
35}
36
37impl Point {
38    pub fn new(x: i32, y: i32) -> Point {
39        Point { x, y }
40    }
41
42    pub fn bearing(&self, target: &Point) -> GridDirection {
43        let dx = (target.x - self.x) as f64;
44        let dy = (target.y - self.y) as f64;
45        let mut angle = 90.0 - dy.atan2(dx).to_degrees();
46
47        if angle < 0.0 {
48            angle = angle + 360.0;
49        }
50
51        println!("Angle: {}", angle);
52        let idx = angle.trunc() as usize / 45;
53        ALL_DIRECTIONS[idx]
54    }
55
56    pub fn adjacent_points(&self, board: &GameBoard) -> Vec<Point> {
57        let mut points = Vec::new();
58        for direction in &ALL_DIRECTIONS {
59            if let Some(target) = self.relative_point(board, direction, 1) {
60                points.push(target)
61            }
62        }
63        points
64    }
65
66    pub fn gather_points(
67        &self,
68        board: &GameBoard,
69        direction: &GridDirection,
70        count: usize,
71    ) -> Vec<(Point, usize)> {
72        let mut points = Vec::new();
73        let mut p = Self::relative_point(&self, board, direction, 1);
74        for i in 0..count {
75            match p {
76                Some(point) => {
77                    points.push((point.clone(), i + 1));
78                    p = Self::relative_point(&point, board, direction, 1);
79                }
80                None => break,
81            }
82        }
83        points
84    }
85
86    /// Returns a point 1 unit away in the direction indicated. Grid origin is the most Southwest point
87    pub fn relative_point(
88        &self,
89        board: &GameBoard,
90        direction: &GridDirection,
91        length: i32,
92    ) -> Option<Point> {
93        let destination = match direction {
94            GridDirection::North => Point {
95                x: self.x,
96                y: self.y + length,
97            },
98            GridDirection::NorthEast => Point {
99                x: self.x + length,
100                y: self.y + length,
101            },
102            GridDirection::East => Point {
103                x: self.x + length,
104                y: self.y,
105            },
106            GridDirection::SouthEast => Point {
107                x: self.x + length,
108                y: self.y - length,
109            },
110            GridDirection::South => Point {
111                x: self.x,
112                y: self.y - length,
113            },
114            GridDirection::SouthWest => Point {
115                x: self.x - length,
116                y: self.y - length,
117            },
118            GridDirection::West => Point {
119                x: self.x - length,
120                y: self.y,
121            },
122            GridDirection::NorthWest => Point {
123                x: self.x - length,
124                y: self.y + length,
125            },
126        };
127        if !destination.is_on_board(board) {
128            None
129        } else {
130            Some(destination)
131        }
132    }
133
134    pub fn is_on_board(&self, board: &GameBoard) -> bool {
135        self.x <= board.width as _ && self.y <= board.height as _ && self.x >= 0 && self.y >= 0
136    }
137}
138
139/// Represents the dimensions and other metadata for a game board. All game boards
140/// have an origin of (0,0) that starts in the bottom left (southwest) corner of
141/// the scene.
142#[derive(Debug, Clone, Serialize, Deserialize, Copy)]
143pub struct GameBoard {
144    pub width: u32,
145    pub height: u32,
146}
147
148impl Default for GameBoard {
149    fn default() -> Self {
150        GameBoard {
151            width: DEFAULT_BOARD_WIDTH,
152            height: DEFAULT_BOARD_HEIGHT,
153        }
154    }
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub enum WeaponType {
159    Primary = 0,
160    Secondary = 1,
161}
162
163/// Represents a value contained within a register. Note that not all registers
164/// support all value types
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub enum RegisterValue {
167    Number(u64),
168    Text(String),
169}
170
171/// An operation performed on a register
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub enum RegisterOperation {
174    Accumulate(u64),
175    Decrement(u64),
176    Set(RegisterValue),
177}
178
179/// Represents all possible directions at the game engine resolution
180#[derive(Debug, Clone, Serialize, Deserialize, Copy, PartialEq)]
181pub enum GridDirection {
182    North = 0,
183    NorthEast = 1,
184    East = 2,
185    SouthEast = 3,
186    South = 4,
187    SouthWest = 5,
188    West = 6,
189    NorthWest = 7,
190}
191
192static ALL_DIRECTIONS: [GridDirection; 8] = [
193    GridDirection::North,
194    GridDirection::NorthEast,
195    GridDirection::East,
196    GridDirection::SouthEast,
197    GridDirection::South,
198    GridDirection::SouthWest,
199    GridDirection::West,
200    GridDirection::NorthWest,
201];
202
203/// The set of initial parameters for a match
204#[derive(Debug, Clone, Serialize, Deserialize, Default)]
205pub struct MatchParameters {
206    pub match_id: String,
207    pub width: u32,
208    pub height: u32,
209    pub actors: Vec<String>,
210    pub max_turns: u32,
211    pub aps_per_turn: u32,
212}
213
214impl MatchParameters {
215    pub fn new(
216        match_id: String,
217        width: u32,
218        height: u32,
219        max_turns: u32,
220        aps_per_turn: u32,
221        actors: Vec<String>,
222    ) -> Self {
223        MatchParameters {
224            match_id,
225            width,
226            height,
227            actors,
228            max_turns,
229            aps_per_turn,
230        }
231    }
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize, Default)]
235pub struct TurnStatus {
236    pub current: u32,
237    pub taken: HashSet<String>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub enum DamageSource {
242    Wall,
243    MechWeapon(String),
244    MechCollision(String),
245}
246
247#[cfg(test)]
248mod test {
249    use super::*;
250
251    #[test]
252    fn gather_points() {
253        let board = GameBoard::default();
254        let origin = Point::new(6, 6);
255
256        let points = origin
257            .gather_points(&board, &GridDirection::NorthEast, 6)
258            .into_iter()
259            .map(|(p, _d)| p)
260            .collect::<Vec<_>>();
261        assert_eq!(
262            points,
263            vec![
264                Point::new(7, 7),
265                Point::new(8, 8),
266                Point::new(9, 9),
267                Point::new(10, 10),
268                Point::new(11, 11),
269                Point::new(12, 12)
270            ]
271        );
272
273        let truncated_points = origin
274            .gather_points(&board, &GridDirection::SouthWest, 20)
275            .into_iter()
276            .map(|(p, _d)| p)
277            .collect::<Vec<_>>();
278        assert_eq!(
279            truncated_points,
280            vec![
281                Point::new(5, 5),
282                Point::new(4, 4),
283                Point::new(3, 3),
284                Point::new(2, 2),
285                Point::new(1, 1),
286                Point::new(0, 0),
287            ]
288        );
289    }
290
291    #[test]
292    fn adjacent_points() {
293        let board = GameBoard::default();
294        let origin = Point::new(10, 5);
295        let points = origin.adjacent_points(&board);
296        assert_eq!(
297            points,
298            vec![
299                Point::new(10, 6), // North
300                Point::new(11, 6),
301                Point::new(11, 5), // East
302                Point::new(11, 4),
303                Point::new(10, 4), // South
304                Point::new(9, 4),
305                Point::new(9, 5), // West
306                Point::new(9, 6),
307            ]
308        )
309    }
310
311    #[test]
312    fn compute_bearing() {
313        // this should be a 45 degree bearing, or NorthEast
314        let me = Point::new(0, 0);
315        let them = Point::new(5, 5);
316        assert_eq!(me.bearing(&them), GridDirection::NorthEast);
317
318        assert_eq!(
319            Point::new(0, 0).bearing(&Point::new(5, 0)),
320            GridDirection::East
321        );
322        assert_eq!(
323            Point::new(0, 0).bearing(&Point::new(0, -5)),
324            GridDirection::South
325        );
326        assert_eq!(
327            Point::new(1, 1).bearing(&Point::new(-1, -1)),
328            GridDirection::SouthWest
329        );
330        assert_eq!(
331            Point::new(5, 10).bearing(&Point::new(1, 6)),
332            GridDirection::SouthWest
333        );
334        assert_eq!(
335            Point::new(0, 0).bearing(&Point::new(0, 5)),
336            GridDirection::North
337        );
338        assert_eq!(
339            Point::new(6, 8).bearing(&Point::new(10, 4)),
340            GridDirection::SouthEast
341        );
342        assert_eq!(
343            Point::new(9, 11).bearing(&Point::new(4, 11)),
344            GridDirection::West
345        );
346    }
347}