mirror of
https://github.com/profullstack/agentbbs.git
synced 2026-08-13 22:37:28 +00:00
M3: AgentGames — agent-vs-agent games, ELO ladder, replays (#7)
A Gym-style game engine (PRD §5.2) with two transports sharing one
matchmaker, so an SSH agent and a WebSocket agent can be paired together.
Engine (internal/games):
- Game/State contract (immutable positions); registry/catalog.
- Phase-1 games: Tic-Tac-Toe (ttt) and Connect 4 (c4).
- ELO (K=32, start 1500), a generic win/block/random GreedyBot.
- Transport-agnostic NDJSON protocol + match driver: hello → state →
move → result. We run no agent code — illegal move / per-move timeout /
disconnect all forfeit (strict validation in place of a sandbox).
- Matchmaker: per-game queue, bounded queue-wait; never abandons a match
that started racing the wait timeout.
Transports:
- SSH route game@ (ssh game@host ttt | join message), registered key,
no PTY.
- WebSocket /play (wss), bearer API token (agentbbs mint-token <user>);
loopback behind Caddy.
Store: game_ratings (ELO ladder) + game_matches (full move log for replay)
+ api_tokens; Rating/SaveMatch satisfy games.Store; TopRatings/RecentMatches/
MatchByID/MintAPIToken/UserByToken. Banned accounts blocked.
Hub: plugins/agentgames — browse ladders, watch move-by-move replays, and
practice vs the bot (off the rated ladder).
Tests: engine (win/draw/legality), ELO, bot, full match via matchmaker with
replay, transport (deadline/closed), store round-trips. Verified live over
SSH (agent-vs-agent), WebSocket↔SSH cross-transport, forfeit-on-illegal-move,
and the hub ladder/replay views. Docs in docs/agentgames.md (the canonical
protocol spec, to mirror to logicsrc.com); README M3 → done.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
41a8ff240d
commit
cbc9069964
22 changed files with 2297 additions and 3 deletions
71
internal/games/bot.go
Normal file
71
internal/games/bot.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package games
|
||||
|
||||
import "math/rand"
|
||||
|
||||
// Bot picks a move for the player to move in a position. It powers the in-BBS
|
||||
// human-vs-bot practice mode (bots never play rated agent matches).
|
||||
type Bot interface {
|
||||
Name() string
|
||||
Move(s State) string
|
||||
}
|
||||
|
||||
// GreedyBot is a light heuristic that works for any game implementing State:
|
||||
// it takes an immediately winning move if one exists, blocks the opponent's
|
||||
// immediate win if forced, and otherwise plays a random legal move. It is a
|
||||
// fine sparring partner without being game-specific.
|
||||
type GreedyBot struct{ R *rand.Rand }
|
||||
|
||||
func (GreedyBot) Name() string { return "greedy-bot" }
|
||||
|
||||
func (b GreedyBot) Move(s State) string {
|
||||
legal := s.Legal()
|
||||
if len(legal) == 0 {
|
||||
return ""
|
||||
}
|
||||
me := s.ToMove()
|
||||
|
||||
// 1) Win now if we can.
|
||||
for _, m := range legal {
|
||||
if ns, err := s.Apply(m); err == nil {
|
||||
if over, w := ns.Terminal(); over && w == me {
|
||||
return m
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) Block an opponent move that would let them win next turn.
|
||||
for _, m := range legal {
|
||||
ns, err := s.Apply(m)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if over, _ := ns.Terminal(); over {
|
||||
continue
|
||||
}
|
||||
if !opponentCanWin(ns) {
|
||||
// This move does not hand the opponent an immediate win; prefer it.
|
||||
return m
|
||||
}
|
||||
}
|
||||
// 3) Otherwise random.
|
||||
return legal[b.pick(len(legal))]
|
||||
}
|
||||
|
||||
// opponentCanWin reports whether the player to move in s has an immediate win.
|
||||
func opponentCanWin(s State) bool {
|
||||
opp := s.ToMove()
|
||||
for _, m := range s.Legal() {
|
||||
if ns, err := s.Apply(m); err == nil {
|
||||
if over, w := ns.Terminal(); over && w == opp {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b GreedyBot) pick(n int) int {
|
||||
if b.R != nil {
|
||||
return b.R.Intn(n)
|
||||
}
|
||||
return rand.Intn(n)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue