Stage 5: robot opponent (pool, seed-derived strategy, move driver, matchmaker substitution)
- internal/robot: durable kind='robot' account pool (migration 00004); every per-game and per-turn choice derived deterministically from the game seed (restart-stable FNV mix); a background move driver; margin targeting (band 1-30, closest-to-band); right-skewed [2,90]min delays (median ~10m); opponent-anchored sleep with +/-3h drift; daytime nudge reply + proactive 12h nudge; friend/chat blocked via profile toggles. - engine.Candidates (decoded ranked plays); game.Candidates + RobotTurns; social.LastNudgeAt. - matchmaker: 10s wait then robot substitution (reaper) + Poll delivery seam. - config (BACKEND_ROBOT_DRIVE_INTERVAL, BACKEND_LOBBY_ROBOT_WAIT, BACKEND_LOBBY_REAPER_INTERVAL); main wiring + boot-time pool provisioning. - metrics: robot account_stats (authoritative balance) + robot_games_finished_total OTel counter + per-finish log. - docs: PLAN, ARCHITECTURE, FUNCTIONAL(+ru), TESTING, README; account.go comment. - tests: robot strategy units, matchmaker reaper/Poll, engine.Candidates; inttest robot full-game / substitution / proactive-nudge.
This commit is contained in:
@@ -23,6 +23,7 @@ import (
|
||||
"scrabble/backend/internal/game"
|
||||
"scrabble/backend/internal/lobby"
|
||||
"scrabble/backend/internal/postgres"
|
||||
"scrabble/backend/internal/robot"
|
||||
"scrabble/backend/internal/server"
|
||||
"scrabble/backend/internal/session"
|
||||
"scrabble/backend/internal/social"
|
||||
@@ -54,7 +55,8 @@ func main() {
|
||||
|
||||
// run wires the process dependencies in order — telemetry, database (with
|
||||
// migrations), engine dictionaries, session cache, game domain (with its
|
||||
// turn-timeout sweeper), HTTP server — and blocks until ctx is cancelled.
|
||||
// turn-timeout sweeper), the robot opponent (pool + move driver) and the
|
||||
// matchmaking reaper, HTTP server — and blocks until ctx is cancelled.
|
||||
func run(ctx context.Context, cfg config.Config, logger *zap.Logger) error {
|
||||
tel, err := telemetry.New(ctx, cfg.Telemetry)
|
||||
if err != nil {
|
||||
@@ -103,15 +105,27 @@ func run(ctx context.Context, cfg config.Config, logger *zap.Logger) error {
|
||||
logger.Info("game turn-timeout sweeper started",
|
||||
zap.Duration("interval", cfg.Game.TimeoutSweepInterval))
|
||||
|
||||
// Stage 4 lobby & social domains. They have no active driver yet — their REST
|
||||
// and stream surface is added with the gateway in Stage 6 — so they are handed
|
||||
// to the server (like the route groups) for the handlers to come.
|
||||
// Stage 4 lobby & social domains. Their REST and stream surface is added with
|
||||
// the gateway in Stage 6, so they are handed to the server (like the route
|
||||
// groups) for the handlers to come.
|
||||
mailer := newMailer(cfg.SMTP, logger)
|
||||
emails := account.NewEmailService(accounts, mailer)
|
||||
socialSvc := social.NewService(social.NewStore(db), accounts, games)
|
||||
matchmaker := lobby.NewMatchmaker(games)
|
||||
|
||||
// Stage 5 robot opponent: provision its durable account pool (a hard startup
|
||||
// dependency, like the dictionaries) and start its move driver. The matchmaker
|
||||
// substitutes a pooled robot for a missing human after the wait window.
|
||||
robots := robot.NewService(games, accounts, socialSvc, tel.MeterProvider().Meter("scrabble/backend/robot"), logger)
|
||||
if err := robots.EnsurePool(ctx); err != nil {
|
||||
return fmt.Errorf("provision robot pool: %w", err)
|
||||
}
|
||||
go robots.Run(ctx, cfg.Robot.DriveInterval)
|
||||
logger.Info("robot driver started", zap.Duration("interval", cfg.Robot.DriveInterval))
|
||||
|
||||
matchmaker := lobby.NewMatchmaker(games, robots, cfg.Lobby.RobotWait, logger)
|
||||
go matchmaker.RunReaper(ctx, cfg.Lobby.ReaperInterval)
|
||||
invitations := lobby.NewInvitationService(lobby.NewStore(db), games, accounts, socialSvc)
|
||||
logger.Info("lobby and social domains ready")
|
||||
logger.Info("lobby and social domains ready", zap.Duration("robot_wait", cfg.Lobby.RobotWait))
|
||||
|
||||
srv := server.New(cfg.HTTPAddr, server.Deps{
|
||||
Logger: logger,
|
||||
|
||||
Reference in New Issue
Block a user