/**
* 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.newalgorithm;
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.SkatConstants;
import org.jskat.util.Suit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The BidEvaluator is the class to generate the acceptable bid value and to
* decide, which game to play after discarding the Skat.
*
* @author Daniel Loreck
*
*/
class BidEvaluator {
private static Logger oLog = LoggerFactory.getLogger(BidEvaluator.class);
/**
* Maximum value that the player will bid
*/
private int oMaxBid = -1;
private GameType oSuggestedGameType = GameType.RAMSCH;
private Suit oSuggestedTrumpSuit = null;
private Player oPlayersPosition;
private boolean oGrandHand = false;
private boolean oGrandSchneider = false;
private boolean oGrandSchwarz = false;
private boolean oGrandOuvert = false;
private boolean oNullHand = false;
private boolean oNullOuvert = false;
private boolean oSuitHand = false;
private boolean oSuitSchneider = false;
/**
* default constructor
*
* @param cards
* hand of the player
*/
BidEvaluator(CardList pCards, Player pPlayersPosition) {
oPlayersPosition = pPlayersPosition;
oLog.debug("Checking out what to bid with [" + pCards + "]"
+ pCards.dumpFlag());
eval(pCards);
}
public void eval(CardList pCards) {
oLog.debug("CARDLISTSIZE: " + pCards.size());
if (pCards.size() < 10) {
oLog.warn("Not enough cards for bid evaluation!");
return;
}
oMaxBid = 0;
// if(check4Grand(pCards)) {
// if(true) {
// int tMultiplier = Helper.getGrandMultiplier(pCards);
// if(oGrandHand) tMultiplier++;
// if(oGrandSchneider) tMultiplier++;
// if(oGrandSchwarz) tMultiplier++;
// if(oGrandOuvert) tMultiplier++;
//
// oSuggestedGameType = GameType.GRAND;
// oMaxBid = tMultiplier *
// SkatConstants.getGameBaseValue(GameType.GRAND, false, false);
// }
// if (check4Null(pCards)) {
// int tMaxBid = SkatConstants.getGameBaseValue(GameType.NULL,
// oNullHand, oNullOuvert);
//
// if (tMaxBid > oMaxBid) {
// oSuggestedGameType = GameType.NULL;
// oMaxBid = tMaxBid;
// }
// }
getHighestSuitMultiplier(pCards);
}
private boolean check4Null(CardList pCards) {
pCards.sort(GameType.NULL);
// Count NullCount
int tNullCount = 0;
tNullCount += getSuitNullLength(pCards, Suit.CLUBS); // Clubs
tNullCount += getSuitNullLength(pCards, Suit.SPADES); // Spades
tNullCount += getSuitNullLength(pCards, Suit.HEARTS); // Hearts
tNullCount += getSuitNullLength(pCards, Suit.DIAMONDS); // Diamonds
if (tNullCount == 10) {
oNullHand = true;
oNullOuvert = true;
}
if (tNullCount >= 7) {
return true;
}
return false;
}
private boolean check4Grand(CardList pCards) {
pCards.sort(GameType.GRAND);
// if not enough Jacks -> no Grand
int tGrandBinary = Helper.getGrandBinary(pCards);
if (!(tGrandBinary > 1024 || tGrandBinary == 832)) {
return false;
}
// Count Jacks
int tJackCount = Helper.countJacks(pCards);
// Count Aces
int tAcesCount = 0;
if (pCards.contains(Card.CA)) {
tAcesCount++; // Clubs
}
if (pCards.contains(Card.SA)) {
tAcesCount++; // Spades
}
if (pCards.contains(Card.HA)) {
tAcesCount++; // Hearts
}
if (pCards.contains(Card.DA)) {
tAcesCount++; // Diamonds
}
// Count 10Duos (10 mit niedriger Karte)
int t10DuoCount = 0;
if (!pCards.contains(Card.CA) && pCards.contains(Card.CT)
&& pCards.getSuitCount(Suit.CLUBS, false) > 1) {
t10DuoCount++; // Clubs
}
if (!pCards.contains(Card.SA) && pCards.contains(Card.ST)
&& pCards.getSuitCount(Suit.SPADES, false) > 1) {
t10DuoCount++; // Spades
}
if (!pCards.contains(Card.HA) && pCards.contains(Card.HT)
&& pCards.getSuitCount(Suit.HEARTS, false) > 1) {
t10DuoCount++; // Hearts
}
if (!pCards.contains(Card.DA) && pCards.contains(Card.DT)
&& pCards.getSuitCount(Suit.DIAMONDS, false) > 1) {
t10DuoCount++; // Diamonds
}
// Count Blanksuits
int tBlankSuits = 0;
if (pCards.getSuitCount(Suit.CLUBS, false) == 0) {
tBlankSuits++; // Clubs
}
if (pCards.getSuitCount(Suit.SPADES, false) == 0) {
tBlankSuits++; // Spades
}
if (pCards.getSuitCount(Suit.HEARTS, false) == 0) {
tBlankSuits++; // Hearts
}
if (pCards.getSuitCount(Suit.DIAMONDS, false) == 0) {
tBlankSuits++; // Diamonds
}
// Count Flutelength
int tFluteCount = 0;
tFluteCount += getSuitFluteLength(pCards, Suit.CLUBS); // Clubs
tFluteCount += getSuitFluteLength(pCards, Suit.SPADES); // Spades
tFluteCount += getSuitFluteLength(pCards, Suit.HEARTS); // Hearts
tFluteCount += getSuitFluteLength(pCards, Suit.DIAMONDS); // Diamonds
// Player has a cardlist to win all tricks
if (pCards.contains(Card.CJ) && pCards.contains(Card.SJ)
&& oPlayersPosition.equals(Player.FOREHAND)
&& tFluteCount + tJackCount == 9 || pCards.contains(Card.CJ)
&& pCards.contains(Card.SJ) && tJackCount >= 3
&& tFluteCount + tJackCount == 10) {
oGrandHand = true;
oGrandSchneider = true;
oGrandSchwarz = true;
oGrandOuvert = true;
return true;
}
// Player has a cardlist to win 9 tricks
if (pCards.contains(Card.CJ) && pCards.contains(Card.SJ)
&& oPlayersPosition.equals(Player.FOREHAND)
&& tFluteCount + tJackCount == 9 || pCards.contains(Card.CJ)
&& pCards.contains(Card.SJ) && tJackCount >= 3
&& tFluteCount + tJackCount == 9) {
oGrandHand = true;
oGrandSchneider = true;
return true;
}
if (tAcesCount + t10DuoCount >= 3
|| tAcesCount + t10DuoCount == 2
&& (tBlankSuits >= 1 && tJackCount >= 2
&& pCards.get(1) == Card.SJ || tFluteCount >= 4
&& tJackCount >= 3 || tFluteCount >= 5
&& pCards.contains(Card.CJ) && pCards.contains(Card.SJ))
|| tFluteCount >= 7 || tFluteCount >= 2 && tBlankSuits == 2) {
return true;
}
return false;
}
private void getHighestSuitMultiplier(CardList pCards) {
Suit tMostFrequentSuitColor = pCards.getMostFrequentSuit();
int tMultiplier = Helper.getSuitMultiplier(pCards,
tMostFrequentSuitColor);
int tNumberOfTrumpCards = pCards.getTrumpCount(tMostFrequentSuitColor);
// Count fast blank
int tBlankCount = 0;
int t1CardCount = 0;
int t2CardCount = 0;
for (Suit s : Suit.values()) {
if (pCards.getSuitCount(s, false) == 0) {
tBlankCount++;
} else if (pCards.getSuitCount(s, false) == 1
&& !pCards.contains(Card.getCard(s, Rank.ACE))) {
t1CardCount++;
} else if (pCards.getSuitCount(s, false) == 2
&& !pCards.contains(Card.getCard(s, Rank.ACE))
&& !pCards.contains(Card.getCard(s, Rank.TEN))) {
t2CardCount++;
}
}
// Count Jacks
int tJackCount = Helper.countJacks(pCards);
// Count nicht Trumpf-Asse oder 10+Lusche
int tWinCardCount = 0;
if (tMostFrequentSuitColor != Suit.CLUBS
&& (pCards.contains(Card.CA) || pCards.contains(Card.CT)
&& pCards.getSuitCount(Suit.CLUBS, false) >= 2)) {
tWinCardCount++; // Clubs
}
if (tMostFrequentSuitColor != Suit.SPADES
&& (pCards.contains(Card.SA) || pCards.contains(Card.ST)
&& pCards.getSuitCount(Suit.SPADES, false) >= 2)) {
tWinCardCount++; // Spades
}
if (tMostFrequentSuitColor != Suit.HEARTS
&& (pCards.contains(Card.HA) || pCards.contains(Card.HT)
&& pCards.getSuitCount(Suit.HEARTS, false) >= 2)) {
tWinCardCount++; // Hearts
}
if (tMostFrequentSuitColor != Suit.DIAMONDS
&& (pCards.contains(Card.DA) || pCards.contains(Card.DT)
&& pCards.getSuitCount(Suit.DIAMONDS, false) >= 2)) {
tWinCardCount++; // Diamonds
}
// Wenn 8 oder mehr Trumpfkarten
if (tNumberOfTrumpCards >= 8 && pCards.contains(Card.CJ)) {
if (tWinCardCount > 0) {
oSuitSchneider = true;
}
oSuitHand = true;
int tMaxBid = (tMultiplier + 1)
* SkatConstants.getGameBaseValue(GameType
.valueOf(tMostFrequentSuitColor.longString()
.toUpperCase()), false, false);
if (tMaxBid > oMaxBid) {
oSuggestedGameType = GameType.valueOf(tMostFrequentSuitColor
.longString().toUpperCase());
oSuggestedTrumpSuit = tMostFrequentSuitColor;
oMaxBid = tMaxBid;
}
}
// Wenn mit 4 und Ass
else if (pCards.contains(Card.CJ) && tMultiplier >= 5
|| tNumberOfTrumpCards >= 5 && tWinCardCount >= 1
&& tBlankCount + t1CardCount > 0 || tNumberOfTrumpCards >= 6
&& (tWinCardCount >= 1 || t1CardCount == 2 && t2CardCount == 1)) {
int tMaxBid = tMultiplier
* SkatConstants.getGameBaseValue(GameType
.valueOf(tMostFrequentSuitColor.longString()
.toUpperCase()), false, false);
if (tMaxBid > oMaxBid) {
oSuggestedGameType = GameType.valueOf(tMostFrequentSuitColor
.longString().toUpperCase());
oSuggestedTrumpSuit = tMostFrequentSuitColor;
oMaxBid = tMaxBid;
}
}
}
private int getSuitNullLength(CardList pCards, Suit pSuit) {
int tNullLength = 0;
if (pCards.contains(Card.getCard(pSuit, Rank.SEVEN))) {
tNullLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.EIGHT))) {
tNullLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.NINE))) {
tNullLength = pCards.getSuitCount(pSuit, true);
} else if (pCards.contains(Card.getCard(pSuit, Rank.TEN))) {
tNullLength = pCards.getSuitCount(pSuit, true);
} else if (pCards.contains(Card.getCard(pSuit, Rank.JACK))) {
tNullLength = pCards.getSuitCount(pSuit, true);
}
} else if (pCards.contains(Card.getCard(pSuit, Rank.NINE))) {
tNullLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.TEN))) {
tNullLength = pCards.getSuitCount(pSuit, true);
} else if (pCards.contains(Card.getCard(pSuit, Rank.JACK))) {
tNullLength = pCards.getSuitCount(pSuit, true);
}
} else {
tNullLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.EIGHT))) {
tNullLength++;
}
if (pCards.contains(Card.getCard(pSuit, Rank.NINE))) {
tNullLength++;
}
}
}
return tNullLength;
}
private int getSuitFluteLength(CardList pCards, Suit pSuit) {
int tFluteLength = 0;
if (pCards.contains(Card.getCard(pSuit, Rank.ACE))) { // ACE
tFluteLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.TEN))) { // TEN
tFluteLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.KING))) { // KING
tFluteLength++;
if (pCards.contains(Card.getCard(pSuit, Rank.QUEEN))) { // QUEEN
tFluteLength++;
}
if (pCards.contains(Card.getCard(pSuit, Rank.NINE))) { // NINE
tFluteLength++;
}
if (pCards.contains(Card.getCard(pSuit, Rank.EIGHT))) { // EIGHT
tFluteLength++;
}
if (pCards.contains(Card.getCard(pSuit, Rank.SEVEN))) { // SEVEN
tFluteLength++;
}
}
}
}
return tFluteLength;
}
/**
* Gets the maximum bid value of the player
*
* @return maximum bid value
*/
public int getMaxBid() {
return oMaxBid;
}
/**
* tells the AI player whether to pick up the skat or not
*
* @return true, if the skat should be picked up;<br>
* false, for a hand game
*/
public boolean canPlayHandGame() {
switch (oSuggestedGameType) {
case GRAND:
return oGrandHand;
case NULL:
return oNullHand;
case CLUBS:
case SPADES:
case HEARTS:
case DIAMONDS:
return oSuitHand;
}
return false;
}
public boolean canPlayOuvert() {
switch (oSuggestedGameType) {
case NULL:
return oNullOuvert;
case GRAND:
if (oGrandSchneider && oGrandHand && oGrandSchwarz) {
return oGrandOuvert;
}
break;
default:
}
return false;
}
public boolean canPlaySchneider() {
switch (oSuggestedGameType) {
case GRAND:
if (oGrandHand) {
return oGrandSchneider;
}
break;
case CLUBS:
case SPADES:
case HEARTS:
case DIAMONDS:
if (oSuitHand) {
return oSuitSchneider;
}
break;
default:
}
return false;
}
public boolean canPlaySchwarz() {
switch (oSuggestedGameType) {
case GRAND:
if (oGrandHand && oGrandSchneider) {
return oGrandSchwarz;
}
break;
default:
}
return false;
}
public GameType getSuggestedGameType() {
return oSuggestedGameType;
}
public Suit getSuggestedTrumpSuit() {
return oSuggestedTrumpSuit;
}
}