mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 12:28:54 +00:00
The engine is transport-free: no sockets, no protobuf, and no clock of its own. Callers pass the current time in and read the deadline back, so every rule is testable without a timer. One goroutine owns a game. Validation resolves the word before checking the chain link. Roughly a third of dictionary aliases move the first syllable, so "sy hai" resolves to "si hai"; checking the link against what the player typed would reject legal moves. The used-word set is keyed on the canonical form, which also stops the same word being played twice under two spellings. An opening whose last syllable starts nothing is refused. It would hand the first player a game already lost, with no move and no reason, that resolves only when the turn timer expires and then reports a timeout. Three bot difficulties, separated by how far they look ahead rather than by how willing they are to win: random, one-ply greedy, and depth-limited negamax with alpha-beta. Strategies see a read-only view of the board, so a bot cannot bypass the same validation a human's move goes through. Simulation on the real corpus, alternating sides across 60 games per pairing: hard beats easy 98%, medium beats easy 87%, hard beats medium 65%. Decisions take at most 19ms against a 150ms budget. Three defects surfaced only under simulation. Withholding the winning move from the middle bot, as first designed, made it lose to the random bot 97% of the time. The search evaluated leaves with an inverted sign, so it hunted for positions where it was about to be trapped. And the rate limit on taking an instant win did nothing, because declining the shortcut let the search rediscover the same move. Games against the hard bot end after about three moves versus fifteen for two random bots: with 1,814 dead-end syllables an instant win is usually available. Tunable, and flagged for playtesting.