little cleanup + new strat
This commit is contained in:
@@ -1,62 +1,90 @@
|
|||||||
use crate::models::{game_state::GameState, player_action::PlayerAction};
|
use crate::models::{game_state::GameState, player_action::PlayerAction};
|
||||||
use crate::models::base::Base;
|
use crate::models::base::Base;
|
||||||
|
use crate::models::target::Target;
|
||||||
|
|
||||||
pub fn decide(game_state: GameState) -> Vec<PlayerAction> {
|
pub fn decide(game_state: GameState) -> Vec<PlayerAction> {
|
||||||
|
// all planned attacks
|
||||||
let mut attacks: Vec<PlayerAction> = Vec::new();
|
let mut attacks: Vec<PlayerAction> = Vec::new();
|
||||||
|
|
||||||
|
// a list of all friendly and opponent bases
|
||||||
let mut own_bases: Vec<Base> = Vec::new();
|
let mut own_bases: Vec<Base> = Vec::new();
|
||||||
let mut opponent_bases: Vec<Base> = Vec::new();
|
let mut opponent_bases: Vec<Base> = Vec::new();
|
||||||
|
|
||||||
for base in game_state.bases {
|
// iterate over all bases
|
||||||
|
game_state.bases.iter().for_each(|base| {
|
||||||
|
// sort them into owned and opponent bases
|
||||||
if base.player == game_state.game.player {
|
if base.player == game_state.game.player {
|
||||||
own_bases.push(base);
|
own_bases.push(base.clone());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
opponent_bases.push(base);
|
opponent_bases.push(base.clone());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
for base in own_bases {
|
// iterate through all owned bases
|
||||||
let mut target: Option<(Base, u32)> = None;
|
own_bases.iter().for_each(|base| {
|
||||||
for opponent in opponent_bases.clone() {
|
// the target of the base, currently none
|
||||||
let req = base.required_to_defeat(&opponent, &game_state.actions, &game_state.config);
|
let mut target: Option<Target> = None;
|
||||||
if req > 0 && req < base.population && base.population_in_n_ticks(base.distance_to(&opponent), &game_state.config, &game_state.actions) > 5 {
|
|
||||||
if let Some(target_some) = target {
|
// iterate over all opponents as possible targets
|
||||||
if (target_some.1 >= req && target_some.0.distance_to(&base) > opponent.distance_to(&base)) || ((target_some.0.uid != 0 && base.uid == 0) && opponent.distance_to(&base) < 10 && target_some.0.distance_to(&base) > opponent.distance_to(&base)) {
|
opponent_bases.iter().for_each(|opponent| {
|
||||||
target = Some((opponent, req));
|
// calculate the required bits to conquer the opponents base
|
||||||
|
let required_bits: u32 = base.required_to_defeat(&opponent, &game_state.config, &game_state.actions);
|
||||||
|
|
||||||
|
// check that the base could be conquered with at least 1/4 of the population remaining in base
|
||||||
|
if required_bits + (game_state.config.base_levels[base.level as usize].max_population / 4) < base.population {
|
||||||
|
// check if there is already a target
|
||||||
|
if let Some(target_value) = target {
|
||||||
|
// get if the new target is closer than the old one
|
||||||
|
let is_closer: bool = base.distance_to(&opponent) < base.distance_to(&target_value.base);
|
||||||
|
// check if the new target is closer and the required bits are less or the game is free and within grace period
|
||||||
|
if is_closer && (required_bits <= target_value.required_bits || (base.uid == 0 && opponent.distance_to(&base) < game_state.config.paths.grace_period)) {
|
||||||
|
// set the new target
|
||||||
|
target = Some(Target::new(base.clone(), required_bits));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// there is no target yet, so this is the first one
|
||||||
else {
|
else {
|
||||||
target = Some((opponent, req));
|
// set the new target
|
||||||
}
|
target = Some(Target::new(base.clone(), required_bits));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut consider_upgrade: bool = false;
|
||||||
|
// check if a target has been selected
|
||||||
if let Some(target) = target {
|
if let Some(target) = target {
|
||||||
if base.population_in_n_ticks(base.distance_to(&target.0), &game_state.config, &game_state.actions) > target.1 + 3 * game_state.config.paths.death_rate + 1 {
|
//check if the base would die until the attack reaches its target
|
||||||
|
if !base.will_die_within_in_n_ticks(base.distance_to(&target.base), &game_state.config, &game_state.actions) {
|
||||||
|
// attack
|
||||||
attacks.push(PlayerAction {
|
attacks.push(PlayerAction {
|
||||||
src: base.uid,
|
src: base.uid,
|
||||||
dest: target.0.uid,
|
dest: target.base.uid,
|
||||||
amount: target.1 + 3 * game_state.config.paths.death_rate,
|
amount: target.required_bits + 3,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if base.population > game_state.config.base_levels[base.level as usize].max_population -1 {
|
// consider upgrade
|
||||||
|
else {
|
||||||
|
consider_upgrade = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// consider upgrade
|
||||||
|
else {
|
||||||
|
consider_upgrade = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the max population is at its limit and no attacks were made
|
||||||
|
if consider_upgrade && base.population > game_state.config.base_levels[base.level as usize].max_population - 1 {
|
||||||
|
// upgrade with all bits over limit
|
||||||
attacks.push(PlayerAction {
|
attacks.push(PlayerAction {
|
||||||
src: base.uid,
|
src: base.uid,
|
||||||
dest: base.uid,
|
dest: base.uid,
|
||||||
amount: base.population - (game_state.config.base_levels[base.level as usize].max_population - 3),
|
amount: base.population - (game_state.config.base_levels[base.level as usize].max_population - 1),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if base.population > game_state.config.base_levels[base.level as usize].max_population -1 {
|
|
||||||
attacks.push(PlayerAction {
|
|
||||||
src: base.uid,
|
|
||||||
dest: base.uid,
|
|
||||||
amount: base.population - (game_state.config.base_levels[base.level as usize].max_population - 3),
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
println!("{:?}", target);
|
// return attacks
|
||||||
}
|
|
||||||
return attacks;
|
return attacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ pub mod path_config;
|
|||||||
pub mod player_action;
|
pub mod player_action;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
pub mod progress;
|
pub mod progress;
|
||||||
|
pub mod target;
|
||||||
|
|
||||||
|
|||||||
@@ -27,41 +27,74 @@ impl Default for Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Base {
|
impl Base {
|
||||||
pub fn population_in_n_ticks(&self, ticks: u32, config: &GameConfig, attacks: &Vec<BoardAction>) -> u32 {
|
// get base population from base in n ticks with current config and attacks on the board
|
||||||
|
fn raw_population_in_n_ticks(&self, ticks: u32, config: &GameConfig, attacks: &Vec<BoardAction>) -> i32 {
|
||||||
|
// set the population in future to the current population
|
||||||
let mut population_in_future: i32 = self.population as i32;
|
let mut population_in_future: i32 = self.population as i32;
|
||||||
if self.uid != 0 { population_in_future += (ticks + 1) as i32 * config.base_levels[self.level as usize].spawn_rate as i32; }
|
|
||||||
for attack in attacks {
|
// check if the base has a passive generation rate
|
||||||
|
if self.uid != 0 {
|
||||||
|
// add the spawned bits to the population
|
||||||
|
population_in_future += (ticks * config.base_levels[self.level as usize].spawn_rate) as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through all attacks
|
||||||
|
attacks.iter().for_each(|attack| {
|
||||||
|
// check if the attack will arrive in time
|
||||||
if attack.arrival_in_ticks() >= ticks {
|
if attack.arrival_in_ticks() >= ticks {
|
||||||
|
// get the amount of bits in the attack on arrival
|
||||||
let val_on_target: i32 = attack.amount_at_target(&config.paths) as i32;
|
let val_on_target: i32 = attack.amount_at_target(&config.paths) as i32;
|
||||||
|
|
||||||
|
// check if the attack is owned by the base
|
||||||
if attack.player == self.player {
|
if attack.player == self.player {
|
||||||
|
// the attack will contribute to the population
|
||||||
population_in_future += val_on_target;
|
population_in_future += val_on_target;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// the attack will fight with the base
|
||||||
population_in_future -= val_on_target;
|
population_in_future -= val_on_target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
return population_in_future.abs() as u32;
|
// return abs of population, because on negative population it is likely to be conquered by an opponent
|
||||||
|
return population_in_future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn population_in_n_ticks(&self, ticks: u32, config: &GameConfig, attacks: &Vec<BoardAction>) -> u32 {
|
||||||
|
return self.raw_population_in_n_ticks(ticks, config, attacks).abs() as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn will_die_within_in_n_ticks(&self, ticks: u32, config: &GameConfig, attacks: &Vec<BoardAction>) -> bool {
|
||||||
|
return self.raw_population_in_n_ticks(ticks, config, attacks) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the distance to another base
|
||||||
pub fn distance_to(&self, base: &Base) -> u32 {
|
pub fn distance_to(&self, base: &Base) -> u32 {
|
||||||
return (((base.position.x - self.position.x).pow(2) + (base.position.y - self.position.y).pow(2) + (base.position.z - self.position.z).pow(2)) as f64).powf(1.0 / 2.0) as u32;
|
// calculate the Euclidean distance between the bases
|
||||||
|
return (( (base.position.x - self.position.x).pow(2)
|
||||||
|
+ (base.position.y - self.position.y).pow(2)
|
||||||
|
+ (base.position.z - self.position.z).pow(2)
|
||||||
|
) as f64).sqrt() as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn required_to_defeat(&self, base: &Base, attacks: &Vec<BoardAction>, game_config: &GameConfig) -> u32 {
|
// get the amount of bits required to defeat another base
|
||||||
let d: u32 = self.distance_to(base);
|
pub fn required_to_defeat(&self, target_base: &Base, game_config: &GameConfig, attacks: &Vec<BoardAction>) -> u32 {
|
||||||
|
// get the distance between the bases
|
||||||
|
let d: u32 = self.distance_to(target_base);
|
||||||
|
|
||||||
let mut pop = base.population_in_n_ticks(d, game_config, attacks);
|
// get the population of the target base when an own attack would arrive
|
||||||
|
let population: u32 = target_base.population_in_n_ticks(d, game_config, attacks);
|
||||||
|
|
||||||
if base.uid == 0 && base.level == 4 {
|
// to defeat a base there has to be at least one more bit arriving than the current population is
|
||||||
pop = 1;
|
let mut requirement: u32 = population + 1;
|
||||||
}
|
|
||||||
else {
|
// check if the distance is greater than the grace period
|
||||||
pop += 3;
|
if d > game_config.paths.grace_period {
|
||||||
|
// add the loss over the additional distance
|
||||||
|
requirement += (d - game_config.paths.grace_period) * game_config.paths.death_rate
|
||||||
}
|
}
|
||||||
|
|
||||||
if d < game_config.paths.grace_period {return pop}
|
// return required bits
|
||||||
|
return requirement;
|
||||||
return pop + (d - game_config.paths.grace_period) * game_config.paths.death_rate
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,26 @@ impl Default for BoardAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BoardAction {
|
impl BoardAction {
|
||||||
|
// get the remaining ticks until arrival
|
||||||
pub fn arrival_in_ticks(&self) -> u32 {
|
pub fn arrival_in_ticks(&self) -> u32 {
|
||||||
|
// return the remaining ticks
|
||||||
return self.progress.distance - self.progress.traveled;
|
return self.progress.distance - self.progress.traveled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the amount of bits that reach a base
|
||||||
pub fn amount_at_target(&self, config: &PathConfig) -> u32 {
|
pub fn amount_at_target(&self, config: &PathConfig) -> u32 {
|
||||||
if self.progress.distance < config.grace_period { return self.amount; }
|
// if the distance is smaller than the grace period all bits will reach their destination
|
||||||
|
if self.progress.distance < config.grace_period {
|
||||||
|
return self.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the number of bits that will die until their destination
|
||||||
let deaths: u32 = config.death_rate * (self.progress.distance - config.grace_period);
|
let deaths: u32 = config.death_rate * (self.progress.distance - config.grace_period);
|
||||||
|
|
||||||
|
// there are more deaths than the bits in the attack, no bit will reach the destination
|
||||||
if deaths > self.amount { return 0 }
|
if deaths > self.amount { return 0 }
|
||||||
|
|
||||||
|
// return the bits at destination
|
||||||
return self.amount - deaths;
|
return self.amount - deaths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/models/target.rs
Normal file
13
src/models/target.rs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
use crate::models::base::Base;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Target {
|
||||||
|
pub base: Base,
|
||||||
|
pub required_bits: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Target {
|
||||||
|
pub fn new(base: Base, required_bits: u32) -> Target {
|
||||||
|
return Target {base, required_bits};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user