tetris-io 0.1.1

Terminal-based Tetris game built with Ratatui and Crossterm
Documentation
use std::time::Instant;

use crate::domain::command::Command;
use crate::domain::events::Event;
use crate::domain::rules::{RulesConfig, step};
use crate::domain::state::GameState;
use crate::domain::timing::clamp_dt_ms;

use super::flow::NetOutcome;
use super::session::GameSessionState;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SinglePlayerStep {
    Continue { events: Vec<Event> },
    Ended,
}

pub fn frame_dt_ms(last_frame: &mut Instant, now: Instant, max_dt_ms: u64) -> u64 {
    let dt = now.duration_since(*last_frame);
    *last_frame = now;
    clamp_dt_ms(dt.as_millis() as u64, max_dt_ms)
}

pub fn advance_single_player(
    session: &mut GameSessionState,
    now: Instant,
    cfg: RulesConfig,
    dt_ms: u64,
    commands: &[Command],
    soft_drop_active: bool,
) -> SinglePlayerStep {
    if session.apply_mode_end_if_reached(now).is_some() {
        return SinglePlayerStep::Ended;
    }

    let events = step(&mut session.state, cfg, dt_ms, commands, soft_drop_active);
    if events.iter().any(|e| matches!(e, Event::GameOver)) {
        session.game_over_time = Some(session.elapsed_secs(now));
        return SinglePlayerStep::Ended;
    }
    SinglePlayerStep::Continue { events }
}

pub fn advance_sim_state(
    state: &mut GameState,
    cfg: RulesConfig,
    dt_ms: u64,
    commands: &[Command],
    soft_drop_active: bool,
) -> Vec<Event> {
    step(state, cfg, dt_ms, commands, soft_drop_active)
}

pub fn compute_net_outcome(
    local_game_over: bool,
    remote_over: bool,
    remote_disconnect: bool,
) -> Option<NetOutcome> {
    if !local_game_over && !remote_over {
        return None;
    }
    let outcome = if remote_disconnect {
        NetOutcome::Disconnect
    } else if local_game_over && remote_over {
        NetOutcome::Draw
    } else if local_game_over {
        NetOutcome::Lose
    } else {
        NetOutcome::Win
    };
    Some(outcome)
}