use bwapi_sys as sys;
use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::Div;
pub trait Point {
fn x(&self) -> isize;
fn y(&self) -> isize;
fn scale() -> isize;
fn length(&self) -> f64 {
let x = self.x().pow(2);
let y = self.y().pow(2);
((x + y) as f64).sqrt()
}
}
macro_rules! create_point_type {
($(#[$attr_struct:meta])* struct $name:ident $(#[$attr_scale:meta])* scale: $scale:expr) => {
$(#[$attr_struct])*
#[derive(Eq, PartialEq, Debug)]
pub struct $name {
x: isize,
y: isize
}
impl $name {
pub fn new(x: isize, y: isize) -> $name {
Self { x, y }
}
pub fn distance<'a>(&'a self, position: &'a Self) -> f64 {
(self - position).length()
}
}
impl Point for $name {
fn x(&self) -> isize { self.x }
fn y(&self) -> isize { self.y }
$(#[$attr_scale])*
fn scale() -> isize { $scale }
}
impl From<sys::$name> for $name {
fn from(sys_pos: sys::$name) -> $name {
Self { x: sys_pos.x as isize, y: sys_pos.y as isize }
}
}
impl From<$name> for sys::$name {
fn from(pos: $name) -> sys::$name {
Self { x: pos.x as i32, y: pos.y as i32 }
}
}
};
}
create_point_type!(
struct Position
scale: 1
);
create_point_type!(
struct WalkPosition
scale: 8
);
create_point_type!(
struct TilePosition
scale: 32
);
macro_rules! create_position_cast {
( $bigger:ty => $smaller:ty ) => {
impl From<$bigger> for $smaller {
fn from(pos: $bigger) -> $smaller {
assert!(<$bigger>::scale() > <$smaller>::scale());
let scale = <$bigger>::scale() / <$smaller>::scale();
Self { x: pos.x() * scale, y: pos.y() * scale }
}
}
impl From<$smaller> for $bigger {
fn from(pos: $smaller) -> $bigger {
assert!(<$bigger>::scale() > <$smaller>::scale());
let scale = <$bigger>::scale() / <$smaller>::scale();
Self { x: pos.x() / scale, y: pos.y() / scale }
}
}
};
}
create_position_cast!(WalkPosition => Position);
create_position_cast!(TilePosition => Position);
create_position_cast!(TilePosition => WalkPosition);
macro_rules! create_point_ops {
( binary: $trait:ident method: $method:ident as $op:tt for $type:ty ) => {
impl $trait<$type> for $type {
type Output = $type;
fn $method(self, other: $type) -> $type {
Self::Output { x: self.x $op other.x, y: self.y $op other.y }
}
}
impl<'a> $trait<$type> for &'a $type {
type Output = $type;
fn $method(self, other: $type) -> $type {
Self::Output { x: self.x $op other.x, y: self.y $op other.y }
}
}
impl<'a> $trait<&'a $type> for $type {
type Output = $type;
fn $method(self, other: &'a $type) -> $type {
Self::Output { x: self.x $op other.x, y: self.y $op other.y }
}
}
impl<'a, 'b> $trait<&'a $type> for &'b $type {
type Output = $type;
fn $method(self, other: &'a $type) -> $type {
Self::Output { x: self.x $op other.x, y: self.y $op other.y }
}
}
};
( scalar: $trait:ident method: $method:ident as $op:tt for $type:ty, $other:ty ) => {
impl $trait<$other> for $type {
type Output = $type;
fn $method(self, other: $other) -> $type {
Self::Output { x: self.x $op other, y: self.y $op other }
}
}
impl<'a> $trait<$other> for &'a $type {
type Output = $type;
fn $method(self, other: $other) -> $type {
Self::Output { x: self.x $op other, y: self.y $op other }
}
}
impl<'a> $trait<&'a $other> for $type {
type Output = $type;
fn $method(self, other: &'a $other) -> $type {
Self::Output { x: self.x $op other, y: self.y $op other }
}
}
impl<'a, 'b> $trait<&'a $other> for &'b $type {
type Output = $type;
fn $method(self, other: &'a $other) -> $type {
Self::Output { x: self.x $op other, y: self.y $op other }
}
}
};
( $name:ty ) => {
create_point_ops!(binary: Add method: add as + for $name );
create_point_ops!(binary: Sub method: sub as - for $name );
create_point_ops!(scalar: Mul method: mul as * for $name, isize);
create_point_ops!(scalar: Div method: div as / for $name, isize);
};
}
create_point_ops!(Position);
create_point_ops!(WalkPosition);
create_point_ops!(TilePosition);
#[cfg(test)]
mod tests {
use bwapi_sys as sys;
use super::*;
#[test]
fn test_position_distance() {
let p1 = Position::new(1, 2);
let p2 = Position::new(1, 2);
assert!(p2.distance(&p1) < 0.001);
}
#[test]
fn position_add() {
let p1 = Position::new(1, 5) + Position::new(2, 4);
assert_eq!(p1, Position::new(3, 9));
let p2 = &p1 + Position::new(1, 2);
assert_eq!(p2, Position::new(4, 11));
let p3 = p2 + &p1;
assert_eq!(p3, Position::new(7, 20));
let p4 = &p3 + &p1;
assert_eq!(p4, Position::new(10, 29));
}
#[test]
fn position_mul() {
let p1 = Position::new(1, 2) * 3;
assert_eq!(p1, Position::new(3, 6));
let p2 = &p1 * 3;
assert_eq!(p2, Position::new(9, 18));
let p3 = p2 * &3;
assert_eq!(p3, Position::new(27, 54));
let p4 = &p3 * &2;
assert_eq!(p4, Position::new(54, 108));
}
#[test]
fn walkposition_from_position() {
let p1 = Position::new(8, 16);
let p2: WalkPosition = From::from(p1);
assert_eq!(p2, WalkPosition::new(1, 2));
let p3 = Position::new(8, 16);
let p4: WalkPosition = p3.into();
assert_eq!(p4, WalkPosition::new(1, 2));
}
#[test]
fn position_from_walkposition() {
let p1 = WalkPosition::new(1, 2);
let p2: Position = From::from(p1);
assert_eq!(p2, Position::new(8, 16));
let p3 = WalkPosition::new(1, 2);
let p4: Position = p3.into();
assert_eq!(p4, Position::new(8, 16));
}
#[test]
fn position_cast_sys() {
let p1 = Position::new(32, 16);
let sys_p1: sys::Position = From::from(p1);
let p2: Position = sys_p1.into();
let p3: WalkPosition = p2.into();
assert_eq!(p3, WalkPosition::new(4, 2));
}
}