diff --git a/modules/Innovation/Cards/AbstractCard.php b/modules/Innovation/Cards/AbstractCard.php index c55b3a47..cce53c4f 100644 --- a/modules/Innovation/Cards/AbstractCard.php +++ b/modules/Innovation/Cards/AbstractCard.php @@ -928,6 +928,12 @@ protected function hasIcon(?array $card, int $icon): bool return $this->game->hasRessource($card, $icon); } + protected function hasDemandEffect(int $cardId): bool + { + $card = $this->game->getCardInfo($cardId); + return $card['has_demand'] == true; + } + protected function getBonuses(int $playerId = null): array { return $this->game->getVisibleBonusesOnBoard(self::coercePlayerId($playerId)); @@ -981,6 +987,14 @@ protected static function getValue(?array $card): int return Locations::isFaceup($card['location']) ? intval($card['faceup_age']) : intval($card['age']); } + protected static function getFaceupValue(?array $card): int + { + if (!$card) { + return 0; + } + return intval($card['faceup_age']); + } + protected static function getId(?array $card): int|null { if (!$card) { diff --git a/modules/Innovation/Cards/Artifacts/Card197_3E.php b/modules/Innovation/Cards/Artifacts/Card197_3E.php index 1f67364e..73aac96c 100644 --- a/modules/Innovation/Cards/Artifacts/Card197_3E.php +++ b/modules/Innovation/Cards/Artifacts/Card197_3E.php @@ -17,7 +17,7 @@ public function initialExecution() self::setMaxSteps(1); } else { foreach (self::getTopCards() as $card) { - if ($card['has_demand'] == true) { + if (self::hasDemandEffect($card)) { self::draw(10); return; } @@ -40,7 +40,7 @@ public function getInteractionOptions(): array public function compelMightBeEffective(): bool { foreach (self::getTopCards() as $card) { - if ($card['has_demand'] == true) { + if (self::hasDemandEffect($card)) { return true; } } diff --git a/modules/Innovation/Cards/Artifacts/Card197_4E.php b/modules/Innovation/Cards/Artifacts/Card197_4E.php index d5288eb7..21cd315e 100644 --- a/modules/Innovation/Cards/Artifacts/Card197_4E.php +++ b/modules/Innovation/Cards/Artifacts/Card197_4E.php @@ -16,7 +16,7 @@ public function initialExecution() { if (self::isCompel()) { foreach (self::getTopCards() as $card) { - if ($card['has_demand'] == true) { + if (self::hasDemandEffect($card)) { self::transferToScorePile(self::getBottomCardOfColor($card['color']), self::getLauncherId()); self::transferToScorePile(self::getBottomCardOfColor($card['color']), self::getLauncherId()); } @@ -43,7 +43,7 @@ public function handleCardChoice(array $card) public function compelMightBeEffective(): bool { foreach (self::getTopCards() as $card) { - if ($card['has_demand'] == true) { + if (self::hasDemandEffect($card)) { return true; } } diff --git a/modules/Innovation/Cards/Base/Card440.php b/modules/Innovation/Cards/Base/Card440.php index 5bfee2dd..89e696c3 100644 --- a/modules/Innovation/Cards/Base/Card440.php +++ b/modules/Innovation/Cards/Base/Card440.php @@ -3,6 +3,7 @@ namespace Innovation\Cards\Base; use Innovation\Cards\AbstractCard; +use Innovation\Enums\Locations; class Card440 extends AbstractCard { @@ -26,32 +27,16 @@ public function getInteractionOptions(): array { if (self::isDemand()) { if (self::isFirstInteraction()) { - return [ - 'player_id' => self::getLauncherId(), - 'choose_icon_type' => true, - // TODO(4E): Non-standard icons should be an option too here. - 'icon' => [1, 3, 4, 5, 6, 7], - ]; + // TODO(4E): Non-standard icons should be an option too here (and use constants). + return self::youMust()->chooseIcon([1, 3, 4, 5, 6, 7])->ofMyChoice()->build(); } else { - return [ - 'location_from' => 'board', - 'return_keyword' => true, - 'with_icon' => self::getAuxiliaryValue(), - ]; + return self::youMust()->return()->exactly(2)->fromYourBoard()->withIcon(self::getAuxiliaryValue())->refreshingSelection()->build(); } } if (self::isFirstInteraction()) { - return [ - 'location_from' => 'board', - 'return_keyword' => true, - ]; + return self::youMust()->return()->fromYourBoard()->build(); } else { - return [ - 'n' => 'all', - 'location_from' => 'score', - 'return_keyword' => true, - 'age_min' => self::getAuxiliaryValue(), - ]; + return self::youMust()->return()->all()->fromYourScore()->minValue(self::getAuxiliaryValue())->build(); } } @@ -71,4 +56,16 @@ public function handleIconChoice(int $icon) self::notifyIconChoice($icon); self::setAuxiliaryValue($icon); } + + public function demandMightBeEffective(): bool + { + // NOTE: This could be improved by filtering out stacks where the top card has only [HEALTH] icons. + return self::hasCards(Locations::BOARD); + } + + public function nonDemandsMightBeEffective(): bool + { + return self::hasCards(Locations::BOARD) || self::hasCards(Locations::SCORE); + } + } \ No newline at end of file diff --git a/modules/Innovation/Cards/Base/Card441.php b/modules/Innovation/Cards/Base/Card441.php index 6dce5031..4de793a3 100644 --- a/modules/Innovation/Cards/Base/Card441.php +++ b/modules/Innovation/Cards/Base/Card441.php @@ -33,13 +33,7 @@ public function initialExecution() public function getInteractionOptions(): array { - return [ - 'n' => 'all', - 'location_from' => 'pile', - 'return_keyword' => true, - 'color' => [self::getAuxiliaryValue()], - 'card_ids_are_in_auxiliary_array' => true, - ]; + return self::youMust()->return()->all()->fromYourBoard()->withColor(self::getAuxiliaryValue())->onlyCardsInAuxiliaryArray()->build(); } public function afterInteraction() diff --git a/modules/Innovation/Cards/Base/Card442.php b/modules/Innovation/Cards/Base/Card442.php index e29d8b9e..7b907eb9 100644 --- a/modules/Innovation/Cards/Base/Card442.php +++ b/modules/Innovation/Cards/Base/Card442.php @@ -17,7 +17,7 @@ public function initialExecution() { if (self::isFirstNonDemand()) { $revealedCard = self::drawAndReveal(11); - $color = $revealedCard['color']; + $color = self::getColor($revealedCard); if (self::splayAslant($color)) { $stack = self::getStack($color); for ($i = 0; $i < count($stack) - 4; $i++) { diff --git a/modules/Innovation/Cards/Base/Card443.php b/modules/Innovation/Cards/Base/Card443.php index a0f74cff..f0228414 100644 --- a/modules/Innovation/Cards/Base/Card443.php +++ b/modules/Innovation/Cards/Base/Card443.php @@ -3,7 +3,6 @@ namespace Innovation\Cards\Base; use Innovation\Cards\AbstractCard; -use Innovation\Enums\Locations; class Card443 extends AbstractCard { @@ -20,16 +19,9 @@ public function initialExecution() public function getInteractionOptions(): array { if (self::isFirstInteraction()) { - return [ - 'choose_value' => true, - 'age' => self::getAuxiliaryArray(), - ]; + return self::youMust()->chooseValue(self::getAuxiliaryArray())->build(); } else { - return [ - 'location_from' => Locations::BOARD, - 'score_keyword' => true, - 'age' => self::getAuxiliaryValue(), - ]; + return self::youMust()->score()->fromYourBoard()->value(self::getAuxiliaryValue())->build(); } } @@ -46,4 +38,9 @@ public function handleValueChoice(int $value) self::setAuxiliaryValue($value); // Track value to score next } + public function nonDemandsMightBeEffective(): bool + { + return count(self::filterByValue(self::getTopCards(), [11])) > 0; + } + } \ No newline at end of file diff --git a/modules/Innovation/Cards/Base/Card444.php b/modules/Innovation/Cards/Base/Card444.php index 7f563626..28e357b4 100644 --- a/modules/Innovation/Cards/Base/Card444.php +++ b/modules/Innovation/Cards/Base/Card444.php @@ -3,7 +3,6 @@ namespace Innovation\Cards\Base; use Innovation\Cards\AbstractCard; -use Innovation\Enums\Locations; class Card444 extends AbstractCard { @@ -26,25 +25,17 @@ public function getInteractionOptions(): array if (self::isFirstInteraction()) { $topCards = self::getTopCards(); $colors = self::getColorsMatchingValues($topCards, self::getRepeatedValues($topCards)); - return [ - 'location_from' => Locations::BOARD, - 'return_keyword' => true, - 'color' => $colors, - ]; + return self::youMust()->return()->fromYourBoard()->withColor($colors)->build(); } else if (self::isSecondInteraction()) { - return [ - 'location_from' => Locations::BOARD, - 'return_keyword' => true, - 'age' => self::getLastSelectedFaceUpAge(), - ]; + return self::youMust()->return()->fromYourBoard()->value(self::getAuxiliaryValue())->non(self::getLastSelectedColor())->build(); } else { - return [ - 'n' => 'all', - 'location_from' => Locations::HAND_OR_SCORE, - 'return_keyword' => true, - 'age_max' => self::getLastSelectedFaceUpAge(), - ]; + return self::youMust()->return()->all()->fromYourHandOrScore()->maxValue(self::getAuxiliaryValue())->build(); } } + public function demandMightBeEffective(): bool + { + return count(self::getRepeatedValues(self::getTopCards())) > 0; + } + } \ No newline at end of file diff --git a/modules/Innovation/Cards/Base/Card445.php b/modules/Innovation/Cards/Base/Card445.php index 1d5c8168..d941d5d1 100644 --- a/modules/Innovation/Cards/Base/Card445.php +++ b/modules/Innovation/Cards/Base/Card445.php @@ -16,9 +16,9 @@ public function initialExecution() { do { $card = self::drawAndTuck(11); - $color = $card['color']; + $color = self::getColor($card); $stack = self::getStack($color); - + if (count($stack) >= 2 && self::getValue($stack[1]) === 11) { self::lose(); return; diff --git a/modules/Innovation/Cards/Base/Card446.php b/modules/Innovation/Cards/Base/Card446.php index 47c0670e..98e29f94 100644 --- a/modules/Innovation/Cards/Base/Card446.php +++ b/modules/Innovation/Cards/Base/Card446.php @@ -14,18 +14,10 @@ class Card446 extends AbstractCard public function getInteractionOptions(): array { - $maxScoreValue = self::getMaxValueInLocation(Locations::SCORE); if (self::isDemand()) { - return [ - 'player_id' => self::getLauncherId(), - 'choose_value' => true, - ]; + return self::youMust()->chooseValue()->ofMyChoice()->build(); } else { - return [ - 'location_from' => Locations::SCORE, - 'location_to' => Locations::REVEALED, - 'age' => $maxScoreValue, - ]; + return self::youMust()->reveal()->highest()->fromYourScore()->build(); } } @@ -44,4 +36,14 @@ public function handleCardChoice(array $card) } } + public function demandMightBeEffective(): bool + { + return self::hasCards(Locations::SCORE); + } + + public function nonDemandsMightBeEffective(): bool + { + return self::hasCards(Locations::SCORE); + } + } \ No newline at end of file diff --git a/modules/Innovation/Cards/Base/Card447.php b/modules/Innovation/Cards/Base/Card447.php index 00e821c1..f98a2a23 100644 --- a/modules/Innovation/Cards/Base/Card447.php +++ b/modules/Innovation/Cards/Base/Card447.php @@ -27,20 +27,14 @@ public function getInteractionOptions(): array $cardIds = []; foreach (self::getStack($color) as $card) { if ($card['position'] < 3) { - $cardIds[] = $card['id']; + $cardIds[] = self::getId($card); } } self::setAuxiliaryArray($cardIds); self::setAuxiliaryValue2(0); // Track the sum of the values of the cards being returned - return [ - 'n' => count($cardIds), - 'location_from' => Locations::PILE, - 'color' => [$color], - 'return_keyword' => true, - 'card_ids_are_in_auxiliary_array' => true, - ]; + return self::youMust()->return()->exactly(count($cardIds))->yourStack($color)->onlyCardsInAuxiliaryArray()->build(); } public function handleAbortedInteraction() @@ -51,14 +45,14 @@ public function handleAbortedInteraction() public function handleCardChoice(array $card) { - self::incrementAuxiliaryValue2($card['faceup_age']); + self::incrementAuxiliaryValue2(self::getFaceupValue($card)); } public function afterInteraction() { $card = self::drawAndMeld(ceil(self::getAuxiliaryValue2() / 2)); if (self::getNumChosen() === 3) { - self::setAuxiliaryValue($card['color']); + self::setAuxiliaryValue(self::getColor($card)); self::setNextStep(1); } } diff --git a/modules/Innovation/Cards/Base/Card448.php b/modules/Innovation/Cards/Base/Card448.php index b1135bb7..404b8fd0 100644 --- a/modules/Innovation/Cards/Base/Card448.php +++ b/modules/Innovation/Cards/Base/Card448.php @@ -15,14 +15,10 @@ class Card448 extends AbstractCard public function getInteractionOptions(): array { if (self::isFirstInteraction()) { - return ['choose_from' => Locations::HAND]; + self::setAuxiliaryValue(-1); // Track the ID of the junked card + return self::youMust()->chooseCardFrom(Locations::HAND)->build(); } else { - return [ - 'n' => 'all', - 'location_from' => Locations::HAND, - 'return_keyword' => true, - 'age' => self::getLastSelectedFaceUpAge(), - ]; + return self::youMust()->return()->all()->fromYourHand()->value(self::getLastSelectedFaceUpAge())->build(); } } @@ -31,7 +27,7 @@ public function handleCardChoice(array $card) if (self::isFirstInteraction()) { $this->game->revealCardWithoutMoving(self::getPlayerId(), $card); self::junk($card); - self::setAuxiliaryValue($card['id']); + self::setAuxiliaryValue(self::getId($card)); self::setMaxSteps(2); } } @@ -40,9 +36,10 @@ public function afterInteraction() { if (self::isSecondInteraction()) { $card = self::getCard(self::getAuxiliaryValue()); - self::draw($card['faceup_age']); - self::draw($card['faceup_age']); - self::draw($card['faceup_age']); + $value = self::getFaceupValue($card); + self::draw($value); + self::draw($value); + self::draw($value); self::selfExecute($card); } } diff --git a/modules/Innovation/Cards/Base/Card449.php b/modules/Innovation/Cards/Base/Card449.php index d59e54b9..7d1f93d4 100644 --- a/modules/Innovation/Cards/Base/Card449.php +++ b/modules/Innovation/Cards/Base/Card449.php @@ -15,14 +15,7 @@ class Card449 extends AbstractCard public function getInteractionOptions(): array { - return [ - 'n' => 'all', - 'owner_from' => self::getPlayerId(), - 'location_from' => Locations::BOARD, - 'owner_to' => self::getLauncherId(), - 'location_to' => Locations::BOARD, - 'has_demand_effect' => true, - ]; + return self::youMust()->all()->withDemandEffect()->fromYourBoard()->toMine()->build(); } public function afterInteraction() @@ -39,4 +32,14 @@ public function afterInteraction() } } + public function demandMightBeEffective(): bool + { + foreach (self::getTopCards() as $card) { + if (self::hasDemandEffect($card)) { + return true; + } + } + return false; + } + } \ No newline at end of file diff --git a/modules/Innovation/Cards/InteractionBuilder.php b/modules/Innovation/Cards/InteractionBuilder.php index 16481abe..2023b5bd 100644 --- a/modules/Innovation/Cards/InteractionBuilder.php +++ b/modules/Innovation/Cards/InteractionBuilder.php @@ -43,6 +43,18 @@ function otherThan(int $cardId): InteractionBuilder return $this; } + function onlyCardsInAuxiliaryArray(): InteractionBuilder + { + $this->interactionOptions['card_ids_are_in_auxiliary_array'] = true; + return $this; + } + + function withDemandEffect(): InteractionBuilder + { + $this->interactionOptions['has_demand_effect'] = true; + return $this; + } + // NUMBER OF CARDS function exactly(int $n): InteractionBuilder @@ -236,6 +248,13 @@ function fromYourHand(): InteractionBuilder return $this; } + function fromYourHandOrScore(): InteractionBuilder + { + $this->interactionOptions['location_from'] = Locations::HAND_OR_SCORE; + $this->interactionOptions['owner_from'] = $this->state->getPlayerId(); + return $this; + } + function fromMyHand(): InteractionBuilder { $this->interactionOptions['location_from'] = Locations::HAND; @@ -416,16 +435,21 @@ function choose(array $choices): InteractionBuilder return $this; } - function choosePlayer(array $playerIds): InteractionBuilder + function choosePlayer(?array $playerIds): InteractionBuilder { $this->interactionOptions['choose_player'] = true; - $this->interactionOptions['players'] = $playerIds; + if ($playerIds !== null) { + $this->interactionOptions['players'] = $playerIds; + } return $this; } - function chooseValue(): InteractionBuilder + function chooseValue(?array $values = null): InteractionBuilder { $this->interactionOptions['choose_value'] = true; + if ($values !== null) { + $this->interactionOptions['age'] = $values; + } return $this; } @@ -435,7 +459,12 @@ function chooseTwoColors(): InteractionBuilder return $this; } - + function chooseIcon(array $icons): InteractionBuilder + { + $this->interactionOptions['choose_icon'] = true; + $this->interactionOptions['icon'] = $icons; + return $this; + } function chooseToRearrange(): InteractionBuilder {