package ca.carleton.blackjack.game; import ca.carleton.blackjack.game.entity.AIPlayer; import ca.carleton.blackjack.game.entity.Player; import ca.carleton.blackjack.game.entity.card.Card; import ca.carleton.blackjack.game.entity.card.Rank; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; import static ca.carleton.blackjack.game.BlackJackGame.uniqueResult; import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; import static org.springframework.util.CollectionUtils.containsAny; /** * Service class implementing the logic of our program. *

* Created by Mike on 10/7/2015. */ @Service public class BlackJackService { private static final Logger LOG = LoggerFactory.getLogger(BlackJackService.class); /** * The action the dealer will take according to our game rules. * * @param dealer the dealer. * @return the option they will use for their next move. */ public GameOption getDealerOption(final AIPlayer dealer) { final int handValue = (int) dealer.getHand().getHandValue(); if (handValue < 17) { return GameOption.HIT; } final List cards = dealer.getHand().getCards(); final List cardRanks = cards.stream().map(Card::getRank).collect(toList()); if (handValue == 17 && containsAny(cardRanks, asList(Rank.ACE_HIGH, Rank.ACE_LOW))) { return GameOption.HIT; } else if (handValue == 17) { return GameOption.STAY; } return GameOption.HIT; } /** * The action the AI will take according to our game rules. * * @param player the AI. * @param otherPlayers the other players. * @return the option they will use for their next move. */ public GameOption getAIOption(final AIPlayer player, final List otherPlayers) { if (this.shouldAISplit(player.getHand().getCards()) && !player.getHand().isSplitHand()) { return GameOption.SPLIT; } final int handValue = (int) player.getHand().getHandValue(); if (handValue == 21) { LOG.info("Staying because AI has 21"); return GameOption.STAY; } for (final Player other : otherPlayers) { if (other.getLastOption() == GameOption.STAY) { if (other.getHand().getCards().size() == 2) { // should only have 1 visible if their two initial cards. final Card visibleCard = other.getHand() .getCards() .stream() .filter(card -> !card.isHidden()) .collect(uniqueResult()); if (visibleCard.getRank().getValue() == 10 || visibleCard.getRank() == Rank.ACE_LOW || visibleCard.getRank() == Rank.ACE_HIGH) { LOG.info("Hitting because AI saw that another player stayed with 2 cards (10 visible)."); return GameOption.HIT; } } } } final List othersThatAreNotBust = otherPlayers.stream() .filter(otherGuy -> otherGuy.getLastOption() == null || (otherGuy.getLastOption() != null && otherGuy.getLastOption() != GameOption.BUST)) .collect(Collectors.toList()); if (handValue >= 18 && handValue <= 20) { for (final Player other : othersThatAreNotBust) { final List visibleCards = other.getHand() .getCards() .stream() .filter(card -> !card.isHidden()) .collect(Collectors.toList()); final int valueOfVisibleCards = (int) (long) visibleCards.stream() .mapToInt(card -> card.getRank().getValue()) .sum(); LOG.info("Value of visible cards for {} is {}", other, valueOfVisibleCards); if (valueOfVisibleCards > (handValue - 10)) { LOG.info("Hitting because value of visible cards > (hand value - 10). Checked against {}", other); return GameOption.HIT; } } LOG.info("Staying because value is between 18 and 20)"); return GameOption.STAY; } LOG.info("Hitting because ran out of options."); return GameOption.HIT; } /** * We only split if the initial cards are the same rank. */ private boolean shouldAISplit(final List cards) { return cards.size() == 2 && cards.get(0).getRank() == cards.get(1).getRank(); } }