mess from yesterday
This commit is contained in:
1
src/logic.rs
Normal file
1
src/logic.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod strategy;
|
||||
75
src/logic/strategy.rs
Normal file
75
src/logic/strategy.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::models::{game_state::GameState, player_action::PlayerAction};
|
||||
use crate::models::base::Base;
|
||||
|
||||
pub fn decide(game_state: GameState) -> Vec<PlayerAction> {
|
||||
let mut attacks: Vec<PlayerAction> = Vec::new();
|
||||
|
||||
let mut own_bases: Vec<Base> = Vec::new();
|
||||
let mut opponent_bases: Vec<Base> = Vec::new();
|
||||
|
||||
for base in game_state.bases {
|
||||
if base.player == game_state.game.player {
|
||||
own_bases.push(base);
|
||||
}
|
||||
else {
|
||||
opponent_bases.push(base);
|
||||
}
|
||||
}
|
||||
|
||||
for base in own_bases {
|
||||
let mut target: Option<(Base, u32)> = None;
|
||||
for opponent in opponent_bases.clone() {
|
||||
let req = base.required_to_defeat(&opponent, &game_state.actions, &game_state.config);
|
||||
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 {
|
||||
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)) {
|
||||
target = Some((opponent, req));
|
||||
}
|
||||
}
|
||||
else {
|
||||
target = Some((opponent, req));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
attacks.push(PlayerAction {
|
||||
src: base.uid,
|
||||
dest: target.0.uid,
|
||||
amount: target.1 + 3 * game_state.config.paths.death_rate,
|
||||
});
|
||||
}
|
||||
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),
|
||||
});
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decide_test() {
|
||||
let want = vec![PlayerAction::default()];
|
||||
|
||||
let result = decide(GameState::default());
|
||||
|
||||
assert!(want == result)
|
||||
}
|
||||
}
|
||||
34
src/main.rs
Normal file
34
src/main.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
#![allow(non_snake_case)]
|
||||
mod logic;
|
||||
mod models;
|
||||
mod own_model;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
};
|
||||
use models::{game_state::GameState, player_action::PlayerAction};
|
||||
use tracing::info;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(tracing::Level::DEBUG)
|
||||
.init();
|
||||
|
||||
info!("Start Rust player");
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(identify))
|
||||
.route("/", post(index));
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
|
||||
async fn identify() -> &'static str {
|
||||
"Bitwars Rust Player"
|
||||
}
|
||||
|
||||
async fn index(Json(payload): Json<GameState>) -> Json<Vec<PlayerAction>> {
|
||||
Json(logic::strategy::decide(payload))
|
||||
}
|
||||
11
src/models.rs
Normal file
11
src/models.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
pub mod base;
|
||||
pub mod base_level;
|
||||
pub mod board_action;
|
||||
pub mod game;
|
||||
pub mod game_config;
|
||||
pub mod game_state;
|
||||
pub mod path_config;
|
||||
pub mod player_action;
|
||||
pub mod position;
|
||||
pub mod progress;
|
||||
|
||||
67
src/models/base.rs
Normal file
67
src/models/base.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use crate::models::position::Position;
|
||||
use serde::Deserialize;
|
||||
use crate::models::board_action::BoardAction;
|
||||
use crate::models::game_config::GameConfig;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct Base {
|
||||
pub position: Position, // position of the base
|
||||
pub uid: u32, // uid of the base
|
||||
pub player: u32, // owner of the base
|
||||
pub population: u32, // current population of the base
|
||||
pub level: u32, // level of the base
|
||||
pub units_until_upgrade: u32, // number of units required to upgrade
|
||||
}
|
||||
|
||||
impl Default for Base {
|
||||
fn default() -> Self {
|
||||
Base {
|
||||
position: Position::default(),
|
||||
uid: 0,
|
||||
player: 0,
|
||||
population: 0,
|
||||
level: 0,
|
||||
units_until_upgrade: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Base {
|
||||
pub fn population_in_n_ticks(&self, ticks: u32, config: &GameConfig, attacks: &Vec<BoardAction>) -> u32 {
|
||||
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 {
|
||||
if attack.arrival_in_ticks() >= ticks {
|
||||
let val_on_target: i32 = attack.amount_at_target(&config.paths) as i32;
|
||||
if attack.player == self.player {
|
||||
population_in_future += val_on_target;
|
||||
}
|
||||
else {
|
||||
population_in_future -= val_on_target;
|
||||
}
|
||||
}
|
||||
}
|
||||
return population_in_future.abs() as 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;
|
||||
}
|
||||
|
||||
pub fn required_to_defeat(&self, base: &Base, attacks: &Vec<BoardAction>, game_config: &GameConfig) -> u32 {
|
||||
let d: u32 = self.distance_to(base);
|
||||
|
||||
let mut pop = base.population_in_n_ticks(d, game_config, attacks);
|
||||
|
||||
if base.uid == 0 && base.level == 4 {
|
||||
pop = 1;
|
||||
}
|
||||
else {
|
||||
pop += 3;
|
||||
}
|
||||
|
||||
if d < game_config.paths.grace_period {return pop}
|
||||
|
||||
return pop + (d - game_config.paths.grace_period) * game_config.paths.death_rate
|
||||
}
|
||||
}
|
||||
18
src/models/base_level.rs
Normal file
18
src/models/base_level.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct BaseLevel {
|
||||
pub max_population: u32, // number of sustainable bits
|
||||
pub upgrade_cost: u32, // bits required to unlock this upgrade
|
||||
pub spawn_rate: u32, // number uf bits spawned per tick
|
||||
}
|
||||
|
||||
impl Default for BaseLevel {
|
||||
fn default() -> Self {
|
||||
BaseLevel {
|
||||
max_population: 0,
|
||||
upgrade_cost: 0,
|
||||
spawn_rate: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
39
src/models/board_action.rs
Normal file
39
src/models/board_action.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use crate::models::progress::Progress;
|
||||
use serde::Deserialize;
|
||||
use uuid::Uuid;
|
||||
use crate::models::path_config::PathConfig;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct BoardAction {
|
||||
pub src: u32, // uid of source base
|
||||
pub dest: u32, // uid of destination base
|
||||
pub amount: u32, // number of bits moved
|
||||
pub uuid: Uuid, // uuid of the action
|
||||
pub player: u32, // id of the player who took the action
|
||||
pub progress: Progress, // progress off the action
|
||||
}
|
||||
|
||||
impl Default for BoardAction {
|
||||
fn default() -> Self {
|
||||
BoardAction {
|
||||
src: 0,
|
||||
dest: 0,
|
||||
amount: 0,
|
||||
uuid: Uuid::default(),
|
||||
player: 0,
|
||||
progress: Progress::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BoardAction {
|
||||
pub fn arrival_in_ticks(&self) -> u32 {
|
||||
return self.progress.distance - self.progress.traveled;
|
||||
}
|
||||
pub fn amount_at_target(&self, config: &PathConfig) -> u32 {
|
||||
if self.progress.distance < config.grace_period { return self.amount; }
|
||||
let deaths: u32 = config.death_rate * (self.progress.distance - config.grace_period);
|
||||
if deaths > self.amount { return 0 }
|
||||
return self.amount - deaths;
|
||||
}
|
||||
}
|
||||
22
src/models/game.rs
Normal file
22
src/models/game.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct Game {
|
||||
pub uid: u32, // uid of game
|
||||
pub tick: u32, // tick in game
|
||||
pub player_count: u32, // number of players
|
||||
pub remaining_players: u32, // number of players remaining
|
||||
pub player: u32, // uid of your player
|
||||
}
|
||||
|
||||
impl Default for Game {
|
||||
fn default() -> Self {
|
||||
Game {
|
||||
uid: 0,
|
||||
tick: 0,
|
||||
player_count: 0,
|
||||
remaining_players: 0,
|
||||
player: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/models/game_config.rs
Normal file
18
src/models/game_config.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use crate::models::base_level::BaseLevel;
|
||||
use crate::models::path_config::PathConfig;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct GameConfig {
|
||||
pub base_levels: Vec<BaseLevel>, // all available base levels
|
||||
pub paths: PathConfig, // settings containing paths between bases
|
||||
}
|
||||
|
||||
impl Default for GameConfig {
|
||||
fn default() -> Self {
|
||||
GameConfig {
|
||||
base_levels: vec![BaseLevel::default()],
|
||||
paths: PathConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/models/game_state.rs
Normal file
24
src/models/game_state.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::models::base::Base;
|
||||
use crate::models::board_action::BoardAction;
|
||||
use crate::models::game::Game;
|
||||
use crate::models::game_config::GameConfig;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct GameState {
|
||||
pub actions: Vec<BoardAction>, // list of all actions in progress
|
||||
pub bases: Vec<Base>, // list of all bases
|
||||
pub config: GameConfig, // settings for this game
|
||||
pub game: Game, // information about the game
|
||||
}
|
||||
|
||||
impl Default for GameState {
|
||||
fn default() -> Self {
|
||||
GameState {
|
||||
actions: vec![BoardAction::default()],
|
||||
bases: vec![Base::default()],
|
||||
config: GameConfig::default(),
|
||||
game: Game::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/models/path_config.rs
Normal file
16
src/models/path_config.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct PathConfig {
|
||||
pub grace_period: u32, // time until groups of bits take damage
|
||||
pub death_rate: u32, // number of units killed every tick after surpassing the grace period
|
||||
}
|
||||
|
||||
impl Default for PathConfig {
|
||||
fn default() -> Self {
|
||||
PathConfig {
|
||||
grace_period: 0,
|
||||
death_rate: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/models/player_action.rs
Normal file
18
src/models/player_action.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct PlayerAction {
|
||||
pub src: u32, // uid of source base
|
||||
pub dest: u32, // uid of destination base
|
||||
pub amount: u32, // number of bits moved
|
||||
}
|
||||
|
||||
impl Default for PlayerAction {
|
||||
fn default() -> Self {
|
||||
PlayerAction {
|
||||
src: 0,
|
||||
dest: 0,
|
||||
amount: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/models/position.rs
Normal file
14
src/models/position.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct Position {
|
||||
pub x: i32, // x coordinate
|
||||
pub y: i32, // y coordinate
|
||||
pub z: i32, // y coordinate
|
||||
}
|
||||
|
||||
impl Default for Position {
|
||||
fn default() -> Self {
|
||||
Position { x: 0, y: 0, z: 0 }
|
||||
}
|
||||
}
|
||||
16
src/models/progress.rs
Normal file
16
src/models/progress.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||
pub struct Progress {
|
||||
pub distance: u32, // distance between the bases
|
||||
pub traveled: u32, // distance already traveled
|
||||
}
|
||||
|
||||
impl Default for Progress {
|
||||
fn default() -> Self {
|
||||
Progress {
|
||||
distance: 0,
|
||||
traveled: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
0
src/own_model.rs
Normal file
0
src/own_model.rs
Normal file
Reference in New Issue
Block a user