package football import ( "fmt" "net/http" "toolbox/pkg/base" "toolbox/pkg/football/logic" "github.com/gin-gonic/gin" ) type footballTool struct{} func init() { base.Register(&footballTool{}) } func (t *footballTool) ID() string { return "football" } func (t *footballTool) Name() string { return "足球概率模拟" } func (t *footballTool) Description() string { return "使用蒙特卡洛算法模拟2026年美加墨世界杯小组赛阶段的出线概率" } func (t *footballTool) Emoji() string { return "⚽" } func (t *footballTool) Init() error { fmt.Println("Initializing Football Tool...") return nil } func (t *footballTool) RegisterRoutes(r *gin.RouterGroup) { r.GET("/defaults", t.handleDefaults) r.POST("/simulate", t.handleSimulate) } type SimulateRequest struct { Groups map[string][]string `json:"groups"` CompletedMatches []logic.Match `json:"completed_matches"` ScoreOutcomes []logic.ScoreOutcome `json:"score_outcomes"` Simulations int `json:"simulations"` } func (t *footballTool) handleDefaults(c *gin.Context) { defaultGroups := map[string][]string{ "A": {"MEX", "RSA", "KOR", "CZE"}, "B": {"CAN", "QAT", "SUI", "BIH"}, "C": {"BRA", "MAR", "HAI", "SCO"}, "D": {"USA", "PAR", "AUS", "TUR"}, "E": {"GER", "CUW", "CIV", "ECU"}, "F": {"NED", "JPN", "TUN", "SWE"}, "G": {"BEL", "EGY", "IRN", "NZL"}, "H": {"ESP", "KSA", "URU", "CPV"}, "I": {"FRA", "SEN", "IRQ", "NOR"}, "J": {"ARG", "ALG", "AUT", "JOR"}, "K": {"POR", "COL", "UZB", "COD"}, "L": {"ENG", "CRO", "GHA", "PAN"}, } defaultMatches := []logic.Match{ // === ROUND 1 === {TeamA: "MEX", TeamB: "RSA", IsCompleted: true, ScoreA: 2, ScoreB: 0}, // Group A {TeamA: "KOR", TeamB: "CZE", IsCompleted: true, ScoreA: 2, ScoreB: 1}, // Group A {TeamA: "CAN", TeamB: "BIH", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group B {TeamA: "USA", TeamB: "PAR", IsCompleted: true, ScoreA: 4, ScoreB: 1}, // Group D {TeamA: "QAT", TeamB: "SUI", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group B {TeamA: "BRA", TeamB: "MAR", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group C {TeamA: "SCO", TeamB: "HAI", IsCompleted: true, ScoreA: 1, ScoreB: 0}, // Group C {TeamA: "AUS", TeamB: "TUR", IsCompleted: true, ScoreA: 2, ScoreB: 0}, // Group D {TeamA: "GER", TeamB: "CUW", IsCompleted: true, ScoreA: 7, ScoreB: 1}, // Group E {TeamA: "NED", TeamB: "JPN", IsCompleted: true, ScoreA: 2, ScoreB: 2}, // Group F {TeamA: "CIV", TeamB: "ECU", IsCompleted: true, ScoreA: 1, ScoreB: 0}, // Group E {TeamA: "SWE", TeamB: "TUN", IsCompleted: true, ScoreA: 5, ScoreB: 1}, // Group F {TeamA: "ESP", TeamB: "CPV", IsCompleted: true, ScoreA: 0, ScoreB: 0}, // Group H {TeamA: "BEL", TeamB: "EGY", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group G {TeamA: "KSA", TeamB: "URU", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group H {TeamA: "IRN", TeamB: "NZL", IsCompleted: true, ScoreA: 2, ScoreB: 2}, // Group G {TeamA: "FRA", TeamB: "SEN", IsCompleted: true, ScoreA: 3, ScoreB: 1}, // Group I {TeamA: "IRQ", TeamB: "NOR", IsCompleted: true, ScoreA: 1, ScoreB: 4}, // Group I {TeamA: "ARG", TeamB: "ALG", IsCompleted: true, ScoreA: 3, ScoreB: 0}, // Group J {TeamA: "AUT", TeamB: "JOR", IsCompleted: true, ScoreA: 3, ScoreB: 1}, // Group J {TeamA: "POR", TeamB: "COD", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group K {TeamA: "ENG", TeamB: "CRO", IsCompleted: true, ScoreA: 4, ScoreB: 2}, // Group L {TeamA: "GHA", TeamB: "PAN", IsCompleted: true, ScoreA: 1, ScoreB: 0}, // Group L {TeamA: "UZB", TeamB: "COL", IsCompleted: true, ScoreA: 1, ScoreB: 3}, // Group K // === ROUND 2 === {TeamA: "CZE", TeamB: "RSA", IsCompleted: true, ScoreA: 1, ScoreB: 1}, // Group A {TeamA: "SUI", TeamB: "BIH", IsCompleted: true, ScoreA: 4, ScoreB: 1}, // Group B {TeamA: "CAN", TeamB: "QAT", IsCompleted: true, ScoreA: 6, ScoreB: 0}, // Group B {TeamA: "MEX", TeamB: "KOR", IsCompleted: true, ScoreA: 1, ScoreB: 0}, // Group A {TeamA: "USA", TeamB: "AUS", IsCompleted: true, ScoreA: 2, ScoreB: 0}, // Group D {TeamA: "SCO", TeamB: "MAR", IsCompleted: false, ScoreA: 0, ScoreB: 1}, // Group C {TeamA: "BRA", TeamB: "HAI", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group C {TeamA: "TUR", TeamB: "PAR", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group D {TeamA: "NED", TeamB: "SWE", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group F {TeamA: "GER", TeamB: "CIV", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group E {TeamA: "ECU", TeamB: "CUW", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group E {TeamA: "TUN", TeamB: "JPN", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group F {TeamA: "ESP", TeamB: "KSA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group H {TeamA: "BEL", TeamB: "IRN", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group G {TeamA: "URU", TeamB: "CPV", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group H {TeamA: "NZL", TeamB: "EGY", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group G {TeamA: "ARG", TeamB: "AUT", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group J {TeamA: "FRA", TeamB: "IRQ", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group I {TeamA: "NOR", TeamB: "SEN", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group I {TeamA: "JOR", TeamB: "ALG", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group J {TeamA: "POR", TeamB: "UZB", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group K {TeamA: "ENG", TeamB: "GHA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group L {TeamA: "PAN", TeamB: "CRO", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group L {TeamA: "COL", TeamB: "COD", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group K // === ROUND 3 === {TeamA: "SUI", TeamB: "CAN", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group B {TeamA: "BIH", TeamB: "QAT", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group B {TeamA: "SCO", TeamB: "BRA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group C {TeamA: "MAR", TeamB: "HAI", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group C {TeamA: "CZE", TeamB: "MEX", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group A {TeamA: "RSA", TeamB: "KOR", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group A {TeamA: "CUW", TeamB: "CIV", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group E {TeamA: "ECU", TeamB: "GER", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group E {TeamA: "JPN", TeamB: "SWE", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group F {TeamA: "TUN", TeamB: "NED", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group F {TeamA: "TUR", TeamB: "USA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group D {TeamA: "PAR", TeamB: "AUS", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group D {TeamA: "NOR", TeamB: "FRA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group I {TeamA: "SEN", TeamB: "IRQ", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group I {TeamA: "CPV", TeamB: "KSA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group H {TeamA: "URU", TeamB: "ESP", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group H {TeamA: "EGY", TeamB: "IRN", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group G {TeamA: "NZL", TeamB: "BEL", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group G {TeamA: "PAN", TeamB: "ENG", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group L {TeamA: "CRO", TeamB: "GHA", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group L {TeamA: "COL", TeamB: "POR", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group K {TeamA: "COD", TeamB: "UZB", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group K {TeamA: "ALG", TeamB: "AUT", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group J {TeamA: "JOR", TeamB: "ARG", IsCompleted: false, ScoreA: 0, ScoreB: 0}, // Group J } c.JSON(http.StatusOK, gin.H{ "groups": defaultGroups, "completed_matches": defaultMatches, "score_outcomes": logic.DefaultScoreOutcomes(), }) } func (t *footballTool) handleSimulate(c *gin.Context) { var req SimulateRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if len(req.Groups) == 0 { c.JSON(http.StatusBadRequest, gin.H{"error": "Groups map cannot be empty"}) return } // Reconstruct Teams var teams []logic.Team for grp, grpTeams := range req.Groups { for _, teamName := range grpTeams { teams = append(teams, logic.Team{Name: teamName, Group: grp}) } } // Generate all group matches (completed + uncompleted) var allMatches []logic.Match for _, grpTeams := range req.Groups { for i := 0; i < len(grpTeams); i++ { for j := i + 1; j < len(grpTeams); j++ { tA := grpTeams[i] tB := grpTeams[j] // Check if this match is completed in input isCompleted := false scoreA, scoreB := 0, 0 for _, cm := range req.CompletedMatches { if !cm.IsCompleted { continue } if cm.TeamA == tA && cm.TeamB == tB { isCompleted = true scoreA = cm.ScoreA scoreB = cm.ScoreB break } else if cm.TeamA == tB && cm.TeamB == tA { isCompleted = true scoreA = cm.ScoreB scoreB = cm.ScoreA break } } allMatches = append(allMatches, logic.Match{ TeamA: tA, TeamB: tB, IsCompleted: isCompleted, ScoreA: scoreA, ScoreB: scoreB, }) } } } sims := req.Simulations if sims <= 0 { sims = 50000 } results := logic.RunMonteCarlo(teams, allMatches, req.ScoreOutcomes, sims) c.JSON(http.StatusOK, results) }