package game import ( "fmt" "slices" ) // A WinCondition determines if there are winners using the current game state. type WinCondition interface { check(*LocalState) []*Player String() string } type winCondition struct { condition func(*LocalState) []*Player desc string } func (w *winCondition) check(s *LocalState) []*Player { return w.condition(s) } func (w *winCondition) String() string { return w.desc } // DummyWinCondition always return an empty winner slice. var DummyWinCondition = &winCondition{ condition: func(*LocalState) []*Player { return []*Player{} }, desc: "dummy wincondition", } func BossGame(name string) *winCondition { return &winCondition{ condition: func(s *LocalState) []*Player { bossFound := false s.FilterUnits(func(u *Unit) bool { if u.Card().Name == "The "+name { bossFound = true } return false }) var winners []*Player if bossFound { if winners = explicitWinners(s); len(winners) > 0 { return winners } return singleConcessionWinner(s) } // No Boss was found for _, p := range s.players { if p.Name != name && !p.Conceded && !slices.Contains(winners, p) { winners = append(winners, p) } } return winners }, desc: fmt.Sprintf("destroy the %s", name), } } // KingGame reports all players without an enemy king. var KingGame = &winCondition{ condition: func(s *LocalState) []*Player { foundKings := map[*Player]struct{}{} for _, u := range s.Units() { if u.card.Name != "King" { continue } foundKings[u.owner] = struct{}{} } var winners []*Player if len(foundKings) == len(s.Players()) { if winners = explicitWinners(s); len(winners) > 0 { return winners } return singleConcessionWinner(s) } nPlayers := len(s.Players()) loosers := make([]*Player, nPlayers) copy(loosers, s.Players()) for p := range foundKings { // Considere players who have conceded as loosers if p.Conceded { continue } i := slices.Index(loosers, p) loosers[i] = loosers[len(loosers)-1] loosers = loosers[:len(loosers)-1] } if len(loosers) == len(s.Players()) { return s.Players() } for _, p := range s.Players() { if !slices.Contains(loosers, p) && !slices.Contains(winners, p) { winners = append(winners, p) } } return winners }, desc: "Destroy the enemy King", } // DeathMatch returns the players without enemy units. var DeathMatch = &winCondition{ condition: func(s *LocalState) []*Player { winners := explicitWinners(s) for _, p := range s.players { if len(s.EnemyUnits(p)) == 0 && !slices.Contains(winners, p) { winners = append(winners, p) } } if len(winners) > 0 { return winners } return singleConcessionWinner(s) }, desc: "Destroy all enemy units", } // singleConcessionWinner returns the player left over after the rest conceded. func singleConcessionWinner(s *LocalState) []*Player { var winner *Player for _, p := range s.players { if p.Conceded { continue } if winner == nil { winner = p // Two players have not conceded yet } else { return nil } } return []*Player{winner} } // explicitWinners returns the players that explicitly won the game. // Winning the game is sometimes possible without fulfilling a maps win condition, // but by achieving some external condition (like Approach Supremacy!). func explicitWinners(s *LocalState) []*Player { winners := []*Player{} for _, p := range s.players { if p.Won { winners = append(winners, p) } } return winners }