/** * 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.algorithmic; import org.apache.log4j.Logger; import org.jskat.data.Trick; 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.jskat.util.rule.SkatRuleFactory; /** * @author Markus J. Luzius <br> * created: 15.06.2011 19:13:50 * */ public class AlgorithmicOpponentPlayer implements IAlgorithmicAIPlayer { private static final Logger log = Logger .getLogger(AlgorithmicOpponentPlayer.class); private final AlgorithmicAIPlayer myPlayer; private final ImmutablePlayerKnowledge knowledge; /** * */ AlgorithmicOpponentPlayer(AlgorithmicAIPlayer p) { myPlayer = p; knowledge = p.getKnowledge(); log.debug("Defining player <" + myPlayer.getPlayerName() + "> as " + this.getClass().getName()); } /* * (non-Javadoc) * * @see org.jskat.ai.IJSkatPlayer#playCard() */ @Override public Card playCard() { if (knowledge.getOwnCards().size() == 1) return knowledge.getOwnCards().get(0); if (knowledge.getTrickCards() == null || knowledge.getTrickCards().isEmpty()) { if (knowledge.getNoOfTricks() < 1) { return openGame(); } return openTrick(); } if (knowledge.getTrickCards().size() == 1) { return playMiddlehandCard(); } return playRearhandCard(); } private Card openGame() { CardList cards = knowledge.getOwnCards(); if (knowledge.getDeclarer() == Player.MIDDLEHAND) { // "kurzer Weg, lange Farbe" Suit longSuit = cards.getMostFrequentSuit(knowledge.getTrumpSuit()); if (longSuit != null && cards.get(cards.getFirstIndexOfSuit(longSuit)).getRank() == Rank.ACE) { log.debug("playCard (1)"); return cards.get(knowledge.getOwnCards().getFirstIndexOfSuit( longSuit)); } log.debug("playCard (2)"); return cards.get(knowledge.getOwnCards().getLastIndexOfSuit( cards.getMostFrequentSuit())); } else if (knowledge.getDeclarer() == Player.REARHAND) { // "langer Weg, kurze Farbe" int minCount = 9; Card result = null; for (Card c : cards) { if (result == null || result.isTrump(knowledge.getGameType())) { result = c; continue; } if (cards.getSuitCount(c.getSuit(), false) < minCount && !(cards.getSuitCount(c.getSuit(), false) == 1 && c .getRank() == Rank.TEN)) { result = c; minCount = cards.getSuitCount(c.getSuit(), false); continue; } if (cards.getSuitCount(c.getSuit(), false) == minCount && c.getRank() == Rank.ACE) { result = c; continue; } if (c.getSuit() == result.getSuit() && cards.getSuitCount(c.getSuit(), false) == minCount) { result = c; continue; } } return result; // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // Suit shortSuit = null; // for(Suit suit: Suit.values()) { // if(suit==knowledge.getGameType().asSuit()) continue; // int cardCount = cards.getSuitCount(suit, false); // if(cardCount>0 && cardCount<minCount) { // shortSuit = suit; // minCount = cardCount; // } // } // if(shortSuit==null) { // log.warn("no short suit found: "+cards); // log.debug("playCard (3)"); // return cards.get(cards.size()-1); // } // if(cards.get(cards.getFirstIndexOfSuit(shortSuit)).getRank()==Rank.ACE) // { // log.debug("playCard (4)"); // return cards.get(cards.getFirstIndexOfSuit(shortSuit)); // } // log.debug("playCard (5)"); // return cards.get(cards.getLastIndexOfSuit(shortSuit)); } else { log.warn(".openGame(): wrong declarer position: " + knowledge.getDeclarer()); log.debug("playCard (6)"); return cards.get(cards.size() - 1); } } private Card openTrick() { CardList cards = knowledge.getOwnCards(); for (Suit s : Suit.values()) { if (!knowledge.couldHaveSuit(knowledge.getDeclarer(), s) && cards.hasSuit(knowledge.getGameType(), s)) { log.debug("playCard (7)"); return cards.get(cards.getLastIndexOfSuit(s)); } } return openGame(); } private Card playMiddlehandCard() { log.debug("I (" + myPlayer.getPlayerName() + ") am in middlehand (OpponentPlayer)"); // fallback: take the first valid card CardList cards = knowledge.getOwnCards(); Card initialCard = knowledge.getTrickCards().get(0); GameType gameType = knowledge.getGameType(); Card result = null; if (knowledge.getDeclarer() == knowledge.getCurrentTrick() .getForeHand()) { log.debug("Single player has already played a card"); for (Card c : cards) { if (c.beats(gameType, initialCard) && c.isAllowed(gameType, initialCard, cards)) { if (result == null) { result = c; continue; } boolean isTrump = initialCard.isTrump(gameType); if (!isTrump && c.getRank() != Rank.ACE && c.getPoints() >= result.getPoints()) { result = c; continue; } if (c.getRank() == Rank.ACE && knowledge.getPotentialSuitCount(knowledge .getCurrentTrick().getForeHand(), initialCard.getSuit(), isTrump, false) < 2) { result = c; continue; } } } if (result != null) { // I can beat the single player's card - so I take it log.debug("playCard (12) - I try to take it"); return result; } // I cannot beat the single player's card if (initialCard.isTrump(gameType)) { if (knowledge.couldHaveTrump(knowledge.getCurrentTrick() .getRearHand())) { int cnt = 0; for (Card c : Card.getBeatingCards(gameType, initialCard)) { if (knowledge.couldHaveCard(knowledge.getCurrentTrick() .getRearHand(), c)) { cnt++; } } if (cnt > 0) { log.debug("Looking for a high value card - rearhand might have " + cnt + " beating card(s)"); for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards)) { if (result == null || c.getPoints() > result.getPoints()) { if (c.getRank() == Rank.ACE && !c.isTrump(gameType) && knowledge.couldHaveSuit( knowledge.getDeclarer(), c.getSuit()) && !knowledge .getOwnCards() .contains( Card.getCard( c.getSuit(), Rank.TEN))) { log.debug("Keeping my ace of " + c.getSuit()); } else { result = c; } } } } if (result != null) { log.debug("playCard (14), cnt=" + cnt); return result; } } } result = cards.get(cards.getLastIndexOfSuit( knowledge.getTrumpSuit(), true)); if (result == null) { return getDefaultCard(cards, initialCard, gameType); } log.debug("playCard (15)"); return result; } else if (knowledge.couldHaveSuit(knowledge.getCurrentTrick() .getRearHand(), initialCard.getSuit())) { // I cannot beat the single player's card, which is not a trump // rear hand could still have the same suit int cntSuit = 0; int cntTrump = 0; for (Card c : Card.getBeatingCards(gameType, initialCard)) { if (knowledge.couldHaveCard(knowledge.getCurrentTrick() .getRearHand(), c)) { if (c.isTrump(gameType)) cntTrump++; else cntSuit++; } } if (cntSuit > 0 || (cntTrump > 1 && knowledge.couldHaveSuit(knowledge .getCurrentTrick().getRearHand(), initialCard .getSuit()))) { if (initialCard.getRank() == Rank.ACE || (initialCard.getRank() == Rank.TEN && !knowledge .couldHaveCard(Player.REARHAND, Card .getCard(initialCard.getSuit(), Rank.ACE)))) { result = cards.get(cards.getLastIndexOfSuit( initialCard.getSuit(), false)); } else { result = cards.get(cards.getFirstIndexOfSuit( initialCard.getSuit(), false)); } log.debug("playCard (13pre1), cnt=" + cntSuit + " / " + cntTrump); } else { result = cards.get(cards.getLastIndexOfSuit( initialCard.getSuit(), false)); log.debug("playCard (13pre2), cnt=" + cntSuit + " / " + cntTrump); } if (result == null) { for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards)) { result = c; } } } log.debug("playCard (13)"); return result; } if (knowledge.couldHaveTrump(knowledge.getCurrentTrick() .getRearHand())) { result = cards.get(cards.getFirstIndexOfSuit( initialCard.getSuit(), false)); } } else { log.debug("Single player is in rearhand"); if (knowledge.couldHaveSuit(knowledge.getDeclarer(), initialCard.getSuit())) { if (initialCard.getRank() != Rank.JACK && initialCard.getSuit() != knowledge.getTrumpSuit() && cards.contains(Card.getCard(initialCard.getSuit(), Rank.ACE))) { log.debug("playCard (11)"); return Card.getCard(initialCard.getSuit(), Rank.ACE); } } else { // if the single player doesn't have that suit: take the lowest // one result = cards.get(cards.getLastIndexOfSuit(initialCard .getSuit())); if (result != null) { log.debug("playCard (10)"); return result; } if (knowledge.couldHaveTrump(knowledge.getDeclarer())) { } } } // fallback: get last valid card return getDefaultCard(cards, initialCard, gameType); } private Card playRearhandCard() { log.debug("I (" + myPlayer.getPlayerName() + ") am in rearhand (OpponentPlayer)"); // fallback: take the first valid card CardList cards = knowledge.getOwnCards(); Card initialCard = knowledge.getTrickCards().get(0); GameType gameType = knowledge.getGameType(); Card result = null; if (initialCard.beats(gameType, knowledge.getTrickCards().get(1))) { // forehand win log.debug("forehand win - declarer=" + knowledge.getDeclarer()); if (knowledge.getDeclarer() == knowledge.getCurrentTrick() .getForeHand()) { // it's a single player win so far log.debug("Single player is in forehand and has the trick so far"); boolean myTrick = false; for (Card c : cards) { if (!c.isAllowed(gameType, initialCard, cards)) continue; if (result == null) { result = c; continue; } Trick tmpTrick = (Trick) knowledge.getCurrentTrick() .clone(); tmpTrick.addCard(c); if (SkatRuleFactory.getSkatRules(gameType) .calculateTrickWinner(gameType, tmpTrick) != knowledge .getDeclarer()) { if (!myTrick) { log.debug("I can take the trick with " + c); result = c; myTrick = true; } else if (c.getPoints() >= result.getPoints()) result = c; continue; } else if (!myTrick && c.getPoints() <= result.getPoints()) result = c; } if (result != null) return result; for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards) && (result == null || c.getPoints() <= result .getPoints())) result = c; } log.debug("playRearhandCard() (2)"); return result; } else { // it's ours already log.debug("it is ours (declarer in forehand)"); if (!initialCard.isTrump(gameType)) { result = cards.get(cards.getFirstIndexOfSuit( initialCard.getSuit(), false)); } else { for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards) && (result == null || c.getPoints() > result .getPoints())) result = c; } } if (result != null) { log.debug("playRearhandCard() (3)"); return result; } for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards) && (result == null || (c.getPoints() > result .getPoints() && c.getRank() != Rank.ACE))) result = c; } log.debug("playRearhandCard() (4)"); return result; } } else { // middlehand win if (knowledge.getDeclarer() != knowledge.getCurrentTrick() .getForeHand()) { log.debug("Single player is in middlehand and has the trick so far"); // it's a single player win so far boolean myTrick = false; for (Card c : cards) { if (!c.isAllowed(gameType, initialCard, cards)) continue; if (result == null) { result = c; continue; } Trick tmpTrick = (Trick) knowledge.getCurrentTrick() .clone(); tmpTrick.addCard(c); if (SkatRuleFactory.getSkatRules(gameType) .calculateTrickWinner(gameType, tmpTrick) != knowledge .getDeclarer()) { if (!myTrick) { log.debug("I can take the trick with " + c); result = c; myTrick = true; } else if (c.getPoints() >= result.getPoints()) result = c; continue; } else if (!myTrick && c.getPoints() <= result.getPoints()) result = c; } } else { // it's ours already log.debug("it is ours (declarer in middlehand)"); if (!initialCard.isTrump(gameType)) { result = cards.get(cards.getFirstIndexOfSuit( initialCard.getSuit(), false)); if (result == null) { // "schmieren" for (Card c : cards) { if (result == null) result = c; else if (result.isTrump(gameType) && (!c.isTrump(gameType) || c.getPoints() >= result .getPoints())) result = c; else if (c.getRank() == Rank.ACE && !knowledge.couldHaveSuit( knowledge.getDeclarer(), c.getSuit())) result = c; else if (c.getPoints() > result.getPoints()) result = c; } if (result != null) { log.debug("playRearhandCard() (9)"); return result; } } } else { for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards) && (result == null || c.getPoints() > result.getPoints() || result .getRank() == Rank.JACK)) result = c; } } if (result != null) { log.debug("playRearhandCard() (7)"); return result; } for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards) && (result == null || (c.getPoints() > result .getPoints() && c.getRank() != Rank.ACE))) result = c; } log.debug("playRearhandCard() (8)"); return result; } } return getDefaultCard(cards, initialCard, gameType); } /** * Gets a fallback card, if no other algorithm returned a card * * @param cards * @param initialCard * @param gameType * @return a default card */ private Card getDefaultCard(CardList cards, Card initialCard, GameType gameType) { Card result = null; for (Card c : cards) { if (c.isAllowed(gameType, initialCard, cards)) { result = c; } } if (result != null) { log.debug("playCard (8)"); return result; } log.warn("no possible card found in card list [" + cards + "] with " + gameType + " / " + initialCard); log.debug("playCard (9)"); return cards.get(0); } /* * (non-Javadoc) * * @see * org.jskat.ai.algorithmic.IAlgorithmicAIPlayer#discardSkat(org.jskat.ai * .algorithmic.BidEvaluator) */ @Override public CardList discardSkat(BidEvaluator bidEvaluator) { throw new IllegalStateException("opponent player cannot discard a skat"); } }