package game import ( "strings" "sync" "muhq.space/muhqs-game/go/log" ) 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 *LocalState) *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.Map().WinCondition = BossGame(KRAKEN_NAME) s.addPermanent(NewUnitFromPath("kraken/the_kraken", kraken_tile[0], kraken)) addKrakenControl(kraken) return kraken } func (s *LocalState) AddKraken() *Player { kraken := NewKraken(len(s.Players())+1, s) s.players = append(s.players, kraken) return 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.check(s)) == 0 { ctrl.krakenTurn() } }() } func (c *KrakenControl) Player() *Player { return c.kraken } func (c *KrakenControl) SendAction(Action) error { return nil } func (c *KrakenControl) RecvNotification() (PlayerNotification, error) { var n PlayerNotification return n, nil } func (c *KrakenControl) Close() { close(c.actions) } 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] costs := c.BuyCosts.Costs(s) if costs > kraken.Resource { log.Info("Kraken discards because insufficient resource", "card", c.Name, "resource", kraken.Resource) kraken.DiscardPile.AddCard(c) break } kraken.Hand.AddCard(c) var costFunc ActionCostFunc = func(s *LocalState) bool { kraken.Resource -= costs return true } a := newPlayActionWithCostFunc(kraken, c, costFunc) err := selectRandomTargets(s.Rand, a.Targets()) if err != nil { log.Info("Diacard because no target for", "card", 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) { if u.card.Name == "The Kraken" { continue } 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, error) { s := ctrl.kraken.gameState kraken := ctrl.kraken if !s.IsActivePlayer(kraken) || s.activePhase != Phases.ActionPhase || !s.stack.IsEmpty() { return NewPassPriority(ctrl.kraken), nil } ctrl.syncGameState.Done() return <-ctrl.actions, nil } func (ctrl *KrakenControl) SendNotification(n PlayerNotification) error { return nil } func (p *Player) IsKraken() bool { return p.Name == KRAKEN_NAME } func (s *LocalState) FindKraken() *Player { for _, p := range s.Players() { if p.IsKraken() { return p } } return nil } func (s *LocalState) 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 }