use crate::{
analyzer::{player::Player, Analyzer},
geom::*,
models::{GameCommand, PLAYER_MAX_THROTTLE, PLAYER_MIN_THROTTLE},
};
use rand::{thread_rng, Rng};
use std::{collections::VecDeque, fmt::Debug, time::Duration};
pub trait Behavior: Send + Debug {
fn next_command(&mut self, _: &Analyzer) -> Option<GameCommand>;
fn box_clone(&self) -> Box<Behavior>;
}
impl Clone for Box<Behavior> {
fn clone(&self) -> Self {
self.box_clone()
}
}
impl Default for Box<Behavior> {
fn default() -> Self {
Box::new(Skip {})
}
}
#[derive(Clone, Debug)]
pub struct Sequence {
inner: VecDeque<Box<Behavior>>,
}
impl Behavior for Sequence {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
while let Some(next) = self.inner.front_mut() {
if let Some(command) = next.next_command(analyzer) {
return Some(command);
}
self.inner.pop_front();
}
None
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
impl Sequence {
pub fn new() -> Self {
Sequence::with_slice(&[])
}
pub fn with_slice(behaviors: &[&Behavior]) -> Self {
Self { inner: behaviors.into_iter().map(|b| b.box_clone()).collect::<VecDeque<_>>() }
}
}
#[derive(Clone, Debug)]
pub struct Skip;
impl Behavior for Skip {
fn next_command(&mut self, _: &Analyzer) -> Option<GameCommand> {
None
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Noop;
impl Behavior for Noop {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
Some(GameCommand::Rotate(analyzer.own_player().angle.positive().get()))
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Throttle {
pub value: f32,
}
impl Behavior for Throttle {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
if (analyzer.own_player().throttle - self.value).abs() > 0.05 {
Some(GameCommand::Throttle(self.value))
} else {
None
}
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
impl Throttle {
pub fn stop() -> Self {
Self { value: 0.0 }
}
pub fn max() -> Self {
Self { value: PLAYER_MAX_THROTTLE }
}
}
#[derive(Clone, Debug)]
pub struct MoveTo {
pub destination: Point,
pub end_with_brake: bool,
}
impl Behavior for MoveTo {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
let own_player = analyzer.own_player();
if own_player.distance(&self.destination) < 10.0 {
if self.end_with_brake {
self.end_with_brake = false;
return Some(GameCommand::Throttle(0.0));
} else {
return None;
}
}
let angle = own_player.angle_to(&self.destination);
Sequence::with_slice(&[
&Rotate::with_margin_degrees(angle, 5.0),
&Throttle::max(),
&Noop {},
])
.next_command(analyzer)
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Rotate {
angle: Radian,
margin: Radian,
}
impl Behavior for Rotate {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
if (analyzer.own_player().angle.positive() - self.angle.positive()).abs() > self.margin {
Some(GameCommand::Rotate(self.angle.positive().get()))
} else {
None
}
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
impl Rotate {
pub fn new(angle: Radian) -> Self {
Self::with_margin_degrees(angle, 0.1)
}
pub fn with_margin_degrees(angle: Radian, margin_degrees: f32) -> Self {
Self { angle, margin: Radian::degrees(margin_degrees) }
}
}
#[derive(Clone, Debug)]
pub struct Fire {
times: u32,
}
impl Behavior for Fire {
fn next_command(&mut self, _: &Analyzer) -> Option<GameCommand> {
if self.times > 0 {
self.times -= 1;
Some(GameCommand::Fire)
} else {
None
}
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
impl Fire {
pub fn new() -> Self {
Self::with_times(1)
}
pub fn with_times(times: u32) -> Self {
Self { times }
}
}
#[derive(Clone, Debug)]
pub struct FireAt {
target: Target,
times: u32,
next: Sequence,
}
impl Behavior for FireAt {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
if let Some(next_command) = self.next.next_command(analyzer) {
return Some(next_command);
}
if self.times > 0 {
if let Some(target) = self.target.get(analyzer) {
self.times -= 1;
let angle = analyzer.own_player().angle_to(target);
self.next =
Sequence::with_slice(&[&Rotate::with_margin_degrees(angle, 5.0), &Fire::new()]);
return self.next.next_command(analyzer);
}
}
None
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
impl FireAt {
pub fn new(target: Target) -> Self {
Self::with_times(target, 1)
}
pub fn with_times(target: Target, times: u32) -> Self {
Self { target, times, next: Sequence::new() }
}
}
#[derive(Clone, Debug)]
struct Random;
impl Behavior for Random {
fn next_command(&mut self, _: &Analyzer) -> Option<GameCommand> {
let mut rng = thread_rng();
match rng.gen_range(0, 4) {
0 => None,
1 => Some(GameCommand::Rotate(rng.gen_range(0.0, 2.0 * std::f32::consts::PI))),
2 => {
Some(GameCommand::Throttle(rng.gen_range(PLAYER_MIN_THROTTLE, PLAYER_MAX_THROTTLE)))
},
3 => Some(GameCommand::Fire),
_ => unreachable!(),
}
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Chase {
pub target: Target,
pub distance: f32,
}
impl Behavior for Chase {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
if let Some(target) = self.target.get(analyzer) {
let distance_to_target = analyzer.own_player().distance(target);
if distance_to_target > self.distance {
let angle = analyzer.own_player().angle_to(target);
return Sequence::with_slice(&[
&Rotate::with_margin_degrees(angle, 10.0),
&Throttle::max(),
&Noop {},
])
.next_command(analyzer);
}
}
None
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub struct Dodge;
impl Behavior for Dodge {
fn next_command(&mut self, analyzer: &Analyzer) -> Option<GameCommand> {
if let Some(bullet) = analyzer.bullets_colliding(Duration::from_secs(1)).next() {
let angle = bullet.velocity.tangent();
Sequence::with_slice(&[&Rotate::with_margin_degrees(angle, 30.0), &Throttle::max()])
.next_command(analyzer)
} else {
None
}
}
fn box_clone(&self) -> Box<Behavior> {
Box::new(self.clone())
}
}
#[derive(Clone, Debug)]
pub enum Target {
Id(u32),
Closest,
LeastMoving,
HighestScore,
HighestScoreAfter(Duration),
}
impl Target {
pub fn get<'a>(&self, analyzer: &'a Analyzer) -> Option<&'a Player> {
match self {
Target::Id(id) => analyzer.player(*id),
Target::Closest => analyzer.player_closest(),
Target::LeastMoving => analyzer.player_least_moving(),
Target::HighestScore => analyzer.player_highest_score(),
Target::HighestScoreAfter(after) => analyzer.player_highest_score_after(*after),
}
}
}