mess from yesterday

This commit is contained in:
2024-05-18 12:17:49 +02:00
commit b71633f318
32 changed files with 1708 additions and 0 deletions

1
src/logic.rs Normal file
View File

@@ -0,0 +1 @@
pub mod strategy;

75
src/logic/strategy.rs Normal file
View 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
View 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
View 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
View 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
View 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,
}
}
}

View 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
View 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
View 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
View 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
View 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,
}
}
}

View 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
View 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
View 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
View File