add maps to matches api

This commit is contained in:
Ilion Beyst 2022-08-26 19:21:30 +02:00
parent 624fa99fad
commit e26f13c8bb
6 changed files with 45 additions and 13 deletions

View file

@ -1,4 +1,4 @@
use diesel::{PgConnection, QueryDsl, QueryResult, RunQueryDsl}; use diesel::prelude::*;
use crate::schema::maps; use crate::schema::maps;
@ -26,6 +26,10 @@ pub fn find_map(id: i32, conn: &PgConnection) -> QueryResult<Map> {
maps::table.find(id).get_result(conn) maps::table.find(id).get_result(conn)
} }
pub fn find_map_by_name(name: &str, conn: &PgConnection) -> QueryResult<Map> {
maps::table.filter(maps::name.eq(name)).first(conn)
}
pub fn list_maps(conn: &PgConnection) -> QueryResult<Vec<Map>> { pub fn list_maps(conn: &PgConnection) -> QueryResult<Vec<Map>> {
maps::table.get_results(conn) maps::table.get_results(conn)
} }

View file

@ -21,6 +21,7 @@ pub struct NewMatch<'a> {
pub state: MatchState, pub state: MatchState,
pub log_path: &'a str, pub log_path: &'a str,
pub is_public: bool, pub is_public: bool,
pub map_id: Option<i32>,
} }
#[derive(Insertable)] #[derive(Insertable)]

View file

