import { CardType } from "../dominion/card-type" ; import { Cards } from "../utils/cards" ; import { SegmentedRange } from "../utils/segmented-range" ; import { SupplyCard } from "../dominion/supply-card" ; import { SupplyCorrection } from "./supply-correction" ; import { SupplyDivision } from "./supply-division" ; import { SupplyDivisions } from "./supply-divisions" ; import { getRandomInt } from "../utils/rand" ; export class ReactionSupplyCorrection implements SupplyCorrection { isSatisfied ( divisions ) { const selectedCards = SupplyDivisions . getLockedAndSelectedCards ( divisions ) ; if ( this . hasReaction ( selectedCards ) ) { return true ; } if ( selectedCards . filter ( Cards . filterByRequiredType ( CardType . ATTACK ) ) . length ) { return false ; } const attacks = SupplyDivisions . getAvailableCardsOfType ( divisions , CardType . ATTACK ) ; return attacks . length == 0 ; } allowLockedCard ( divisions , card ) { if ( ! card . isAttack ) { return true ; } if ( this . hasReaction ( SupplyDivisions . getLockedAndSelectedCards ( divisions ) ) ) { return true ; } const availableReactionsPerDivision = SupplyDivisions . getAvailableCardsOfTypePerDivision ( divisions , CardType . REACTION ) ; let totalReactions = 0 ; for ( let reactions of availableReactionsPerDivision ) { totalReactions += reactions . length ; } if ( totalReactions < 1 ) { return false ; } const divisionContainingCard = this . getDivisionContainingCard ( divisions , card ) ; for ( let i = 0 ; i < divisions . length ; i ++ ) { const division = divisions [ i ] ; if ( division == divisionContainingCard ) { if ( division . unfilledCount >= 2 && availableReactionsPerDivision [ i ] . length > 0 ) { return true ; } } else { if ( ! division . isFilled && availableReactionsPerDivision [ i ] . length > 0 ) { return true ; } } } return false ; } correctDivisions ( divisions ) { const lockedCards = SupplyDivisions . getLockedCards ( divisions ) ; const lockedCardsHasAttack = lockedCards . filter ( Cards . filterByRequiredType ( CardType . ATTACK ) ) . length > 0 ; const hasAvailableReactions = this . hasAvailableReactions ( divisions ) ; if ( lockedCardsHasAttack && hasAvailableReactions ) { return this . lockRandomCardOfType ( divisions , CardType . REACTION ) ; } if ( lockedCardsHasAttack && ! hasAvailableReactions ) { throw new Error ( "Attack is locked but no reactions available." ) ; } if ( ! hasAvailableReactions || ! this . checkIfAttacksShouldBeIncluded ( divisions ) ) { return this . removeAttacksFromAvailableCards ( divisions ) ; } const newDivisions = this . lockRandomCardOfType ( divisions , CardType . REACTION ) ; return this . lockRandomCardOfType ( newDivisions , CardType . ATTACK ) ; } private removeAttacksFromAvailableCards ( divisions ) { const newDivisions = [ ] ; for ( let division of divisions ) { const attackCards = division . availableCards . filter ( Cards . filterByRequiredType ( CardType . ATTACK ) ) ; newDivisions . push ( division . createDivisionByRemovingCards ( Cards . extractIds ( attackCards ) ) ) ; } return newDivisions ; } private checkIfAttacksShouldBeIncluded ( divisions ) { const divisionsWithReaction = this . lockRandomCardOfType ( divisions , CardType . REACTION ) ; const filledDivisions = this . fillDivisions ( divisionsWithReaction ) ; const cards = SupplyDivisions . getLockedAndSelectedCards ( filledDivisions ) ; return cards . filter ( Cards . filterByRequiredType ( CardType . ATTACK ) ) . length > 0 ; } private fillDivisions ( divisions ) { const results = [ ] ; for ( let division of divisions ) { while ( ! division . isFilled ) { const selectedCard = this . getRandomCard ( division . availableCards ) ; division = division . createDivisionBySelectingCard ( selectedCard . id ) ; } results . push ( division ) ; } return results ; } private lockRandomCardOfType ( divisions , cardType ) { const newDivisions = divisions . concat ( ) ; const availableCardsPerDivision = SupplyDivisions . getAvailableCardsOfTypePerDivision ( newDivisions , cardType ) ; const counts = [ ] ; for ( let cards of availableCardsPerDivision ) { counts . push ( cards . length ) ; } const segmentedRange = new SegmentedRange ( 0 , counts ) ; const divisionIndex = segmentedRange . getRandomSegmentIndex ( ) ; const cardToLock = this . getRandomCard ( availableCardsPerDivision [ divisionIndex ] ) ; newDivisions [ divisionIndex ] = newDivisions [ divisionIndex ] . createDivisionByLockingCard ( cardToLock . id ) ; return newDivisions ; } private hasAvailableReactions ( divisions ) { const availableReactions = SupplyDivisions . getAvailableCardsOfType ( divisions , CardType . REACTION ) ; return availableReactions . length > 0 ; } private getDivisionContainingCard ( divisions , card ) { for ( let division of divisions ) { const allCards = division . lockedAndSelectedCards . concat ( division . availableCards ) ; if ( allCards . filter ( Cards . filterByIncludedIds ( [ card . id ] ) ) . length ) { return division ; } } throw new Error ( `Card not found in any of the divisions: ${ card . id }` ) ; } private getRandomCard ( cards ) { return cards [ getRandomInt ( 0 , cards . length ) ] ; } private hasReaction ( cards ) { return cards . filter ( Cards . filterByRequiredType ( CardType . REACTION ) ) . length > 0 ; } }