/**
* 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.player;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jskat.data.GameAnnouncement;
import org.jskat.data.Trick;
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;
/**
* Holds the complete knowledge about a game, contains perfect and imperfect
* information
*/
public class ImmutablePlayerKnowledge {
/**
* Declarer player
*/
protected Player declarer;
/** the basic game information */
protected GameAnnouncement announcement;
/** Player position */
protected Player playerPosition;
/**
* Contains all cards played by the players
*/
// FIXME (markus 07.11.11.) why is this a "Set<Card>" instead of "CardList"?
protected final Map<Player, Set<Card>> playedCards = new HashMap<Player, Set<Card>>();
/**
* Contains all cards that could be on a certain position
*/
// FIXME (markus 07.11.11.) why is this a "Set<Card>" instead of "CardList"?
protected final Map<Player, Set<Card>> possiblePlayerCards = new HashMap<Player, Set<Card>>();
/**
* Contains all cards that could be in the skat
*/
// FIXME (markus 07.11.11.) why is this a "Set<Card>" instead of "CardList"?
protected final Set<Card> possibleSkatCards = new HashSet<Card>();
/**
* Holds the highest bid every player has made during bidding
*/
protected final Map<Player, Integer> highestBid = new HashMap<Player, Integer>();
/**
* The current trick
*/
protected Trick currentTrick;
/**
* Card played by the player on the left, represents first card in a trick
* or is NULL otherwise
*/
protected Card leftPlayerTrickCard;
/**
* Card played by the player on the right, represents the first or second
* card in a trick or is NULL otherwise
*/
protected Card rightPlayerTrickCard;
/**
* Counts the trump cards still on players hand
*/
protected int trumpCount;
/**
* Counts the number of cards on players hand for every card
*
* FIXME: do we need this numbers and if do we count the Jacks into this
* number
*/
// protected final Map<Suit, Integer> suitCount = new HashMap<Suit,
// Integer>();
/**
* Counts the points for every suit on players hand
*
* FIXME: do we need this numbers and if do we count the Jacks into this
* number
*/
// protected final Map<Suit, Integer> suitPoints = new HashMap<Suit,
// Integer>();
/**
* Holds trick information
*/
protected final List<Trick> tricks = new ArrayList<Trick>();
/** Player cards */
protected final Set<Card> ownCards = new HashSet<>();
/** Skat cards */
protected final Set<Card> skat = new HashSet<>();
/** Cards of the single player */
protected Set<Card> singlePlayerCards = new HashSet<>();
/** Flag for hand game */
protected boolean handGame;
/** Flag for ouvert game */
protected boolean ouvertGame;
/** Flag for schneider announced */
protected boolean schneiderAnnounced;
/** Flag for schwarz announced */
protected boolean schwarzAnnounced;
/**
* Constructor
*/
protected ImmutablePlayerKnowledge() {
}
/**
* Checks whether a player could have a card information, this is an
* uncertain information
*
* @param player
* Player ID
* @param card
* Card to check
* @return TRUE if the player could have the card
*/
public final boolean couldHaveCard(final Player player, final Card card) {
return possiblePlayerCards.get(player).contains(card);
}
/**
* Checks whether a player could have a card of the given suit, this is an
* uncertain information
*
* @param player
* Player ID
* @param suit
* Suit to check
* @return TRUE if the player could have any card of the suit
*/
public final boolean couldHaveSuit(final Player player, final Suit suit) {
boolean result = false;
for (Rank rank : Rank.values()) {
if (Rank.JACK.equals(rank)) {
continue;
}
result |= couldHaveCard(player, Card.getCard(suit, rank));
}
return result;
}
/**
* Checks whether a player could have any trump cards left, this is an
* uncertain information
*
* @param player
* Player ID
* @return TRUE if the player could have any trump card
*/
public final boolean couldHaveTrump(final Player player) {
for (Card c : Card.values()) {
if (c.isTrump(getGameType()) && couldHaveCard(player, c)) {
return true;
}
}
return false;
}
/**
* Checks whether a card could lie in the skat
*
* @param card
* Card
* @return TRUE if card could lie in the skat
*/
public final boolean couldLieInSkat(final Card card) {
return possibleSkatCards.contains(card);
}
/**
* Checks whether any player might have any trump cards left
*
* @return TRUE if any player could still have any trump cards
*/
public final boolean couldOpponentsHaveTrump() {
if (playerPosition == declarer) {
for (Player p : Player.values()) {
if (p == declarer) {
continue;
}
for (Suit s : Suit.values()) {
if (couldHaveCard(p, Card.getCard(s, Rank.JACK))) {
return true;
}
}
for (Rank r : Rank.values()) {
if (couldHaveCard(p, Card.getCard(getGameAnnouncement()
.getGameType().getTrumpSuit(), r))) {
return true;
}
}
}
}
return false;
}
/**
* Gets all tricks that are completed
*
* @return List of completed tricks
*/
public final List<Trick> getCompletedTricks() {
return Collections.unmodifiableList(tricks);
}
/**
* Provides access to the current trick
*
* @return The current trick
*/
public final Trick getCurrentTrick() {
return (Trick) currentTrick.clone();
}
/**
* Gets the declarer position
*
* @return Declarer position
*/
public final Player getDeclarer() {
return declarer;
}
/**
* @return the game
*/
public final GameAnnouncement getGameAnnouncement() {
// FIXME jan 29.10.2013: make game announcement clonable
return announcement;
}
/**
* convenience method for getGame().getGameType()
*
* @return the gameType
*/
public final GameType getGameType() {
if (announcement == null) {
return null;
}
return announcement.getGameType();
}
/**
* Gets the highest bid for a player
*
* @param player
* Player ID
* @return Highest bid for the player
*/
public final Integer getHighestBid(final Player player) {
return highestBid.get(player);
}
/**
* Gets the number of tricks
*
* @return Number of tricks
*/
public final int getNoOfTricks() {
return tricks.size();
}
/**
* @return the ownCards
*/
public final CardList getOwnCards() {
CardList result = new CardList();
result.addAll(ownCards);
return result;
}
/**
* Converts all the cards from the tricks to the binary matrix, one int for
* each suit<br>
* <br>
* The index of the array equals the Suit ordinal (0=Clubs, 3=Diamonds).
*
* @return an array int[4]
*/
public final int[] getPlayedCardsBinary() {
int[] result = new int[4];
for (Trick t : tricks) {
int[] tmp = t.getCardList().toBinary();
for (int i = 0; i < 4; i++) {
result[i] += tmp[i];
}
}
return result;
}
/**
* Gets the player position
*
* @return Player position
*/
public final Player getPlayerPosition() {
return playerPosition;
}
/**
* Checks how many cards of the given suit a player could have, this is an
* uncertain information
*
* @param player
* Player ID
* @param suit
* Suit to check
* @param isTrump
* TRUE, if the suit is also trump
* @param includeJacks
* TRUE, if Jacks should be included in the count
* @return TRUE if the player could have any card of the suit
*/
public final int getPotentialSuitCount(final Player player,
final Suit suit, final boolean isTrump,
final boolean includeJacks) {
int result = 0;
for (Rank r : Rank.values()) {
if (r == Rank.JACK && !includeJacks) {
continue;
} else if (couldHaveCard(player, Card.getCard(suit, r))) {
result++;
}
}
if (isTrump) {
for (Suit s : Suit.values()) {
if (couldHaveCard(player, Card.getCard(s, Rank.JACK))) {
result++;
}
}
}
return result;
}
/**
* @return the singlePlayerCards
*/
public final CardList getSinglePlayerCards() {
CardList result = new CardList();
result.addAll(singlePlayerCards);
return result;
}
/**
* @return the skat
*/
public final CardList getSkat() {
CardList result = new CardList();
result.addAll(skat);
return result;
}
/**
* Get cards of the current trick
*
* @return List of cards played in the current trick
*/
public final CardList getTrickCards() {
CardList trick = new CardList();
if (leftPlayerTrickCard != null) {
trick.add(leftPlayerTrickCard);
}
if (rightPlayerTrickCard != null) {
trick.add(rightPlayerTrickCard);
}
return trick;
}
/**
* Gets the number of trump cards that is known to the player (either by
* being on his own hand or by having been played)
*
* @return the number of known trump cards
*/
public final int getTrumpCount() {
return trumpCount;
}
/**
* Gets the current trump suit
*
* @return Trump suit or null if there is no trump
*/
public final Suit getTrumpSuit() {
if (announcement == null || announcement.getGameType() == null) {
throw new IllegalStateException("Game type not available."); //$NON-NLS-1$
}
return announcement.getGameType().getTrumpSuit();
}
/**
* Checks whether a player has a card alone by ruling out all other options,
* this might be an uncertain information
*
* @param player
* Player ID
* @param card
* Card to check
* @return TRUE if and only if the player has the card alone
*/
public final boolean hasCard(final Player player, final Card card) {
if (couldHaveCard(player, card)) {
return getPossibleCardPositions(card) == 1;
}
return false;
}
private int getPossibleCardPositions(final Card card) {
int possessionCount = 0;
for (Player playerPosition : Player.values()) {
if (couldHaveCard(playerPosition, card)) {
possessionCount++;
}
}
if (couldLieInSkat(card)) {
possessionCount++;
}
return possessionCount;
}
/**
* Checks whether a card is still outstanding
*
* @param card
* Card to check
* @return TRUE if the card is still outstanding
*/
public final boolean isCardOutstanding(final Card card) {
return !isCardPlayed(card);
}
/**
* Checks whether a card was played already
*
* @param card
* Card to check
* @return TRUE if the card was played
*/
public final boolean isCardPlayed(final Card card) {
return playedCards.get(Player.FOREHAND).contains(card)
|| playedCards.get(Player.MIDDLEHAND).contains(card)
|| playedCards.get(Player.REARHAND).contains(card);
}
/**
* Checks whether a card was played by a player
*
* @param player
* Player ID
* @param card
* Card
* @return TRUE if the card was played by the player
*/
public final boolean isCardPlayedBy(final Player player, final Card card) {
return playedCards.get(player).contains(card);
}
/**
* Checks whether a card was played by another player in the current trick
*
* @param otherPlayer
* Player position of the other player
* @param card
* Card played
* @return TRUE if the card was played by the other player in the current
* trick
*/
public final boolean isCardPlayedInTrick(Player otherPlayer, Card card) {
boolean result = false;
if (getPlayerPosition().getLeftNeighbor() == otherPlayer) {
if (card.equals(leftPlayerTrickCard)) {
result = true;
}
} else if (getPlayerPosition().getRightNeighbor() == otherPlayer) {
if (card.equals(rightPlayerTrickCard)) {
result = true;
}
}
return result;
}
/**
* @return the handGame
*/
public final boolean isHandGame() {
return handGame;
}
/**
* @return the ouvertGame
*/
public final boolean isOuvertGame() {
return ouvertGame;
}
/**
* Checks whether a card is on the players hand
*
* @param card
* Card
* @return TRUE, if the card is on the players hand
*/
public final boolean isOwnCard(Card card) {
return ownCards.contains(card);
}
/**
* @return the schneiderAnnounced
*/
public final boolean isSchneiderAnnounced() {
return schneiderAnnounced;
}
/**
* @return the schwarzAnnounced
*/
public final boolean isSchwarzAnnounced() {
return schwarzAnnounced;
}
/**
* @see Object#toString()
*/
@Override
public String toString() {
StringBuffer result = new StringBuffer();
result.append("Played cards:\n"); //$NON-NLS-1$
for (Suit suit : Suit.values()) {
result.append(suit.shortString()).append(": "); //$NON-NLS-1$
for (Rank rank : Rank.values()) {
if (playedCards.get(Player.FOREHAND).contains(
Card.getCard(suit, rank))
|| playedCards.get(Player.MIDDLEHAND).contains(
Card.getCard(suit, rank))
|| playedCards.get(Player.REARHAND).contains(
Card.getCard(suit, rank))) {
result.append(suit.shortString())
.append(rank.shortString()).append(' ');
} else {
result.append("-- "); //$NON-NLS-1$
}
}
result.append('\n');
}
return result.toString();
}
}