Something went wrong. Try again.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163package sim
import "core:testing"
@(test)deterministic_replay_matches_original_run :: proc(t: ^testing.T) { history: Run_History run_history_init(&history, run_id = 7, seed = 12345) defer run_history_destroy(&history)
state: Sim_State sim_init(&state, history.seed, history.initial_state)
commands := [?]Input_Command{ {move = .E}, {move = .E}, {move = .N}, {move = .NE}, {move = .SW}, {}, {move = .S, attack = true}, }
for command in commands { recorded := run_history_record_tick(&history, &state, command) testing.expect(t, recorded, "command history should align one command per tick") sim_tick(&state, command) }
// The original run records only commands. The digest is derived after the fact // from canonical sim fields, proving replay without making positions authority. original_digest := sim_digest(&state)
replay_state: Sim_State replay_digest := replay_history(&history, &replay_state)
state_player := world_player(&state.world) replay_player := world_player(&replay_state.world) // Replay must land on the same tick, command count, digest, and explicit player // state when initialized from the same seed and fed the same command stream. testing.expect_value(t, replay_state.tick, state.tick) testing.expect_value(t, run_history_command_count(&history), state.tick) testing.expect_value(t, replay_digest, original_digest) testing.expect(t, state_player != nil && replay_player != nil, "player actors should exist") testing.expect_value(t, i32(replay_player.position.x), i32(state_player.position.x)) testing.expect_value(t, i32(replay_player.position.y), i32(state_player.position.y)) testing.expect_value(t, i32(state_player.position.x), i32(PLAYER_MOVE_CARDINAL) * 2) testing.expect_value(t, i32(state_player.position.y), i32(fixed_from_whole(0)))}
@(test)digital_diagonal_movement_uses_precomputed_normalized_delta :: proc(t: ^testing.T) { state: Sim_State sim_init(&state)
// Diagonal digital input should normalize before it reaches sim_tick. The tick // path then uses a precomputed fixed-point delta instead of sqrt/division work. command := input_command_from_digital(false, true, true, false) sim_tick(&state, command) player := world_player(&state.world)
testing.expect_value(t, command.move, Move_Direction.NE) testing.expect(t, player != nil, "player actor should exist") testing.expect_value(t, i32(player.position.x), i32(PLAYER_MOVE_DIAGONAL)) testing.expect_value(t, i32(player.position.y), i32(-PLAYER_MOVE_DIAGONAL))}
@(test)history_rejects_missing_command_ticks :: proc(t: ^testing.T) { history: Run_History run_history_init(&history) defer run_history_destroy(&history)
state: Sim_State sim_init(&state) sim_tick(&state, {})
// Recording is tick-indexed: skipping a command for tick 0 makes the history // invalid rather than silently inserting a late command at tick 1. recorded := run_history_record_tick(&history, &state, {}) testing.expect(t, !recorded, "history should not accept a command after the tick was skipped") testing.expect_value(t, run_history_command_count(&history), u64(0))}
@(test)physical_echo_replays_archived_commands_inside_live_world :: proc(t: ^testing.T) { session: Run_Session run_session_init(&session, seed = 12345) defer run_session_destroy(&session)
prior_commands := [?]Input_Command{{move = .E}, {move = .E}, {move = .N}} for command in prior_commands { testing.expect(t, run_session_tick(&session, command), "prior run should record") } testing.expect(t, run_session_complete_and_restart(&session), "run should archive and restart")
testing.expect_value(t, len(session.archive.histories), 1) testing.expect_value(t, session.current_run_id, u32(2)) testing.expect_value(t, session.sim.tick, u64(0)) testing.expect_value(t, session.sim.world.actor_count, u32(2)) testing.expect_value(t, run_archive_command_count_for_run(&session.archive, 1), u64(len(prior_commands)))
echo := first_echo(&session.sim.world) testing.expect(t, echo != nil, "restart should spawn an echo actor") testing.expect_value(t, echo.echo.source_run_id, u32(1)) testing.expect_value(t, echo.echo.replay_tick, u64(0)) testing.expect_value(t, echo.echo.mode, Echo_Replay_Mode.Following)
testing.expect(t, run_session_tick(&session, {move = .S}), "live run should tick with echo") player := world_player(&session.sim.world) echo = first_echo(&session.sim.world) testing.expect(t, player != nil && echo != nil, "player and echo should coexist") testing.expect_value(t, i32(player.position.y), i32(PLAYER_MOVE_CARDINAL)) testing.expect_value(t, i32(echo.position.x), i32(PLAYER_MOVE_CARDINAL)) testing.expect_value(t, echo.echo.replay_tick, u64(1)) testing.expect_value(t, echo.echo.mode, Echo_Replay_Mode.Following)
testing.expect(t, run_session_tick(&session, {}), "echo should consume second command") testing.expect(t, run_session_tick(&session, {}), "echo should consume final command") echo = first_echo(&session.sim.world) testing.expect(t, echo != nil, "echo should remain inspectable after timeline end") testing.expect_value(t, i32(echo.position.x), i32(PLAYER_MOVE_CARDINAL) * 2) testing.expect_value(t, i32(echo.position.y), i32(-PLAYER_MOVE_CARDINAL)) testing.expect_value(t, echo.echo.replay_tick, u64(len(prior_commands))) testing.expect_value(t, echo.echo.mode, Echo_Replay_Mode.Timeline_Ended) testing.expect_value(t, run_archive_command_count_for_run(&session.archive, 1), u64(len(prior_commands))) testing.expect_value(t, run_history_command_count(&session.current_history), u64(3))}
@(test)echo_replay_digest_is_stable_for_same_archive_and_live_commands :: proc(t: ^testing.T) { first_digest := echo_replay_digest_fixture() second_digest := echo_replay_digest_fixture() testing.expect_value(t, second_digest, first_digest)}
first_echo :: proc(world: ^World_State) -> ^Actor_State { for index in 0..<world.actor_count { actor := &world.actors[index] if actor.kind == .Echo { return actor } } return nil}
echo_replay_digest_fixture :: proc() -> u64 { session: Run_Session run_session_init(&session, seed = 333) defer run_session_destroy(&session)
prior_commands := [?]Input_Command{{move = .E}, {move = .N}, {move = .NE}, {move = .S}} for command in prior_commands { run_session_tick(&session, command) } run_session_complete_and_restart(&session) live_commands := [?]Input_Command{{move = .S}, {}, {move = .W}, {}} for command in live_commands { run_session_tick(&session, command) } return sim_digest(&session.sim)}