@ -111,15 +111,20 @@ impl pb::client_api_service_server::ClientApiService for ClientApiServer {
db::bots::find_bot_with_version_by_name(&match_request.opponent_name, &conn) db::bots::find_bot_with_version_by_name(&match_request.opponent_name, &conn)
.map_err(|_| Status::not_found("opponent not found"))?; .map_err(|_| Status::not_found("opponent not found"))?;
// TODO: allow map as parameter here
let map = db::maps::find_map_by_name(&"hex", &conn)
.map_err(|_| Status::not_found("map not found"))?;
let player_key = gen_alphanumeric(32); let player_key = gen_alphanumeric(32);
let remote_bot_spec = Box::new(RemoteBotSpec { let remote_bot_spec = Box::new(RemoteBotSpec {
player_key: player_key.clone(), player_key: player_key.clone(),
router: self.router.clone(), router: self.router.clone(),
}); });
let run_match = RunMatch::from_players( let run_match = RunMatch::new(
self.runner_config.clone(), self.runner_config.clone(),
false, false,
map,
vec![ vec![
MatchPlayer::BotSpec { MatchPlayer::BotSpec {
spec: remote_bot_spec, spec: remote_bot_spec,

View file

@ -7,6 +7,7 @@ use tokio::task::JoinHandle;
use crate::{ use crate::{
db::{ db::{
self, self,
maps::Map,
matches::{MatchData, MatchResult}, matches::{MatchData, MatchResult},
}, },
util::gen_alphanumeric, util::gen_alphanumeric,
@ -18,6 +19,10 @@ pub struct RunMatch {
players: Vec<MatchPlayer>, players: Vec<MatchPlayer>,
config: Arc<GlobalConfig>, config: Arc<GlobalConfig>,
is_public: bool, is_public: bool,
// Map is mandatory for now.
// It would be nice to allow "anonymous" (eg. randomly generated) maps
// in the future, too.
map: Map,
} }
pub enum MatchPlayer { pub enum MatchPlayer {
@ -32,9 +37,10 @@ pub enum MatchPlayer {
impl RunMatch { impl RunMatch {
// TODO: create a MatchParams struct // TODO: create a MatchParams struct
pub fn from_players( pub fn new(
config: Arc<GlobalConfig>, config: Arc<GlobalConfig>,
is_public: bool, is_public: bool,
map: Map,
players: Vec<MatchPlayer>, players: Vec<MatchPlayer>,
) -> Self { ) -> Self {
let log_file_name = format!("{}.log", gen_alphanumeric(16)); let log_file_name = format!("{}.log", gen_alphanumeric(16));
@ -43,13 +49,14 @@ impl RunMatch {
log_file_name, log_file_name,
players, players,
is_public, is_public,
map,
} }
} }
fn into_runner_config(self) -> runner::MatchConfig { fn into_runner_config(self) -> runner::MatchConfig {
runner::MatchConfig { runner::MatchConfig {
map_path: PathBuf::from(&self.config.maps_directory).join("hex.json"), map_path: PathBuf::from(&self.config.maps_directory).join(self.map.file_path),
map_name: "hex".to_string(), map_name: self.map.name,
log_path: PathBuf::from(&self.config.match_logs_directory).join(&self.log_file_name), log_path: PathBuf::from(&self.config.match_logs_directory).join(&self.log_file_name),
players: self players: self
.players .players
@ -88,6 +95,7 @@ impl RunMatch {
state: db::matches::MatchState::Playing, state: db::matches::MatchState::Playing,
log_path: &self.log_file_name, log_path: &self.log_file_name,
is_public: self.is_public, is_public: self.is_public,
map_id: Some(self.map.id),
}; };
let new_match_players = self let new_match_players = self
.players .players

View file

@ -1,4 +1,5 @@
use crate::db::bots::BotVersion; use crate::db::bots::BotVersion;
use crate::db::maps::Map;
use crate::{db::bots::Bot, DbPool, GlobalConfig}; use crate::{db::bots::Bot, DbPool, GlobalConfig};
use crate::db; use crate::db;
@ -15,6 +16,8 @@ use tokio;
const RANKER_INTERVAL: u64 = 60; const RANKER_INTERVAL: u64 = 60;
const RANKER_NUM_MATCHES: i64 = 10_000; const RANKER_NUM_MATCHES: i64 = 10_000;
const RANKER_MAP_NAME: &str = "hex";
pub async fn run_ranker(config: Arc<GlobalConfig>, db_pool: DbPool) { pub async fn run_ranker(config: Arc<GlobalConfig>, db_pool: DbPool) {
// TODO: make this configurable // TODO: make this configurable
// play at most one match every n seconds // play at most one match every n seconds
@ -25,7 +28,7 @@ pub async fn run_ranker(config: Arc<GlobalConfig>, db_pool: DbPool) {
.expect("could not get database connection"); .expect("could not get database connection");
loop { loop {
interval.tick().await; interval.tick().await;
let bots = db::bots::all_active_bots_with_version(&db_conn).unwrap(); let bots = db::bots::all_active_bots_with_version(&db_conn).expect("could not load bots");
if bots.len() < 2 { if bots.len() < 2 {
// not enough bots to play a match // not enough bots to play a match
continue; continue;
@ -34,13 +37,17 @@ pub async fn run_ranker(config: Arc<GlobalConfig>, db_pool: DbPool) {
let mut rng = &mut rand::thread_rng(); let mut rng = &mut rand::thread_rng();
bots.choose_multiple(&mut rng, 2).cloned().collect() bots.choose_multiple(&mut rng, 2).cloned().collect()
}; };
play_ranking_match(config.clone(), selected_bots, db_pool.clone()).await;
let map =
db::maps::find_map_by_name(RANKER_MAP_NAME, &db_conn).expect("could not load map");
play_ranking_match(config.clone(), map, selected_bots, db_pool.clone()).await;
recalculate_ratings(&db_conn).expect("could not recalculate ratings"); recalculate_ratings(&db_conn).expect("could not recalculate ratings");
} }
} }
async fn play_ranking_match( async fn play_ranking_match(
config: Arc<GlobalConfig>, config: Arc<GlobalConfig>,
map: Map,
selected_bots: Vec<(Bot, BotVersion)>, selected_bots: Vec<(Bot, BotVersion)>,
db_pool: DbPool, db_pool: DbPool,
) { ) {
@ -53,7 +60,7 @@ async fn play_ranking_match(
players.push(player); players.push(player);
} }
let (_, handle) = RunMatch::from_players(config, true, players) let (_, handle) = RunMatch::new(config, true, map, players)
.run(db_pool.clone()) .run(db_pool.clone())
.await .await
.expect("failed to run match"); .expect("failed to run match");

View file

@ -14,12 +14,13 @@ use serde::{Deserialize, Serialize};
use super::matches::ApiMatch; use super::matches::ApiMatch;
const DEFAULT_OPPONENT_NAME: &str = "simplebot"; const DEFAULT_OPPONENT_NAME: &str = "simplebot";
const DEFAULT_MAP_NAME: &str = "hex";
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct SubmitBotParams { pub struct SubmitBotParams {
pub code: String, pub code: String,
// TODO: would it be better to pass an ID here?
pub opponent_name: Option<String>, pub opponent_name: Option<String>,
pub map_name: Option<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -40,17 +41,24 @@ pub async fn submit_bot(
.opponent_name .opponent_name
.unwrap_or_else(|| DEFAULT_OPPONENT_NAME.to_string()); .unwrap_or_else(|| DEFAULT_OPPONENT_NAME.to_string());
let map_name = params
.map_name
.unwrap_or_else(|| DEFAULT_MAP_NAME.to_string());
let (opponent_bot, opponent_bot_version) = let (opponent_bot, opponent_bot_version) =
db::bots::find_bot_with_version_by_name(&opponent_name, &conn) db::bots::find_bot_with_version_by_name(&opponent_name, &conn)
.map_err(|_| StatusCode::BAD_REQUEST)?; .map_err(|_| StatusCode::BAD_REQUEST)?;
let map = db::maps::find_map_by_name(&map_name, &conn).map_err(|_| StatusCode::BAD_REQUEST)?;
let player_bot_version = save_code_string(&params.code, None, &conn, &config) let player_bot_version = save_code_string(&params.code, None, &conn, &config)
// TODO: can we recover from this? // TODO: can we recover from this?
.expect("could not save bot code"); .expect("could not save bot code");
let run_match = RunMatch::from_players( let run_match = RunMatch::new(
config, config,
false, false,
map.clone(),
vec![ vec![
MatchPlayer::BotVersion { MatchPlayer::BotVersion {
bot: None, bot: None,
@ -82,8 +90,7 @@ pub async fn submit_bot(
bot: Some(opponent_bot), bot: Some(opponent_bot),
}, },
], ],
// TODO! map: Some(map),
map: None,
}; };
let api_match = super::matches::match_data_to_api(full_match_data); let api_match = super::matches::match_data_to_api(full_match_data);