// Command holdem-train trains the casino's poker bots and writes the policy the
// table embeds.
//
//	go run ./cmd/holdem-train -iterations 5000000 -out internal/games/holdem/policy.gob
//
// It is not part of Pete. It runs when the engine's rules change, takes half an
// hour, and produces a file. Nothing at runtime imports it.
//
// The bots are trained heads-up, at every stack depth the table deals — that
// range is the point, because poker at twenty big blinds and poker at a hundred
// are different games and a bot that only knows one of them folds into the other.
// A six-handed table then reuses the same policy, which is an approximation, and
// a documented one.
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime"
	"time"

	"pete/internal/games/holdem"
)

func main() {
	iterations := flag.Int("iterations", 5_000_000, "hands to train on")
	workers := flag.Int("workers", runtime.NumCPU(), "parallel workers")
	out := flag.String("out", "internal/games/holdem/policy.gob", "where to write the policy")
	stakes := flag.String("stakes", "low", "which table's blinds to train at")
	minBB := flag.Int64("min-bb", 20, "shallowest stack, in big blinds")
	maxBB := flag.Int64("max-bb", 100, "deepest stack, in big blinds")
	seed := flag.Uint64("seed", 20260714, "seed, so a run can be repeated")
	flag.Parse()

	tier, err := holdem.TierBySlug(*stakes)
	if err != nil {
		log.Fatalf("no such table %q", *stakes)
	}

	fmt.Printf("training %s hands on %d workers — %s blinds, %d–%d BB deep\n",
		commas(*iterations), *workers, tier.Name, *minBB, *maxBB)

	started := time.Now()
	last := started
	policy := holdem.Train(*iterations, *workers, tier, *minBB, *maxBB, *seed, func(done int) {
		if time.Since(last) < 20*time.Second {
			return
		}
		last = time.Now()
		frac := float64(done) / float64(*iterations)
		if frac <= 0 {
			return
		}
		left := time.Duration(float64(time.Since(started)) * (1 - frac) / frac)
		fmt.Printf("  %s / %s hands (%.0f%%), about %s to go\n",
			commas(done), commas(*iterations), frac*100, left.Round(time.Second))
	})

	f, err := os.Create(*out)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if err := holdem.Save(f, policy); err != nil {
		log.Fatal(err)
	}

	info, _ := f.Stat()
	fmt.Printf("\ndone in %s: %s nodes, %.1f MB → %s\n",
		time.Since(started).Round(time.Second), commas(policy.Meta.Nodes),
		float64(info.Size())/(1<<20), *out)
}

func commas(n int) string {
	s := fmt.Sprint(n)
	if len(s) <= 3 {
		return s
	}
	var out []byte
	for i, c := range []byte(s) {
		if i > 0 && (len(s)-i)%3 == 0 {
			out = append(out, ',')
		}
		out = append(out, c)
	}
	return string(out)
}
