use crate::{bot::Rs, distance::*, geometry::Point2, pixel_map::ByteMap};
use std::{
cmp::{Ordering, Reverse},
convert::TryInto,
fmt,
};
#[derive(Default)]
pub struct Ramps {
pub all: Vec<Ramp>,
pub my: Ramp,
pub enemy: Ramp,
}
type Pos = (usize, usize);
#[derive(Default, Clone)]
pub struct Ramp {
pub points: Vec<Pos>,
height: Rs<ByteMap>,
start_location: Point2,
}
impl Ramp {
pub(crate) fn new(points: Vec<Pos>, height: &Rs<ByteMap>, start_location: Point2) -> Self {
Self {
points,
height: Rs::clone(&height),
start_location,
}
}
pub fn upper(&self) -> Vec<Pos> {
let mut max = u8::MIN;
let mut result = Vec::new();
for &p in &self.points {
let h = self.height[p];
match h.cmp(&max) {
Ordering::Greater => {
max = h;
result = vec![p];
}
Ordering::Equal => result.push(p),
_ => {}
}
}
result
}
pub fn lower(&self) -> Vec<Pos> {
let mut min = u8::MAX;
let mut result = Vec::new();
for &p in &self.points {
let h = self.height[p];
match h.cmp(&min) {
Ordering::Less => {
min = h;
result = vec![p];
}
Ordering::Equal => result.push(p),
_ => {}
}
}
result
}
pub fn top_center(&self) -> Option<Pos> {
let ps = self.upper();
if ps.is_empty() {
None
} else {
let (x, y) = ps.iter().fold((0, 0), |(ax, ay), (x, y)| (ax + x, ay + y));
Some((x / ps.len(), y / ps.len()))
}
}
pub fn bottom_center(&self) -> Option<Pos> {
let ps = self.lower();
if ps.is_empty() {
None
} else {
let (x, y) = ps.iter().fold((0, 0), |(ax, ay), (x, y)| (ax + x, ay + y));
Some((x / ps.len(), y / ps.len()))
}
}
fn upper2_for_ramp_wall(&self) -> Option<[Pos; 2]> {
let mut upper = self.upper();
if upper.len() > 5 {
return None;
}
match upper.len().cmp(&2) {
Ordering::Greater => self.bottom_center().and_then(|(center_x, center_y)| {
upper.sort_unstable_by_key(|(x, y)| {
let dx = x.checked_sub(center_x).unwrap_or_else(|| center_x - x);
let dy = y.checked_sub(center_y).unwrap_or_else(|| center_y - y);
Reverse(dx * dx + dy * dy)
});
upper[..2].try_into().ok()
}),
Ordering::Equal => upper.as_slice().try_into().ok(),
Ordering::Less => None,
}
}
pub fn corner_depots(&self) -> Option<[Point2; 2]> {
if let Some(ps) = self.upper2_for_ramp_wall() {
let p1 = Point2::from(ps[0]);
let p2 = Point2::from(ps[1]);
let center = (p1 + p2) / 2.0;
return center.circle_intersection(self.depot_in_middle()?, 5_f32.sqrt());
}
None
}
pub fn barracks_in_middle(&self) -> Option<Point2> {
let upper_len = self.upper().len();
if upper_len != 2 && upper_len != 5 {
return None;
}
if let Some(ps) = self.upper2_for_ramp_wall() {
let p1 = Point2::from(ps[0]);
let p2 = Point2::from(ps[1]);
let intersects = p1.circle_intersection(p2, 5_f32.sqrt())?;
let (x, y) = *self.lower().first()?;
let lower = Point2::new(x as f32, y as f32);
return intersects.iter().furthest(lower).copied();
}
None
}
pub fn barracks_correct_placement(&self) -> Option<Point2> {
self.barracks_in_middle().map(|pos| {
if self
.corner_depots()
.map_or(false, |depots| pos.x + 1.0 > depots[0].x.max(depots[1].x))
{
pos
} else {
pos.offset(-2.0, 0.0)
}
})
}
pub fn depot_in_middle(&self) -> Option<Point2> {
let upper_len = self.upper().len();
if upper_len != 2 && upper_len != 5 {
return None;
}
if let Some(ps) = self.upper2_for_ramp_wall() {
let p1 = Point2::from(ps[0]);
let p2 = Point2::from(ps[1]);
let intersects = p1.circle_intersection(p2, 1.581_138_8)?; let (x, y) = *self.lower().first()?;
let lower = Point2::new(x as f32, y as f32);
return intersects.iter().furthest(lower).copied();
}
None
}
pub fn protoss_wall_pylon(&self) -> Option<Point2> {
let middle = self.depot_in_middle()?;
Some(middle + (self.barracks_in_middle()? - middle) * 6.0)
}
pub fn protoss_wall_buildings(&self) -> Option<[Point2; 2]> {
let middle = self.depot_in_middle()?;
let direction = self.barracks_in_middle()? - middle;
let mut depots = self.corner_depots()?.to_vec();
let start = self.start_location;
depots.sort_unstable_by(|d1, d2| {
d1.distance_squared(start)
.partial_cmp(&d2.distance_squared(start))
.unwrap()
});
let wall1 = depots[1] + direction;
Some([wall1, middle + direction + (middle - wall1) / 1.5])
}
pub fn protoss_wall_warpin(&self) -> Option<Point2> {
let middle = self.depot_in_middle()?;
let direction = self.barracks_in_middle()? - middle;
let mut depots = self.corner_depots()?.to_vec();
let start = self.start_location;
depots.sort_unstable_by(|d1, d2| {
d1.distance_squared(start)
.partial_cmp(&d2.distance_squared(start))
.unwrap()
});
Some(depots[0] - direction)
}
}
impl fmt::Debug for Ramp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Ramp({:?})", self.points)
}
}