track player status and return player outcomes from match

This commit is contained in:
Ilion Beyst 2022-09-26 19:57:22 +02:00
parent b7bcbdd3a6
commit 2a37b96da2
2 changed files with 67 additions and 47 deletions

View file

@ -14,7 +14,6 @@ use futures::{stream::FuturesOrdered, StreamExt};
use match_context::MatchCtx; use match_context::MatchCtx;
use match_log::{create_log_sink, MatchLogger}; use match_log::{create_log_sink, MatchLogger};
use planetwars_rules::PwConfig; use planetwars_rules::PwConfig;
use serde::{Deserialize, Serialize};
pub use self::match_context::{EventBus, PlayerHandle}; pub use self::match_context::{EventBus, PlayerHandle};
@ -25,18 +24,6 @@ pub struct MatchConfig {
pub players: Vec<MatchPlayer>, pub players: Vec<MatchPlayer>,
} }
#[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,
}
pub struct MatchPlayer { pub struct MatchPlayer {
pub bot_spec: Box<dyn BotSpec>, pub bot_spec: Box<dyn BotSpec>,
} }
@ -53,6 +40,12 @@ pub trait BotSpec: Send + Sync {
pub struct MatchOutcome { pub struct MatchOutcome {
pub winner: Option<usize>, pub winner: Option<usize>,
pub player_outcomes: Vec<PlayerOutcome>,
}
pub struct PlayerOutcome {
pub had_errors: bool,
pub crashed: bool,
} }
pub async fn run_match(config: MatchConfig) -> MatchOutcome { pub async fn run_match(config: MatchConfig) -> MatchOutcome {
@ -86,36 +79,31 @@ pub async fn run_match(config: MatchConfig) -> MatchOutcome {
let match_ctx = MatchCtx::new(event_bus, players, match_logger); let match_ctx = MatchCtx::new(event_bus, players, match_logger);
// TODO: is this still needed? let mut match_instance = pw_match::PwMatch::create(match_ctx, pw_config);
// assemble the math meta struct match_instance.run().await;
// let match_meta = MatchMeta { match_instance.match_ctx.shutdown().await;
// 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();
let final_state = pw_match::PwMatch::create(match_ctx, pw_config).run().await; let survivors = match_instance.match_state.state().living_players();
let survivors = final_state.state().living_players();
let winner = if survivors.len() == 1 { let winner = if survivors.len() == 1 {
Some(survivors[0]) Some(survivors[0])
} else { } else {
None None
}; };
MatchOutcome { winner } 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,
}
} }
// writing this as a closure causes lifetime inference errors // writing this as a closure causes lifetime inference errors

View file

@ -9,6 +9,7 @@ use tokio::time::Duration;
use serde_json; use serde_json;
use std::collections::HashMap;
use std::convert::TryInto; use std::convert::TryInto;
pub use planetwars_rules::config::{Config, Map}; pub use planetwars_rules::config::{Config, Map};
@ -17,30 +18,43 @@ use planetwars_rules::protocol as proto;
use planetwars_rules::serializer as pw_serializer; use planetwars_rules::serializer as pw_serializer;
use planetwars_rules::{PlanetWars, PwConfig}; use planetwars_rules::{PlanetWars, PwConfig};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MatchConfig {
pub map_name: String,
pub max_turns: usize,
}
pub struct PwMatch { pub struct PwMatch {
pub match_ctx: MatchCtx, pub match_ctx: MatchCtx,
pub match_state: PlanetWars, pub match_state: PlanetWars,
pub player_status: HashMap<usize, PlayerStatus>,
}
pub struct PlayerStatus {
pub had_command_errors: bool,
pub terminated: bool,
} }
impl PwMatch { impl PwMatch {
pub fn create(match_ctx: MatchCtx, config: PwConfig) -> Self { pub fn create(match_ctx: MatchCtx, config: PwConfig) -> Self {
// TODO: this is kind of hacked together at the moment // TODO: this is kind of hacked together at the moment
let match_state = PlanetWars::create(config, match_ctx.players().len()); let match_state = PlanetWars::create(config, match_ctx.players().len());
let player_status = match_ctx
.players()
.into_iter()
.map(|player_id| {
(
player_id as usize,
PlayerStatus {
had_command_errors: false,
terminated: false,
},
)
})
.collect();
PwMatch { PwMatch {
match_state, match_state,
match_ctx, match_ctx,
player_status,
} }
} }
pub async fn run(mut self) -> PlanetWars { pub async fn run(&mut self) {
// log initial state // log initial state
self.log_game_state(); self.log_game_state();
@ -49,14 +63,12 @@ impl PwMatch {
for (player_id, turn) in player_messages { for (player_id, turn) in player_messages {
let player_action = self.execute_action(player_id, turn); let player_action = self.execute_action(player_id, turn);
self.update_player_status(player_id, &player_action);
self.log_player_action(player_id, player_action); self.log_player_action(player_id, player_action);
} }
self.match_state.step(); self.match_state.step();
self.log_game_state(); self.log_game_state();
} }
self.match_ctx.shutdown().await;
self.match_state
} }
async fn prompt_players(&mut self) -> Vec<(usize, RequestResult<Vec<u8>>)> { async fn prompt_players(&mut self) -> Vec<(usize, RequestResult<Vec<u8>>)> {
@ -145,6 +157,26 @@ impl PwMatch {
} }
} }
} }
fn update_player_status(&mut self, player_id: usize, player_action: &PlayerAction) {
let player_status = self.player_status.get_mut(&player_id).unwrap();
match player_action {
PlayerAction::Commands(dispatches) => {
if dispatches.iter().any(|d| d.error.is_some()) {
player_status.had_command_errors = true;
}
}
PlayerAction::ParseError { .. } => {
player_status.had_command_errors = true;
}
PlayerAction::Timeout => {
player_status.had_command_errors = true;
}
PlayerAction::Terminated => {
player_status.terminated = true;
}
}
}
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]