use bwapi_wrapper::prelude::{Error, ScaledPosition, TilePosition, WalkPosition};
use derive_more::{Add, AddAssign, Sub, SubAssign};
use num_derive::FromPrimitive;
use std::ffi::CStr;
use std::os::raw::c_char;
pub type BwResult<T> = Result<T, Error>;
#[derive(Debug, Copy, Clone, FromPrimitive)]
pub enum Color {
Red = 111,
Blue = 165,
Teal = 159,
Purple = 164,
Orange = 179,
Brown = 19,
White = 255,
Yellow = 135,
Green = 117,
Cyan = 128,
Black = 0,
Grey = 74,
}
#[derive(Debug)]
pub enum TextSize {
Small,
Default,
Large,
Huge,
}
pub(crate) fn c_str_to_str(i: &[c_char]) -> String {
unsafe {
let i = &*(i as *const [c_char] as *const [u8]);
CStr::from_bytes_with_nul_unchecked(&i[..=i.iter().position(|&c| c == 0).unwrap()])
.to_string_lossy()
.to_string()
}
}
#[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Add, Sub, AddAssign, SubAssign)]
pub struct Rectangle<P> {
pub tl: P,
pub br: P,
}
impl<const N: i32> Rectangle<ScaledPosition<N>> {
pub fn new<P1: Into<ScaledPosition<N>>, P2: Into<ScaledPosition<N>>>(
corner_a: P1,
corner_b: P2,
) -> Self {
let (mut a, mut b) = (corner_a.into(), corner_b.into());
if a.x > b.x {
core::mem::swap(&mut a.x, &mut b.x);
}
if a.y > b.y {
core::mem::swap(&mut a.y, &mut b.y);
}
Rectangle { tl: a, br: b }
}
pub fn border(&self) -> Vec<ScaledPosition<N>> {
(self.tl.x..=self.br.x)
.map(|x| ScaledPosition::<N>::new(x, self.tl.y))
.chain((self.tl.y + 1..self.br.y).map(|y| ScaledPosition::<N>::new(self.br.x, y)))
.chain(
(self.tl.x..=self.br.x)
.rev()
.map(|x| ScaledPosition::<N>::new(x, self.br.y)),
)
.chain(
(self.tl.y + 1..self.br.y)
.rev()
.map(|y| ScaledPosition::<N>::new(self.tl.x, y)),
)
.collect()
}
pub fn extrude(self, amount: i32) -> Self {
Self::new(self.tl - (amount, amount), self.br + (amount, amount))
}
pub fn shrink(self, amount: i32) -> Self {
self.extrude(-amount)
}
pub fn envelops<const M: i32>(&self, other: Rectangle<ScaledPosition<M>>) -> bool {
self.tl.x * N <= other.tl.x * M
&& self.tl.y * N <= other.tl.y * M
&& self.br.x * N >= other.br.x * M
&& self.br.y * N >= other.br.y * M
}
pub fn intersects<const M: i32>(&self, other: Rectangle<ScaledPosition<M>>) -> bool {
self.tl.x * N <= other.br.x * M
&& self.tl.y * N <= other.br.y * M
&& self.br.x * N >= other.tl.x * M
&& self.br.y * N >= other.tl.y * M
}
pub fn contains<const M: i32>(&self, pos: ScaledPosition<M>) -> bool {
self.tl.x * N <= pos.x * M
&& self.tl.y * N <= pos.y * M
&& self.br.x * N >= pos.x * M
&& self.br.y * N >= pos.y * M
}
pub fn resize_to_contain(mut self, pos: ScaledPosition<N>) -> Self {
self.tl.x = self.tl.x.min(pos.x);
self.br.x = self.br.x.min(pos.x);
self.tl.y = self.tl.y.min(pos.y);
self.br.y = self.br.y.min(pos.y);
self
}
pub fn width(&self) -> i32 {
self.br.x - self.tl.x + 1
}
pub fn height(&self) -> i32 {
self.br.y - self.tl.y + 1
}
}
impl Rectangle<TilePosition> {
pub fn to_walk_rect(self) -> Rectangle<WalkPosition> {
Rectangle {
tl: self.tl.to_walk_position(),
br: self.br.to_walk_position(),
}
}
}
impl<const N: i32> From<(i32, i32, i32, i32)> for Rectangle<ScaledPosition<N>> {
fn from(coords: (i32, i32, i32, i32)) -> Self {
Self::new((coords.0, coords.1), (coords.2, coords.3))
}
}
impl<const N: i32> std::iter::IntoIterator for Rectangle<ScaledPosition<N>> {
type Item = ScaledPosition<N>;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
(self.tl.y..=self.br.y)
.flat_map(|y| (self.tl.x..=self.br.x).map(move |x| ScaledPosition::<N>::new(x, y)))
.collect::<Vec<_>>()
.into_iter()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::*;
#[test]
fn should_return_all_positions_of_rectangle() {
let rec: Rectangle<TilePosition> = Rectangle::new((0, 0), (2, 2));
let mut iter = rec.into_iter();
assert_eq!(Some(TilePosition { x: 0, y: 0 }), iter.next());
assert_eq!(Some(TilePosition { x: 1, y: 0 }), iter.next());
assert_eq!(Some(TilePosition { x: 2, y: 0 }), iter.next());
assert_eq!(Some(TilePosition { x: 0, y: 1 }), iter.next());
assert_eq!(Some(TilePosition { x: 1, y: 1 }), iter.next());
assert_eq!(Some(TilePosition { x: 2, y: 1 }), iter.next());
assert_eq!(Some(TilePosition { x: 0, y: 2 }), iter.next());
assert_eq!(Some(TilePosition { x: 1, y: 2 }), iter.next());
assert_eq!(Some(TilePosition { x: 2, y: 2 }), iter.next());
}
#[test]
fn should_return_border() {
let rec: Rectangle<WalkPosition> = Rectangle::new((3, 4), (5, 6));
assert_eq!(
rec.border(),
[
ScaledPosition { x: 3, y: 4 },
ScaledPosition { x: 4, y: 4 },
ScaledPosition { x: 5, y: 4 },
ScaledPosition { x: 5, y: 5 },
ScaledPosition { x: 5, y: 6 },
ScaledPosition { x: 4, y: 6 },
ScaledPosition { x: 3, y: 6 },
ScaledPosition { x: 3, y: 5 },
]
);
}
}