diff options
author | Justin Worthe <justin@worthe-it.co.za> | 2019-04-22 14:38:40 +0200 |
---|---|---|
committer | Justin Worthe <justin@worthe-it.co.za> | 2019-04-22 14:38:40 +0200 |
commit | 3e54b01003aa9d27de8f4ca13c9240fe785ec0e1 (patch) | |
tree | 0dfe803ba790951e29cba25f791e31418eff5b05 | |
parent | 29a323e0a3bd3ab3e6109b23e15bb5f9e88398e3 (diff) |
Structures representing game state
-rw-r--r-- | Cargo.lock | 7 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/game.rs | 46 | ||||
-rw-r--r-- | src/geometry.rs | 6 | ||||
-rw-r--r-- | src/geometry/point.rs | 84 | ||||
-rw-r--r-- | src/geometry/rect.rs | 19 | ||||
-rw-r--r-- | src/geometry/vec.rs | 154 | ||||
-rw-r--r-- | src/main.rs | 2 |
8 files changed, 319 insertions, 0 deletions
@@ -32,6 +32,11 @@ version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "proc-macro2" version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -188,6 +193,7 @@ dependencies = [ name = "steam-powered-wyrm" version = "0.1.0" dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -234,6 +240,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum itoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" @@ -7,3 +7,4 @@ edition = "2018" serde = { version = "1.0.90", features = ["derive"] } serde_json = "1.0.39" rand = "0.6.5" +num-traits = "0.2.6" diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..492eaf3 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,46 @@ +use crate::geometry::*; + +struct GameBoard { + player: Player, + opponent: Player, + powerups: Vec<Powerup>, + map: Map, +} + +struct Player { + active_worm: usize, + worms: Vec<Worm> +} + +struct Worm { + id: i32, + health: i32, + position: Point2d<u8>, + digging_range: u32, + movement_range: u32, + // This is unnecessary for now, but necessary later. I know + // for sure for the first round that all the worms will do the + // same damage and there isn't any way to change it. + weapon: Option<Weapon>, +} + +struct Weapon { + damage: u32, + range: u32, +} + +enum Powerup { + Health(Point2d<u8>, i32) +} + +struct Map { + size: u8, + /// This is 2d, each row is size long + cells: Vec<CellType> +} + +enum CellType { + Air, + Dirt, + DeepSpace, +} diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 0000000..9e541ef --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,6 @@ +mod vec; +pub use self::vec::*; +mod point; +pub use self::point::*; +mod rect; +pub use self::rect::*; diff --git a/src/geometry/point.rs b/src/geometry/point.rs new file mode 100644 index 0000000..c34d00f --- /dev/null +++ b/src/geometry/point.rs @@ -0,0 +1,84 @@ +use crate::geometry::vec::*; + +use std::ops::*; +use num_traits::{NumOps, NumAssignOps}; +use num_traits::pow::Pow; +use num_traits::real::Real; + +macro_rules! impl_point { + ($PointN:ident { $($field:ident),+ }, $VecN:ident) => { + #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] + pub struct $PointN<T> { + $(pub $field: T),+ + } + + impl<T> $PointN<T> { + pub fn new($($field: T),+) -> $PointN<T> { + $PointN { $($field),+ } + } + } + + impl<T: NumOps + Pow<u8, Output=T> + Copy> $PointN<T> { + pub fn distance_squared(self, other: $PointN<T>) -> T { + (self - other).magnitude_squared() + } + } + + + impl<T: Real + Pow<u8, Output=T>> $PointN<T> { + pub fn distance(self, other: $PointN<T>) -> T { + (self - other).magnitude() + } + } + + impl<T: NumOps> Add<$VecN<T>> for $PointN<T> { + type Output = Self; + + fn add(self, other: $VecN<T>) -> Self { + $PointN { + $($field: self.$field + other.$field),+ + } + } + } + + impl<T: NumAssignOps> AddAssign<$VecN<T>> for $PointN<T> { + fn add_assign(&mut self, other: $VecN<T>) { + $(self.$field += other.$field);+ + } + } + + impl<T: NumOps> Sub for $PointN<T> { + type Output = $VecN<T>; + + fn sub(self, other: Self) -> $VecN<T> { + $VecN { + $($field: self.$field - other.$field),+ + } + } + } + } +} + +impl_point!(Point2d { x, y }, Vec2d); +impl_point!(Point3d { x, y, z }, Vec3d); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn distance_in_x_dimension_example() { + let a = Point2d::new(1., 1.); + let b = Point2d::new(3., 1.); + assert_eq!(a.distance(b), 2.); + assert_eq!(a.distance_squared(b), 4.); + } + + #[test] + fn distance_in_y_dimension_example() { + let a = Point2d::new(1., 1.); + let b = Point2d::new(1., 3.); + assert_eq!(b.distance(a), 2.); + assert_eq!(b.distance_squared(a), 4.); + } +} diff --git a/src/geometry/rect.rs b/src/geometry/rect.rs new file mode 100644 index 0000000..e2b1882 --- /dev/null +++ b/src/geometry/rect.rs @@ -0,0 +1,19 @@ +use crate::geometry::Point2d; +use crate::geometry::Vec2d; + +#[derive(Debug, Default, Clone, Copy, PartialEq)] +pub struct Rectangle<T> { + pub pos: Point2d<T>, + pub size: Vec2d<T>, +} + +impl<T> Rectangle<T> { + pub fn new(x: T, y: T, w: T, h: T) -> Rectangle<T> { + Rectangle { + pos: Point2d::new(x, y), + size: Vec2d::new(w, h), + } + } + + // TODO: Constructor to build a rectangle centered at an x,y +} diff --git a/src/geometry/vec.rs b/src/geometry/vec.rs new file mode 100644 index 0000000..f6bd29f --- /dev/null +++ b/src/geometry/vec.rs @@ -0,0 +1,154 @@ +use std::ops::*; +use num_traits::{NumOps, NumAssignOps}; +use num_traits::pow::Pow; +use num_traits::real::Real; + +macro_rules! fold_array { + ($method:ident, { $x:expr }) => { $x }; + ($method:ident, { $x:expr, $y:expr }) => { $x.$method($y) }; + ($method:ident, { $x:expr, $y:expr, $z:expr }) => { $x.$method($y).$method($z) }; +} + +macro_rules! impl_vector { + ($VecN:ident { $($field:ident),+ }) => { + #[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)] + pub struct $VecN<T> { + $(pub $field: T),+ + } + + impl<T> $VecN<T> { + pub fn new($($field: T),+) -> $VecN<T> { + $VecN { $($field),+ } + } + } + + impl<T: NumOps + Pow<u8, Output=T> + Copy> $VecN<T> { + pub fn magnitude_squared(&self) -> T { + fold_array!(add, { $(self.$field.pow(2)),+ }) + } + } + + impl<T: Real + Pow<u8, Output=T>> $VecN<T> { + pub fn magnitude(&self) -> T { + self.magnitude_squared().sqrt() + } + + pub fn unit(&self) -> $VecN<T> { + let mag = self.magnitude(); + $VecN { + $($field: self.$field / mag),+ + } + } + } + + impl<T: NumOps + Copy> $VecN<T> { + pub fn dot(&self, other: $VecN<T>) -> T { + fold_array!(add, { $(self.$field * other.$field),+ }) + } + } + + impl<T: NumOps> Add for $VecN<T> { + type Output = Self; + + fn add(self, other: Self) -> Self { + $VecN { + $($field: self.$field + other.$field),+ + } + } + } + + impl<T: NumAssignOps> AddAssign for $VecN<T> { + fn add_assign(&mut self, other: Self) { + $(self.$field += other.$field);+ + } + } + + impl<T: NumOps> Sub for $VecN<T> { + type Output = Self; + + fn sub(self, other: Self) -> Self { + $VecN { + $($field: self.$field - other.$field),+ + } + } + } + + impl<T: NumOps + Copy> Mul<T> for $VecN<T> { + type Output = Self; + + fn mul(self, rhs: T) -> Self { + $VecN { + $($field: self.$field * rhs),+ + } + } + } + + impl<T: Neg<Output=T>> Neg for $VecN<T> { + type Output = Self; + + fn neg(self) -> Self { + $VecN { + $($field: -self.$field),+ + } + } + } + + } +} + +impl_vector!(Vec2d { x, y }); +impl_vector!(Vec3d { x, y, z }); + +impl<T: Real + Pow<u8, Output=T>> Vec2d<T> { + pub fn angle(&self) -> T { + self.y.atan2(self.x) + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn right_angles_in_2d_vectors() { + use std::f32::consts::{FRAC_PI_2, PI}; + + assert_eq!(0., Vec2d::new(1., 0.).angle()); + assert_eq!(FRAC_PI_2, Vec2d::new(0., 1.).angle()); + assert_eq!(PI, Vec2d::new(-1., 0.).angle()); + assert_eq!(-FRAC_PI_2, Vec2d::new(0., -1.).angle()); + } + + #[test] + fn unit_normalizes_2d_vector() { + let before = Vec2d::new(2., 10.); + let after = before.unit(); + + assert_eq!(1., after.magnitude()); + assert_eq!(before.angle(), after.angle()); + } + + #[test] + fn unit_normalizes_3d_vector() { + let before = Vec3d::new(2., 10., -5.); + let after = before.unit(); + + assert_eq!(1., after.magnitude()); + // How to define 3d angle? 2 angles I suppose? + // assert_eq!(before.angle(), after.angle()); + } + + #[test] + fn dot_product_example_2d() { + let a = Vec2d::new(-6., 8.); + let b = Vec2d::new(5., 12.); + assert_eq!(66., a.dot(b)); + } + + #[test] + fn magnitude_squared_of_an_integer() { + let a = Vec2d::new(3, 4); + assert_eq!(25, a.magnitude_squared()); + } +} diff --git a/src/main.rs b/src/main.rs index cc39b63..cba96f5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ use rand::prelude::*; mod command; mod json; +mod geometry; +mod game; use command::*; use json::*; |