package game import ( "log" "strings" "sync" ) const ( KRAKEN_NAME = "Kraken" ) var KrakenDecklist = `2 kraken/angry_squid 2 kraken/chachalot 2 kraken/clownfish 2 kraken/deja_vu! 2 kraken/devour_the_poor! 2 kraken/dolphin 2 kraken/flying_dutchmen 2 kraken/frost_pylon 2 kraken/gigantic_hail 2 kraken/sailfish 2 kraken/sold_out! 2 kraken/suprise! 2 kraken/tentacle_slap 2 kraken/tides_change! 2 kraken/unholy_cannonball` func NewKraken(id int, s *State) *Player { kraken := NewPlayer(id, KRAKEN_NAME, NewDeck(), s) d := NewDeckFromDeckList(KrakenDecklist) d.Shuffle(s.Rand) kraken.Deck = d kraken.DrawPerTurn = 0 kraken_tile := s.Map.FilterTiles(func(t *Tile) bool { return t.Raw == "the kraken" }) if len(kraken_tile) != 1 { log.Fatal("No kraken tile found in Map") } s.addPermanent(NewUnitFromPath("kraken/the_kraken", kraken_tile[0], kraken)) addKrakenControl(kraken) return kraken } func (s *State) AddKraken() { kraken := NewKraken(len(s.Players)+1, s) s.Players = append(s.Players, kraken) } type KrakenControl struct { kraken *Player actions chan Action syncGameState *sync.WaitGroup turn int activeSuprise bool } func addKrakenControl(kraken *Player) { ctrl := &KrakenControl{ kraken: kraken, actions: make(chan Action), syncGameState: new(sync.WaitGroup), } kraken.Ctrl = ctrl ctrl.syncGameState.Add(1) go func() { s := kraken.gameState for len(s.Map.WinCondition(s)) == 0 { ctrl.krakenTurn() } }() } func (c *KrakenControl) Player() *Player { return c.kraken } func (ctrl *KrakenControl) awaitGameStateSync() { ctrl.syncGameState.Wait() ctrl.syncGameState.Add(1) } func (ctrl *KrakenControl) krakenTurn() { s := ctrl.kraken.gameState kraken := ctrl.kraken ctrl.activeSuprise = false ctrl.awaitGameStateSync() ctrl.turn++ if ctrl.turn != kraken.Turn { log.Panicf("Ai turn out of sync %d != %d", ctrl.turn, kraken.Turn) } for { drawn := kraken.Deck.Draw(1) if len(drawn) == 0 { break } c := drawn[0] if c.BuyCost > kraken.Resource { log.Println("Kraken discards", c.Name, "because resource to low", kraken.Resource) kraken.DiscardPile.AddCard(c) break } kraken.Hand.AddCard(c) var costFunc ActionCostFunc = func(s *State) bool { kraken.Resource -= c.BuyCost return true } a := NewPlayAction(kraken, c, costFunc) err := selectRandomTargets(s.Rand, a.Targets()) if err != nil { log.Println("No target available for", c.Name) kraken.DiscardPile.AddCard(c) continue } ctrl.actions <- a ctrl.awaitGameStateSync() if c.IsPermanent() && ctrl.activeSuprise { ctrl.activeSuprise = false } } for _, u := range s.OwnUnits(kraken) { unitAI := NewUnitAI(s, u) if unitAI == nil { continue } unitAI.promptAction() for a := range unitAI.actions { ctrl.actions <- a ctrl.awaitGameStateSync() unitAI.promptAction() } } ctrl.actions <- NewPassPriority(ctrl.kraken) } func (ctrl *KrakenControl) RecvAction() Action { s := ctrl.kraken.gameState kraken := ctrl.kraken if !s.IsActivePlayer(kraken) || s.ActivePhase != Phases.ActionPhase || !s.Stack.IsEmpty() { return NewPassPriority(ctrl.kraken) } ctrl.syncGameState.Done() return <-ctrl.actions } func (ctrl *KrakenControl) SendNotification(n PlayerNotification) { } func (p *Player) IsKraken() bool { return p.Name == KRAKEN_NAME } func (s *State) FindKraken() *Player { for _, p := range s.Players { if p.IsKraken() { return p } } return nil } func (s *State) KrakenSpawnTiles() []*Tile { kraken := s.FindKraken() if !kraken.Ctrl.(*KrakenControl).activeSuprise { return s.Map.FilterTiles(func(t *Tile) bool { return strings.Contains(t.Raw, "deep water spawn") }) } spawns := []*Tile{} for _, u := range s.EnemyUnits(kraken) { spawns = append(spawns, TilesInRange(s.Map, u, 1)...) } return spawns }