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;
|
2022-02-23 21:08:56 +01:00
|
|
|
pub mod match_log;
|
2022-01-18 20:17:13 +01:00
|
|
|
pub mod pw_match;
|
2021-12-25 14:45:05 +01:00
|
|
|
|
|
|
|
use std::{
|
|
|
|
path::PathBuf,
|
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
|
|
|
|
2022-01-22 14:32:43 +01:00
|
|
|
use async_trait::async_trait;
|
2022-02-23 21:08:56 +01:00
|
|
|
use futures::{stream::FuturesOrdered, StreamExt};
|
2021-12-25 14:45:05 +01:00
|
|
|
use match_context::MatchCtx;
|
2022-02-23 21:08:56 +01:00
|
|
|
use match_log::{create_log_sink, MatchLogger};
|
2021-12-25 14:45:05 +01:00
|
|
|
use planetwars_rules::PwConfig;
|
|
|
|
|
2022-09-22 19:38:09 +02:00
|
|
|
pub use self::match_context::{EventBus, PlayerHandle};
|
2021-12-25 14:45:05 +01:00
|
|
|
|
|
|
|
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-28 14:57:41 +01:00
|
|
|
pub struct MatchPlayer {
|
2022-01-22 14:32:43 +01:00
|
|
|
pub bot_spec: Box<dyn BotSpec>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
2022-02-03 20:46:40 +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>>,
|
2022-02-23 21:08:56 +01:00
|
|
|
match_logger: MatchLogger,
|
2022-01-22 14:32:43 +01:00
|
|
|
) -> Box<dyn PlayerHandle>;
|
2021-12-25 14:45:05 +01:00
|
|
|
}
|
|
|
|
|
2022-04-27 20:08:48 +02:00
|
|
|
pub struct MatchOutcome {
|
|
|
|
pub winner: Option<usize>,
|
2022-09-26 19:57:22 +02:00
|
|
|
pub player_outcomes: Vec<PlayerOutcome>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct PlayerOutcome {
|
|
|
|
pub had_errors: bool,
|
|
|
|
pub crashed: bool,
|
2022-04-27 20:08:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn run_match(config: MatchConfig) -> MatchOutcome {
|
2021-12-25 14:45:05 +01:00
|
|
|
let pw_config = PwConfig {
|
|
|
|
map_file: config.map_path,
|
2022-08-28 15:47:31 +02:00
|
|
|
max_turns: 500,
|
2021-12-25 14:45:05 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
let event_bus = Arc::new(Mutex::new(EventBus::new()));
|
2022-02-23 21:08:56 +01:00
|
|
|
let match_logger = create_log_sink(&config.log_path).await;
|
2021-12-25 14:45:05 +01:00
|
|
|
|
|
|
|
// 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-02-23 21:08:56 +01:00
|
|
|
start_bot(
|
|
|
|
player_id,
|
|
|
|
event_bus.clone(),
|
2022-03-13 15:20:03 +01:00
|
|
|
player.bot_spec.as_ref(),
|
2022-02-23 21:08:56 +01:00
|
|
|
match_logger.clone(),
|
|
|
|
)
|
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
|
|
|
|
2022-02-23 21:08:56 +01:00
|
|
|
let match_ctx = MatchCtx::new(event_bus, players, match_logger);
|
2021-12-25 21:49:16 +01:00
|
|
|
|
2022-09-26 19:57:22 +02:00
|
|
|
let mut match_instance = pw_match::PwMatch::create(match_ctx, pw_config);
|
|
|
|
match_instance.run().await;
|
|
|
|
match_instance.match_ctx.shutdown().await;
|
|
|
|
|
|
|
|
let survivors = match_instance.match_state.state().living_players();
|
2022-04-27 20:08:48 +02:00
|
|
|
let winner = if survivors.len() == 1 {
|
|
|
|
Some(survivors[0])
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2022-09-26 19:57:22 +02:00
|
|
|
let player_outcomes = (1..=config.players.len())
|
|
|
|
.map(|player_id| {
|
|
|
|
let player_status = &match_instance.player_status[&player_id];
|
|
|
|
PlayerOutcome {
|
|
|
|
had_errors: player_status.had_command_errors,
|
|
|
|
crashed: player_status.terminated,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
MatchOutcome {
|
|
|
|
winner,
|
|
|
|
player_outcomes,
|
|
|
|
}
|
2021-12-25 14:45:05 +01:00
|
|
|
}
|
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>>,
|
2022-03-13 15:20:03 +01:00
|
|
|
bot_spec: &dyn BotSpec,
|
2022-02-23 21:08:56 +01:00
|
|
|
match_logger: MatchLogger,
|
2022-01-23 13:23:23 +01:00
|
|
|
) -> (u32, Box<dyn PlayerHandle>) {
|
2022-02-23 21:08:56 +01:00
|
|
|
let player_handle = bot_spec.run_bot(player_id, event_bus, match_logger).await;
|
2022-01-23 13:23:23 +01:00
|
|
|
(player_id, player_handle)
|
|
|
|
}
|