feat(acb-evolver): add weekly automated map evolution ticker

Wire up the acb-map-evolver to run automatically on a weekly schedule
(Sunday 03:00 UTC by default) from the evolver deployment.

The map evolution ticker:
- Waits until the next scheduled time (weekday:hour:minute UTC)
- Runs acb-map-evolver --once to evolve maps for all player counts
- Repeats every 7 days

The schedule can be configured via ACB_MAP_EVOLUTION_SCHEDULE env var
(format: WEEKDAY:HH:MM, e.g., "0:03:00" for Sunday 03:00 UTC).

Enable via ACB_MAP_EVOLUTION_ENABLED=true or --enable-map-evolution flag.

Per plan §14.6: the weekly map evolution loads engagement scores,
runs MAP-Elites evolution, promotes high-scoring variants, and updates
the active map pool in the database.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-05-08 09:26:38 -04:00
parent b31c306013
commit 5242d6037c
3 changed files with 24 additions and 7 deletions

View file

@ -1 +1 @@
e1b21309cc6d91f6ccbfca96cdd965ab924a027f
b31c306013e5d99cdf1224c14c7ff887139f187d

View file

@ -27,9 +27,15 @@ COPY bots/ ./bots/
# Copy evolver source
COPY cmd/acb-evolver/ ./cmd/acb-evolver/
# Build the binary
# Copy map evolver source
COPY cmd/acb-map-evolver/ ./cmd/acb-map-evolver/
# Build the evolver binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /acb-evolver ./cmd/acb-evolver
# Build the map evolver binary
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /acb-map-evolver ./cmd/acb-map-evolver
# Runtime stage — needs language runtimes for validation sandbox
FROM golang:1.25-alpine
@ -52,8 +58,9 @@ RUN apk --no-cache add \
# Install TypeScript compiler globally
RUN npm install -g typescript ts-node
# Copy binary from builder
# Copy binaries from builder
COPY --from=builder /acb-evolver /app/acb-evolver
COPY --from=builder /acb-map-evolver /app/acb-map-evolver
# Create non-root user
RUN addgroup -g 1000 acb && adduser -D -u 1000 -G acb acb

View file

@ -906,15 +906,25 @@ func nextMapEvolutionTime(schedule WeeklySchedule) time.Time {
// runMapEvolution executes the map evolution by running the acb-map-evolver binary
// with the --once flag to trigger a single evolution run for all player counts.
func runMapEvolution(ctx context.Context, db *sql.DB, verbose bool) error {
// Check if acb-map-evolver binary is available
cmd := exec.CommandContext(ctx, "acb-map-evolver", "--once")
cmd.Env = append(os.Environ(),
// Path to acb-map-evolver binary (built into same container)
const mapEvolverBin = "/app/acb-map-evolver"
// Verify binary exists
if _, err := os.Stat(mapEvolverBin); err != nil {
return fmt.Errorf("acb-map-evolver binary not found at %s: %w", mapEvolverBin, err)
}
// Prepare environment with database URL
cmdEnv := append(os.Environ(),
fmt.Sprintf("ACB_DATABASE_URL=%s", os.Getenv("ACB_DATABASE_URL")),
)
cmd := exec.CommandContext(ctx, mapEvolverBin, "--once")
cmd.Env = cmdEnv
if verbose {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
log.Printf("map evolution: executing acb-map-evolver --once")
log.Printf("map evolution: executing %s --once", mapEvolverBin)
}
output, err := cmd.CombinedOutput()