use super::*;
pub(crate) fn fill_bot_actions(
board: &Board,
bots: &Bots,
actions: &mut [PlayerAction; MAX_PLAYERS],
) {
debug_assert_eq!(actions.len(), bots.0.len(), "a seat with no bot slot");
for (seat, action) in actions.iter_mut().enumerate() {
if let Some(level) = bots.0[seat]
&& matches!(action, PlayerAction::None)
{
*action = bot_action(board, seat as u8, level);
}
}
}
fn fill_online_bots(
board: &Board,
bots: &Bots,
abandoned: &[(u8, u32)],
level: BotLevel,
frame: u32,
actions: &mut [PlayerAction; MAX_PLAYERS],
) {
for (seat, action) in actions.iter_mut().enumerate() {
if let Some(level) = ai_holding(bots, abandoned, level, seat, frame)
&& matches!(action, PlayerAction::None)
{
*action = bot_action(board, seat as u8, level);
}
}
}
pub(super) fn ai_holding(
bots: &Bots,
abandoned: &[(u8, u32)],
level: BotLevel,
seat: usize,
frame: u32,
) -> Option<BotLevel> {
match abandoned
.iter()
.find(|(gone, _)| usize::from(*gone) == seat)
{
Some((_, at)) => (frame >= *at).then_some(level),
None => bots.0[seat],
}
}
#[allow(clippy::too_many_arguments)]
pub(in crate::app) fn advance_sim(
mut sim: ResMut<Sim>,
mut pending: ResMut<PendingActions>,
paused: Res<Paused>,
mut online: ResMut<net::Online>,
mut recorder: ResMut<Recorder>,
mut playback: ResMut<Playback>,
speed: Res<replays::PlaybackSpeed>,
bots: Res<Bots>,
mut tally: ResMut<awards::RoundTally>,
) {
if paused.0 {
return;
}
if let Some((replay, idx)) = &mut playback.0 {
for _ in 0..speed.0.max(1) {
let Some(actions) = replay.inputs.get(*idx).copied() else {
break;
};
*idx += 1;
let before = awards::Reading::of(&sim.0);
sim.0.tick(&actions);
tally.observe(before, &sim.0, &actions);
}
return;
}
if let Some(session) = &mut online.0 {
let local = session.session.seat().map(usize::from);
let call = session.stands.pending_call;
let action = match call {
Some(event) => PlayerAction::CallEvent(event),
None => local.map_or(PlayerAction::None, |seat| pending.0[seat]),
};
let sim_board = &mut sim.bypass_change_detection().0;
let recording = &mut recorder.bypass_change_detection().0;
let tally = &mut *tally;
let bots = &*bots;
let level = {
use crate::app::cycle::Cycle;
BotLevel::from_index(usize::from(session.terms.bot_level))
};
let mut advanced = false;
let committed = session.pump(action, |net| {
if let Some(mut frame_actions) = net.session.advance() {
let at = net.session.frame().saturating_sub(1);
fill_online_bots(
sim_board,
bots,
&net.abandoned,
level,
at,
&mut frame_actions,
);
let before = awards::Reading::of(sim_board);
sim_board.tick(&frame_actions);
tally.observe(before, sim_board, &frame_actions);
debug_assert!(
sim_board.round_over() || sim_board.ticks() == u64::from(net.session.frame()),
"board at tick {} for frame {}",
sim_board.ticks(),
net.session.frame()
);
if let Some(replay) = recording {
replay.record(frame_actions);
debug_assert!(
sim_board.round_over() || replay.inputs.len() as u64 == sim_board.ticks(),
"a recording out of step with its round"
);
}
net.after_frame(sim_board.state_hash());
advanced = true;
}
});
session.send_catch_ups(&sim.bypass_change_detection().0);
if advanced {
sim.set_changed();
recorder.set_changed();
}
if call.is_some() {
if committed {
session.stands.pending_call = None;
}
} else if let Some(seat) = local
&& (committed || session.session.paused())
{
pending.0[seat] = PlayerAction::None;
}
return;
}
let mut actions = std::mem::take(&mut pending.0);
fill_bot_actions(&sim.0, &bots, &mut actions);
let before = awards::Reading::of(&sim.0);
sim.0.tick(&actions);
tally.observe(before, &sim.0, &actions);
if let Some(replay) = &mut recorder.0 {
replay.record(actions);
debug_assert!(
sim.0.round_over() || replay.inputs.len() as u64 == sim.0.ticks(),
"a recording out of step with its round"
);
}
}