/** * Copyright (C) 2017 Jan Schäfer (jansch@users.sourceforge.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jskat.ai.mjl; import org.jskat.player.ImmutablePlayerKnowledge; import org.jskat.util.Card; import org.jskat.util.CardList; import org.jskat.util.GameType; import org.jskat.util.Player; import org.jskat.util.Rank; import org.jskat.util.Suit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Markus J. Luzius (markus@luzius.de) * */ public class Helper { private static Logger log = LoggerFactory.getLogger(Helper.class); /** * Checks whether the current trick would be won by the single player, so * that the AIPlayer can decide which card to play * * @param knowledge * All the necessary trick infos * @return true, if the single player would win the trick */ public static boolean isSinglePlayerWin( final ImmutablePlayerKnowledge knowledge) { if (knowledge.getTrickCards().size() < 2) { // one card on the table: can't be single player win yet return false; } if (knowledge.getDeclarer() == Player.FOREHAND) { if (knowledge .getTrickCards() .get(1) .beats(knowledge.getGameAnnouncement().getGameType(), knowledge.getTrickCards().get(0))) { return false; } else { return true; } } else if (knowledge.getDeclarer() == Player.MIDDLEHAND) { if (knowledge .getTrickCards() .get(1) .beats(knowledge.getGameAnnouncement().getGameType(), knowledge.getTrickCards().get(0))) { return true; } else { return false; } } else { log.warn("Request for wrong singlePlayerPos (" + knowledge.getDeclarer() + ")!"); return false; } } /** * Decides whether a player is able to beat a certain card * * @param cards * hand of the player * @param cardToBeat * card that should be beaten * @param initialCard * initial card of the trick * @param gameType * game type * @return true, if <b>cards</b> contain a card that can beat the * <b>cardToBeat</b> */ public static int isAbleToBeat(final CardList cards, final Card cardToBeat, final Card initialCard, final GameType gameType) { int result = -1; for (int i = 0; i < cards.size(); i++) { if (cards.get(i).isAllowed(gameType, initialCard, cards)) { if (cards.get(i).beats(gameType, initialCard)) { // log.debug(cards.get(i) + " can beat " + cardToBeat + // "."); result = i; break; } } } return result; } /** * Decides whether a player is able to match a certain initial card * * @param cards * Players cards * @param initialCard * Initial card * @param gameType * Game type * @return true if there is at least one card in the hand that can match * <b>initialCard</b> */ public static boolean isAbleToMatch(final CardList cards, final Card initialCard, final GameType gameType) { boolean result = false; for (int i = 0; i < cards.size(); i++) { boolean sameSuit = (cards.get(i).getSuit() == initialCard.getSuit()); if (cards.get(i).isAllowed(gameType, initialCard, cards)) { if (gameType != GameType.NULL) { if (cards.get(i).isTrump(gameType) && initialCard.isTrump(gameType)) { result = true; } else if (!cards.get(i).isTrump(gameType) && !initialCard.isTrump(gameType) && sameSuit) { result = true; } } else if (sameSuit) { result = true; } } if (result) { break; } } return result; } /** * Gets the highest trump card out of a given hand * * @param cards * a hand * @param currTrump * the current trump color * @return index of the highest trump, 0 if there is no trump */ public static int getHighestTrump(final CardList cards, final Suit currTrump) { // if (cards.size() < 1) // return 0; int index = 0; // for (int i = 1; i < cards.size(); i++) { // if (cards.get(i).beats(cards.get(index), // GameType.SUIT, currTrump, cards.get(i))) // index = i; // } return index; } /** * Checks, whether there is at least one trump card in a given hand * * @param cards * a hand * @param currTrump * trump color * @return true, if there is at least one trump card in the hand */ public static boolean hasTrump(final CardList cards, final Suit currTrump) { return false; // return (cards.hasTrump(GameType.SUIT, currTrump)); } /** * Gets the game multiplier * * @param cards * a hand * @return multiplier (only positive values) */ public static int getMultiplier(final CardList cards) { // TODO (js) this might be a candidate for SkatRules int multiplier = 2; if (cards.contains(Card.CJ)) { // game was played with jacks if (cards.contains(Card.SJ)) { multiplier++; if (cards.contains(Card.HJ)) { multiplier++; if (cards.contains(Card.DJ)) { multiplier++; } } } } else { // game was played without jacks if (!cards.contains(Card.SJ)) { multiplier++; if (!cards.contains(Card.HJ)) { multiplier++; if (!cards.contains(Card.DJ)) { multiplier++; } } } } return multiplier; } /** * Converts a hand's and the skat's cards of a certain suit to a binary * stream * * @param cards * a hand * @param skat * the skat * @param suit * only cards of this suit are considered * @return binary value of the available cards */ public static int suitCardsToBinaryWithSkat(final CardList cards, final CardList skat, final Suit suit) { int counter = 0; if (cards.contains(Card.getCard(suit, Rank.SEVEN)) || skat.contains(Card.getCard(suit, Rank.SEVEN))) { counter += 1; } if (cards.contains(Card.getCard(suit, Rank.EIGHT)) || skat.contains(Card.getCard(suit, Rank.EIGHT))) { counter += 2; } if (cards.contains(Card.getCard(suit, Rank.NINE)) || skat.contains(Card.getCard(suit, Rank.NINE))) { counter += 4; } if (cards.contains(Card.getCard(suit, Rank.QUEEN)) || skat.contains(Card.getCard(suit, Rank.QUEEN))) { counter += 8; } if (cards.contains(Card.getCard(suit, Rank.KING)) || skat.contains(Card.getCard(suit, Rank.KING))) { counter += 16; } if (cards.contains(Card.getCard(suit, Rank.TEN)) || skat.contains(Card.getCard(suit, Rank.TEN))) { counter += 32; } if (cards.contains(Card.getCard(suit, Rank.ACE)) || skat.contains(Card.getCard(suit, Rank.ACE))) { counter += 64; } if (cards.contains(Card.getCard(suit, Rank.JACK)) || skat.contains(Card.getCard(suit, Rank.JACK))) { counter += 128; } return counter; } /** * Converts a hand's cards of a certain suit to a binary stream * * @param cards * a hand * @param suit * only cards of this suit are considered * @return binary value of the available cards */ public static int suitCardsToBinary(final CardList cards, final Suit suit) { int counter = 0; if (cards.contains(Card.getCard(suit, Rank.SEVEN))) { counter += 1; } if (cards.contains(Card.getCard(suit, Rank.EIGHT))) { counter += 2; } if (cards.contains(Card.getCard(suit, Rank.NINE))) { counter += 4; } if (cards.contains(Card.getCard(suit, Rank.QUEEN))) { counter += 8; } if (cards.contains(Card.getCard(suit, Rank.KING))) { counter += 16; } if (cards.contains(Card.getCard(suit, Rank.TEN))) { counter += 32; } if (cards.contains(Card.getCard(suit, Rank.ACE))) { counter += 64; } if (cards.contains(Card.getCard(suit, Rank.JACK))) { counter += 128; } return counter; } /** * Converts a hand's cards of a certain suit to a binary stream for a null * game (which means that jacks are also considered) * * @param cards * a hand * @param suit * only cards of this suit are considered * @return binary value of the available cards */ public static int suitCardsToBinaryNullGame(final CardList cards, final Suit suit) { int counter = 0; if (cards.contains(Card.getCard(suit, Rank.SEVEN))) { counter += 1; } if (cards.contains(Card.getCard(suit, Rank.EIGHT))) { counter += 2; } if (cards.contains(Card.getCard(suit, Rank.NINE))) { counter += 4; } if (cards.contains(Card.getCard(suit, Rank.TEN))) { counter += 8; } if (cards.contains(Card.getCard(suit, Rank.JACK))) { counter += 16; } if (cards.contains(Card.getCard(suit, Rank.QUEEN))) { counter += 32; } if (cards.contains(Card.getCard(suit, Rank.KING))) { counter += 64; } if (cards.contains(Card.getCard(suit, Rank.ACE))) { counter += 128; } return counter; } /** * Gets a binary representation of the jacks in the given hand * * @param cards * a hand * @return binary value of the available jacks */ public static int getJacks(final CardList cards) { int counter = 0; if (cards.contains(Card.CJ)) { counter += 1; } if (cards.contains(Card.SJ)) { counter += 2; } if (cards.contains(Card.HJ)) { counter += 4; } if (cards.contains(Card.DJ)) { counter += 8; } return counter; } /** * Gets the number of jacks in the given hand * * @param cards * a hand * @return number of jacks */ public static int countJacks(final CardList cards) { int counter = 0; if (cards.contains(Card.CJ)) { counter++; } if (cards.contains(Card.SJ)) { counter++; } if (cards.contains(Card.HJ)) { counter++; } if (cards.contains(Card.DJ)) { counter++; } return counter; } /** * Converts a binary stream to a suit color * * @param binary * binary stream (four bits) * @return suit color, -1 if more than one bit is set */ public static Suit binaryToSuit(final int binary) { Suit result = null; if (!(binary == 1 || binary == 2 || binary == 4 || binary == 8)) { log.warn(".binaryToSuit(): warning: more than one suit possible! -->" + binary); return result; } if ((binary & 1) > 0) { result = Suit.DIAMONDS; } if ((binary & 2) > 0) { result = Suit.HEARTS; } if ((binary & 4) > 0) { result = Suit.SPADES; } if ((binary & 8) > 0) { result = Suit.CLUBS; } return result; } /** * Converts the suit value to a suit name (one character) <br> * "D" for Diamonds, "H" for Hearts, "S" for Spades, "C" for Clubs * * @param suit * suit value * @return suit name ("x" if not recognized) */ public static String suitName(final Suit suit) { if (suit == Suit.DIAMONDS) { return "D"; } else if (suit == Suit.HEARTS) { return "H"; } else if (suit == Suit.SPADES) { return "S"; } else if (suit == Suit.CLUBS) { return "C"; } else { return "x"; } } }