sots-engine/tests/game_ai/CMakeLists.txt
alex 98257b9e82 RB: sots_turn --turn-commands replays a recorded command stream, and ModCount closes
The milestone needs a second input. The AI is a client, not part of the sim: it decides once,
on one machine, and its decisions reach the server as commands. A save carries the board and
half the input, which is why our turn wrote ModCount 14 where the original writes 24 -- the
missing ten ARE the turn's command stream.

  * `game/ai/apply_order` -- the thirty-step schedule the original drains a batch in: twenty-
    seven per-LIST steps (every player's elements of one list before the next list starts) with
    three per-PLAYER gate loops spliced in at step 10, 29 and 30. Neither list order nor member
    order, and both facts are asserted so a port that sorted cannot pass.
  * `game/ai/command_capture` -- a `.tcb` recorded turn: gates, list lengths, elements in wire
    order, per-client seeds, and `?` for a field the instrument could not read. An element count
    that disagrees with its declaration is REJECTED, because a counter quietly one short is
    indistinguishable from a turn that issued one fewer command.
  * `app/command_replay` -- applies it before the drivers, where the End-Turn dispatcher does.
    Every command is CHARGED; only the ones whose subsystem we hold are APPLIED; the rest are
    declined with the named gap, or marked incomplete when the capture itself lacks the payload.
  * `--turn-commands`, `--replay-count-only`, `--replay-recorded-names`, `--ai-seed`.

Measured on a fresh build directory, canonical pair turn2-state -> turn3-state:
  108 -> 62, closed 46, regressed 0  (was 108 -> 63, closed 45) -- /Sim/ModCount now reads the
  original's 24, decomposed as 2 drivers + 4 research-rate gates + build + rates + list 10 +
  two list-14 + fleet move, with the list-23 population element free.

turn1-state replayed against the SAME run's autosave closes 7 (ModCount and all six research
leaves); against the historical turn2-state it closes 6 and leaves player 512's research pick
diverging -- which is correct, because that recording is from a process that picked differently.

One prediction was falsified and it paid for itself: the first run regressed two leaves because
the rates element's MEMORY field order is not its wire order. The converter no longer claims a
mapping it cannot support.

Two new addresses (the second and third gate-loop heads) via ghidra/addresses.d/lane-rb.json;
header regenerated, never hand-resolved.
2026-09-08 18:57:46 -04:00

45 lines
2.9 KiB
CMake

# game/ai tests: the task vocabulary, the priority table read off the original, and the ranking.
add_executable(game_ai_test_tasks test_tasks.cpp)
target_link_libraries(game_ai_test_tasks PRIVATE sots_game_ai)
target_include_directories(game_ai_test_tasks PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_tasks PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_tasks COMMAND game_ai_test_tasks)
# The AI stepping order and the two-pass model: which AI players run, in what order, and which
# pass may write commands. Pure data -- the ModCount sequence, computed offline.
add_executable(game_ai_test_turn_order test_turn_order.cpp)
target_link_libraries(game_ai_test_turn_order PRIVATE sots_game_ai)
target_include_directories(game_ai_test_turn_order PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_turn_order PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_turn_order COMMAND game_ai_test_turn_order)
# The command block, the order API and the modification counter's arithmetic: what an order costs,
# which lists cost nothing, and the two reconstructions of the reference turn.
add_executable(game_ai_test_orders test_orders.cpp)
target_link_libraries(game_ai_test_orders PRIVATE sots_game_ai)
target_include_directories(game_ai_test_orders PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_orders PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_orders COMMAND game_ai_test_orders)
# The turn's phase list and the two-pass task walk: the dead tail after the submit, the one
# species-restricted phase, and the first pass writing nothing.
add_executable(game_ai_test_agent test_agent.cpp)
target_link_libraries(game_ai_test_agent PRIVATE sots_game_ai)
target_include_directories(game_ai_test_agent PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_agent PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_agent COMMAND game_ai_test_agent)
# The order the server applies a turn's commands in: twenty-seven per-list steps with three
# per-player gate loops spliced in, which is neither list order nor member order.
add_executable(game_ai_test_apply_order test_apply_order.cpp)
target_link_libraries(game_ai_test_apply_order PRIVATE sots_game_ai)
target_include_directories(game_ai_test_apply_order PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_apply_order PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_apply_order COMMAND game_ai_test_apply_order)
# The recorded-turn capture format and the two real turns that exist in it.
add_executable(game_ai_test_command_capture test_command_capture.cpp)
target_link_libraries(game_ai_test_command_capture PRIVATE sots_game_ai)
target_include_directories(game_ai_test_command_capture PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_options(game_ai_test_command_capture PRIVATE -Wall -Wextra -pedantic)
add_test(NAME game_ai_command_capture COMMAND game_ai_test_command_capture)