2022-01-18 20:17:13 +01:00
|
|
|
pub mod bot_runner;
|
2022-01-22 14:32:43 +01:00
|
|
|
pub mod docker_runner;
|
2022-01-18 20:17:13 +01:00
|
|
|
pub mod match_context;
|
|
|
|
pub mod pw_match;
|
2021-12-25 14:45:05 +01:00
|
|
|
|
|
|
|
use std::{
|
2021-12-25 21:49:16 +01:00
|
|
|
io::Write,
|
2021-12-25 14:45:05 +01:00
|
|
|
path::PathBuf,
|
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
|
|
|
|
2022-01-22 14:32:43 +01:00
|
|
|
use async_trait::async_trait;
|
|
|
|
use futures::{stream::FuturesOrdered, FutureExt, StreamExt};
|
2021-12-25 14:45:05 +01:00
|
|
|
use match_context::MatchCtx;
|
|
|
|
use planetwars_rules::PwConfig;
|
2021-12-25 21:49:16 +01:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-12-25 14:45:05 +01:00
|
|
|
|
|
|
|
use self::match_context::{EventBus, PlayerHandle};
|
|
|
|
|
|
|
|
pub struct MatchConfig {
|
2021-12-25 21:49:16 +01:00
|
|
|
pub map_name: String,
|
2021-12-25 14:45:05 +01:00
|
|
|
pub map_path: PathBuf,
|
|
|
|
pub log_path: PathBuf,
|
2021-12-28 14:57:41 +01:00
|
|
|
pub players: Vec<MatchPlayer>,
|
2021-12-25 14:45:05 +01:00
|
|
|
}
|
|
|
|
|
2021-12-25 21:49:16 +01:00
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
pub struct MatchMeta {
|
|
|
|
pub map_name: String,
|
|
|
|
pub timestamp: chrono::DateTime<chrono::Local>,
|
|
|
|
pub players: Vec<PlayerInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
|
pub struct PlayerInfo {
|
|
|
|
pub name: String,
|
|
|
|
}
|
|
|
|
|
2021-12-28 14:57:41 +01:00
|
|
|
pub struct MatchPlayer {
|
2021-12-25 14:45:05 +01:00
|
|
|
pub name: String,
|
2022-01-22 14:32:43 +01:00
|
|
|
pub bot_spec: Box<dyn BotSpec>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2022-01-23 13:23:23 +01:00
|
|
|
pub trait BotSpec : Send + Sync{
|
2022-01-22 14:32:43 +01:00
|
|
|
async fn run_bot(
|
|
|
|
&self,
|
|
|
|
player_id: u32,
|
|
|
|
event_bus: Arc<Mutex<EventBus>>,
|
|
|
|
) -> Box<dyn PlayerHandle>;
|
2021-12-25 14:45:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn run_match(config: MatchConfig) {
|
|
|
|
let pw_config = PwConfig {
|
|
|
|
map_file: config.map_path,
|
|
|
|
max_turns: 100,
|
|
|
|
};
|
|
|
|
|
|
|
|
let event_bus = Arc::new(Mutex::new(EventBus::new()));
|
|
|
|
|
|
|
|
// start bots
|
2022-01-22 14:32:43 +01:00
|
|
|
// TODO: what happens when a bot fails?
|
2021-12-25 14:45:05 +01:00
|
|
|
let players = config
|
|
|
|
.players
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
2021-12-28 14:57:41 +01:00
|
|
|
.map(|(player_id, player)| {
|
2021-12-25 14:45:05 +01:00
|
|
|
let player_id = (player_id + 1) as u32;
|
2022-01-23 13:23:23 +01:00
|
|
|
start_bot(player_id, event_bus.clone(), &player.bot_spec)
|
2021-12-25 14:45:05 +01:00
|
|
|
})
|
2022-01-22 14:32:43 +01:00
|
|
|
.collect::<FuturesOrdered<_>>()
|
|
|
|
// await all results
|
|
|
|
.collect()
|
|
|
|
.await;
|
2021-12-25 21:49:16 +01:00
|
|
|
let mut log_file = std::fs::File::create(config.log_path).expect("could not create log file");
|
|
|
|
|
|
|
|
// assemble the math meta struct
|
|
|
|
let match_meta = MatchMeta {
|
|
|
|
map_name: config.map_name.clone(),
|
|
|
|
timestamp: chrono::Local::now(),
|
|
|
|
players: config
|
|
|
|
.players
|
|
|
|
.iter()
|
|
|
|
.map(|bot| PlayerInfo {
|
|
|
|
name: bot.name.clone(),
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
};
|
|
|
|
write!(
|
|
|
|
log_file,
|
|
|
|
"{}\n",
|
|
|
|
serde_json::to_string(&match_meta).unwrap()
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-12-25 14:45:05 +01:00
|
|
|
let match_ctx = MatchCtx::new(event_bus, players, log_file);
|
|
|
|
|
|
|
|
let match_state = pw_match::PwMatch::create(match_ctx, pw_config);
|
|
|
|
match_state.run().await;
|
|
|
|
}
|
2022-01-23 13:23:23 +01:00
|
|
|
|
|
|
|
// writing this as a closure causes lifetime inference errors
|
|
|
|
async fn start_bot(
|
|
|
|
player_id: u32,
|
|
|
|
event_bus: Arc<Mutex<EventBus>>,
|
|
|
|
bot_spec: &Box<dyn BotSpec>,
|
|
|
|
) -> (u32, Box<dyn PlayerHandle>) {
|
|
|
|
let player_handle = bot_spec.run_bot(player_id, event_bus).await;
|
|
|
|
(player_id, player_handle)
|
|
|
|
}
|