feat: record bot games results
This commit is contained in:
parent
592dfe52af
commit
203825e28f
8 changed files with 98 additions and 14 deletions
|
|
@ -342,7 +342,6 @@ a:hover { text-decoration: underline; }
|
|||
|
||||
.portal-danger-zone {
|
||||
border: 1px solid rgba(122, 30, 42, 0.4);
|
||||
background: rgba(122, 30, 42, 0.04);
|
||||
}
|
||||
.portal-danger-zone h2 {
|
||||
color: var(--ui-red-accent);
|
||||
|
|
|
|||
|
|
@ -188,6 +188,22 @@ pub async fn get_user_games(username: &str, page: i64) -> Result<GamesResponse,
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn submit_bot_game_result(result: String, outcome: String) -> Result<(), String> {
|
||||
let body = serde_json::json!({ "result": result, "outcome": outcome });
|
||||
let resp = gloo_net::http::Request::post(&url("/games/bot-result"))
|
||||
.credentials(web_sys::RequestCredentials::Include)
|
||||
.json(&body)
|
||||
.map_err(|e| e.to_string())?
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
if resp.status() == 200 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("status {}", resp.status()))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_game_detail(id: i64) -> Result<GameDetail, String> {
|
||||
let resp = gloo_net::http::Request::get(&url(&format!("/games/{id}")))
|
||||
.credentials(web_sys::RequestCredentials::Include)
|
||||
|
|
|
|||
|
|
@ -293,12 +293,19 @@ pub fn App() -> impl IntoView {
|
|||
pending,
|
||||
player_name.clone(),
|
||||
backend,
|
||||
auth_username,
|
||||
)
|
||||
.await
|
||||
}
|
||||
None => {
|
||||
run_local_bot_game(screen, &mut cmd_rx, pending, player_name.clone())
|
||||
.await
|
||||
run_local_bot_game(
|
||||
screen,
|
||||
&mut cmd_rx,
|
||||
pending,
|
||||
player_name.clone(),
|
||||
auth_username,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
if !restart {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use futures::channel::mpsc;
|
||||
use leptos::prelude::*;
|
||||
use leptos::task::spawn_local;
|
||||
|
||||
use backbone_lib::traits::{BackEndArchitecture, BackendCommand};
|
||||
|
||||
use crate::api;
|
||||
use crate::app::{GameUiState, NetCommand, PauseReason, Screen};
|
||||
use crate::game::trictrac::backend::TrictracBackend;
|
||||
use crate::game::trictrac::bot_local::bot_decide;
|
||||
|
|
@ -18,6 +20,7 @@ pub async fn run_local_bot_game(
|
|||
cmd_rx: &mut mpsc::UnboundedReceiver<NetCommand>,
|
||||
pending: RwSignal<VecDeque<GameUiState>>,
|
||||
player_name: String,
|
||||
auth_username: RwSignal<Option<String>>,
|
||||
) -> bool {
|
||||
let mut backend = TrictracBackend::new(0);
|
||||
backend.player_arrival(0);
|
||||
|
|
@ -49,7 +52,7 @@ pub async fn run_local_bot_game(
|
|||
suppress_dice_anim: false,
|
||||
}));
|
||||
|
||||
run_local_bot_game_loop(screen, cmd_rx, pending, player_name, backend, vs).await
|
||||
run_local_bot_game_loop(screen, cmd_rx, pending, player_name, backend, vs, auth_username).await
|
||||
}
|
||||
|
||||
/// Runs a bot game from a pre-built backend and initial ViewState (used for snapshot replay).
|
||||
|
|
@ -60,6 +63,7 @@ pub async fn run_local_bot_game_with_backend(
|
|||
pending: RwSignal<VecDeque<GameUiState>>,
|
||||
player_name: String,
|
||||
backend: TrictracBackend,
|
||||
auth_username: RwSignal<Option<String>>,
|
||||
) -> bool {
|
||||
let mut vs = backend.get_view_state().clone();
|
||||
patch_bot_names(&mut vs, &player_name);
|
||||
|
|
@ -76,7 +80,7 @@ pub async fn run_local_bot_game_with_backend(
|
|||
suppress_dice_anim: false,
|
||||
}));
|
||||
|
||||
run_local_bot_game_loop(screen, cmd_rx, pending, player_name, backend, vs).await
|
||||
run_local_bot_game_loop(screen, cmd_rx, pending, player_name, backend, vs, auth_username).await
|
||||
}
|
||||
|
||||
async fn run_local_bot_game_loop(
|
||||
|
|
@ -86,8 +90,10 @@ async fn run_local_bot_game_loop(
|
|||
player_name: String,
|
||||
mut backend: TrictracBackend,
|
||||
mut vs: ViewState,
|
||||
auth_username: RwSignal<Option<String>>,
|
||||
) -> bool {
|
||||
use futures::StreamExt;
|
||||
let mut result_submitted = false;
|
||||
loop {
|
||||
match cmd_rx.next().await {
|
||||
Some(NetCommand::Action(action)) => {
|
||||
|
|
@ -152,6 +158,19 @@ async fn run_local_bot_game_loop(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vs.stage == SerStage::Ended && !result_submitted {
|
||||
result_submitted = true;
|
||||
if let Some(_) = auth_username.get_untracked() {
|
||||
let scores = vs.scores.clone();
|
||||
spawn_local(async move {
|
||||
let (h0, h1) = (scores[0].holes, scores[1].holes);
|
||||
let outcome = if h0 > h1 { "win" } else if h0 < h1 { "loss" } else { "draw" };
|
||||
let result_str = format!("{} - {}", h0, h1);
|
||||
let _ = api::submit_bot_game_result(result_str, outcome.to_string()).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ fn GamesTable(games: Vec<GameSummary>, page: RwSignal<i64>) -> impl IntoView {
|
|||
{rows.into_iter().map(|g| {
|
||||
let started = api::format_ts(g.started_at, locale_tag, &api::DateFormatOptions::date_only());
|
||||
let ended = g.ended_at.map(|ts| api::format_ts(ts, locale_tag, &api::DateFormatOptions::date_only())).unwrap_or_else(|| "—".into());
|
||||
let room_display = if g.room_code == "bot" { "vs Bot".to_string() } else { g.room_code.clone() };
|
||||
let outcome_class = match g.outcome.as_deref() {
|
||||
Some("win") => "outcome-win",
|
||||
Some("loss") => "outcome-loss",
|
||||
|
|
@ -230,7 +231,7 @@ fn GamesTable(games: Vec<GameSummary>, page: RwSignal<i64>) -> impl IntoView {
|
|||
};
|
||||
view! {
|
||||
<tr>
|
||||
<td>{ g.room_code.clone() }</td>
|
||||
<td>{ room_display }</td>
|
||||
<td>{ started }</td>
|
||||
<td>{ ended }</td>
|
||||
<td class=outcome_class>{ outcome_text }</td>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue