/**
* 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 java.util.ArrayList;
import org.apache.log4j.Logger;
import org.jskat.util.Card;
import org.jskat.util.CardList;
import org.jskat.util.GameType;
import org.jskat.util.Rank;
import org.jskat.util.Suit;
public class AlgorithmGrand extends AbstractAlgorithmAI {
private static final Logger log = Logger.getLogger(AlgorithmGrand.class);
AlgorithmGrand(final AlgorithmAI p, GameType pGameType) {
super(p, pGameType);
log.debug("Defining player <" + myPlayer.getPlayerName() + "> as "
+ this.getClass().getName());
}
@Override
protected Card startGame() {
log.debug("Grand starts Game: " + knowledge.getPlayerPosition());
return playStartGameCard(knowledge.getOwnCards(),
knowledge.getTrickCards(), oPlayedCards, oNotOpponentCards,
oSituation);
}
@Override
protected Card playForehandCard() {
log.debug("Grand plays Forehand-Card: " + knowledge.getPlayerPosition());
return playForehandCard(knowledge.getOwnCards(),
knowledge.getTrickCards(), oPlayedCards, oNotOpponentCards,
oSituation);
}
@Override
protected Card playMiddlehandCard() {
log.debug("Grand plays Middlehand-Card: "
+ knowledge.getPlayerPosition());
return playMiddlehandCard(
myPlayer.getPlayableCards(knowledge.getTrickCards()),
knowledge.getTrickCards(), oPlayedCards, oNotOpponentCards,
oSituation);
}
@Override
protected Card playRearhandCard() {
log.debug("Grand plays Rearhand-Card: " + knowledge.getPlayerPosition());
return playRearhandCard(
myPlayer.getPlayableCards(knowledge.getTrickCards()),
knowledge.getTrickCards(), oPlayedCards, oNotOpponentCards,
oSituation);
}
@Override
public CardList discardSkat(BidEvaluator pBid) {
log.debug("discardSkat");
CardList tDiscardCards = discardSkatCards(pBid, knowledge.getOwnCards());
// knowledge.removeOwnCards(tDiscardCards.getImmutableCopy());
// handle wrong discarding
while (tDiscardCards.get(0).equals(tDiscardCards.get(1))) {
tDiscardCards.remove(1);
tDiscardCards.add(knowledge.getOwnCards().get(
random.nextInt(knowledge.getOwnCards().size())));
}
CardList cardsAfterDiscarding = new CardList(knowledge.getOwnCards());
cardsAfterDiscarding.removeAll(tDiscardCards);
oSituation.setCardsAfterDiscarding(cardsAfterDiscarding);
return tDiscardCards;
}
// static methods for creating JUnit-tests and test cardplaybehavior
public static Card playStartGameCard(CardList pCards, CardList pTrickCards,
CardList pPlayedCards, CardList pNotOpponentCards,
Situation pSituation) {
pCards.sort(pSituation.getGameType());
if (Helper.countJacks(pCards) == 4) {
return pCards.get((int) (Math.random() * 3 + 1));
} else if (pCards.contains(Card.CJ)
&& (pCards.contains(Card.HJ) || pCards.contains(Card.DJ))) {
return pCards.get(0);
} else if (pCards.get(1) == Card.SJ) {
return pCards.get(1);
} else if (pCards.get(0) == Card.SJ) {
return pCards
.get((int) (Math.random() * Helper.countJacks(pCards)));
}
return playForehandCard(pCards, pTrickCards, pPlayedCards,
pNotOpponentCards, pSituation);
}
public static Card playForehandCard(CardList pCards, CardList pTrickCards,
CardList pPlayedCards, CardList pNotOpponentCards,
Situation pSituation) {
pCards.sort(pSituation.getGameType());
CardList possibleCards = new CardList();
// 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
}
// Wenn nur noch Buben und eine weitere Karte -> weitere Karte wird
// zuletzt gespielt
if (pCards.size() == Helper.countJacks(pCards) + 1) {
return pCards.get(0);
} else if (Helper.countJacks(pNotOpponentCards) == 3
&& pCards.get(0).getRank() == Rank.JACK) {
// Wenn schlagbar
if (pCards.get(0).getSuit() == Suit.CLUBS
|| pCards.get(0).getSuit() == Suit.SPADES
&& pPlayedCards.contains(Card.CJ)
|| pCards.get(0).getSuit() == Suit.HEARTS
&& pPlayedCards.contains(Card.CJ)
&& pPlayedCards.contains(Card.SJ)) {
return pCards.get(0);
}
// Wenn nicht schlagbar aber 4 Asse oder 10Duo
if (tAcesCount + t10DuoCount + pSituation.getBlankSuits().size() == 4) {
// lange Suit spielen, bis gestochen wird (in 2 von 3 Faellen
// wird lange Farbe gespielt)
if (pSituation.getRandomInt() != 0
&& pCards.getSuitCount(pSituation.getLongestSuit(),
false) > 0) {
// Wenn hoechste Karte der Farbe vorhanden ist
// UND bei den Gegnern noch mindestens 2 Karten der Farbe
// UND beide Gegner sind noch nicht Blank auf der Farbe
// (Wenn sie keine Truempfe mehr besitzen wird auch mit
// niedrigen Karten die Farbe leer gespielt)
if (Helper.isHighestSuitCard(
pCards.get(pCards.getFirstIndexOfSuit(
pSituation.getLongestSuit(), false)),
pPlayedCards, pTrickCards)
&& 7 - pNotOpponentCards.getSuitCount(
pSituation.getLongestSuit(), false) >= 2
&& !pSituation.isLeftPlayerBlankOnColor(pSituation
.getLongestSuit())
&& !pSituation.isRightPlayerBlankOnColor(pSituation
.getLongestSuit())) {
return pCards.get(pCards.getFirstIndexOfSuit(
pSituation.getLongestSuit(), false));
}
return pCards.get(pCards.getLastIndexOfSuit(
pSituation.getLongestSuit(), false));
}
// erst alle Karten spielen, wo die zweite Karte anschließend
// die beste ist
for (Suit lSuit : Suit.values()) {
if (pCards.getSuitCount(lSuit, false) > 1) {
Card possibleHighCard = pCards.get(pCards
.getFirstIndexOfSuit(lSuit, false));
Card possibleSecondCard = pCards.get(pCards
.getFirstIndexOfSuit(lSuit, false) + 1);
CardList tAfterHighCard = new CardList(pPlayedCards);
tAfterHighCard.add(possibleHighCard);
if (Helper.isHighestSuitCard(possibleHighCard, null,
pPlayedCards, pTrickCards)
&& Helper.isHighestSuitCard(possibleSecondCard,
null, tAfterHighCard, pTrickCards)) {
return possibleHighCard;
}
}
}
}
}
// Wenn nicht schlagbar aber selbst noch 2 Buben
else if (Helper.countJacks(pNotOpponentCards) == 3
&& pCards.get(1).getRank() == Rank.JACK) {
return pCards.get(0);
}
// Wenn keine Buben mehr beim Gegner
else if (Helper.countJacks(pNotOpponentCards) == 4) {
for (Suit lSuit : Suit.values()) {
int suitCount = pCards.getSuitCount(lSuit, false);
if (suitCount == 0) {
continue;
}
Card possibleHighCard = pCards.get(pCards.getFirstIndexOfSuit(
lSuit, false));
Card possibleLowCard = pCards.get(pCards.getLastIndexOfSuit(
lSuit, false));
if (Helper.isHighestSuitCard(possibleHighCard,
pSituation.getGameType(), pPlayedCards, pTrickCards)
&& !(suitCount == 2 && possibleLowCard.getPoints() == 0)) {
possibleCards.add(possibleHighCard);
}
}
}
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
return getRandomAllowedCard(pCards, null, pSituation.getGameType());
}
public static Card playMiddlehandCard(CardList pCards,
CardList pTrickCards, CardList pPlayedCards,
CardList pNotOpponentCards, Situation pSituation) {
pCards.sort(pSituation.getGameType());
Card tCardToBeat = pTrickCards.get(0);
Suit tSuit = tCardToBeat.getSuit();
CardList possibleCards = new CardList();
// Wenn ein Bube aufgespielt wurde
if (tCardToBeat.getRank() == Rank.JACK
&& pCards.get(0).getRank() == Rank.JACK) {
if (pCards.get(0).beats(pSituation.getGameType(), tCardToBeat)) {
return getLowestBeatingCard(pCards, pSituation.getGameType(),
tCardToBeat);
}
return pCards.get(Helper.countJacks(pCards) - 1);
}
// Wenn Karte bedient werden kann/muss
if (pCards.getSuitCount(tSuit, false) > 0) {
// Wenn schlagbar
if (Helper.isHighestSuitCard(
pCards.get(pCards.getFirstIndexOfSuit(tSuit, false)),
pPlayedCards, pTrickCards)) {
return pCards.get(pCards.getFirstIndexOfSuit(tSuit, false));
}
// Wenn nicht schlagbar
return pCards.get(pCards.getLastIndexOfSuit(tSuit, false));
}
// Wenn Farbe blank ist
// Wenn >= 10 Punkte im Stich && (kein Bube beim Gegner || CJ und noch
// mindestens ein weiterer Bube)
if (tCardToBeat.getPoints() >= 10
&& (Helper.countJacks(pNotOpponentCards) == 4 || Helper
.countJacks(pNotOpponentCards) >= 2
&& pCards.contains(Card.CJ)
&& pCards.get(1).getRank() == Rank.JACK)) {
return pCards.get(Helper.countJacks(pCards) - 1);
}
possibleCards = getPossibleMaxValueCards(pCards, 0);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
possibleCards = getPossibleMaxValueCards(pCards, 3);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
possibleCards = getPossibleMaxValueCards(pCards, 4);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
return getRandomAllowedCard(pCards, null, pSituation.getGameType());
}
public static Card playRearhandCard(CardList pCards, CardList pTrickCards,
CardList pPlayedCards, CardList pNotOpponentCards,
Situation pSituation) {
pCards.sort(pSituation.getGameType());
Card tForehandCard = pTrickCards.get(0);
Suit tSuit = tForehandCard.getSuit();
Card tMiddlehandCard = pTrickCards.get(1);
CardList possibleCards = new CardList();
Card tCardToBeat = tForehandCard;
if (tMiddlehandCard.beats(pSituation.getGameType(), tCardToBeat)) {
tCardToBeat = tMiddlehandCard;
}
// Wenn Vorhand-Karte bedient werden kann/muss
if (pCards.getSuitCount(tSuit, false) > 0) {
Card possibleHighCard = pCards.get(pCards.getFirstIndexOfSuit(
tSuit, false)); // highest Card
int possibleBeatingCardIndex = pCards.getLastIndexOfSuit(tSuit,
false); // lowest Card
if (pCards.getSuitCount(tSuit, false) == 1) {
return possibleHighCard;
}
// Wenn schlagbar
if (possibleHighCard.beats(pSituation.getGameType(), tCardToBeat)) {
while (!pCards.get(possibleBeatingCardIndex).beats(
pSituation.getGameType(), tCardToBeat)) {
possibleBeatingCardIndex--;
}
return pCards.get(possibleBeatingCardIndex);
}
// Wenn nicht schlagbar
return pCards.get(pCards.getLastIndexOfSuit(tSuit, false));
}
// Wenn Farbe blank ist
else {
// Wenn >= 10 Punkte im Stich && (kein Bube beim Gegner || CJ und
// noch mindestens ein weiterer Bube)
if (tForehandCard.getPoints() + tMiddlehandCard.getPoints() >= 10
&& (Helper.countJacks(pNotOpponentCards) == 4 || Helper
.countJacks(pNotOpponentCards) >= 2
&& pCards.contains(Card.CJ)
&& pCards.get(1).getRank() == Rank.JACK)) {
return pCards.get(Helper.countJacks(pCards) - 1);
}
// Wenn < 10 -> Farbe abwerfen
else {
possibleCards = getPossibleMaxValueCards(pCards, 0);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
possibleCards = getPossibleMaxValueCards(pCards, 3);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
possibleCards = getPossibleMaxValueCards(pCards, 4);
if (!possibleCards.isEmpty()) {
return playRandomCard(possibleCards);
}
}
}
return getRandomAllowedCard(pCards, null, pSituation.getGameType());
}
public static CardList discardSkatCards(BidEvaluator pBid,
CardList pOwnCards) {
CardList tCards = new CardList(pOwnCards);
tCards.sort(GameType.GRAND);
CardList tDiscardCards = new CardList();
CardList t1ToDiscard = new CardList();
ArrayList<CardList> t2ToDiscard = new ArrayList<CardList>();
CardList t1PossibleDiscard = new CardList();
ArrayList<CardList> t2PossibleDiscard = new ArrayList<CardList>();
for (Suit lSuit : Suit.values()) {
if (tCards.getSuitCount(lSuit, false) == 0) {
continue;
}
int lFirstIndex = tCards.getFirstIndexOfSuit(lSuit, false);
int lLastIndex = tCards.getLastIndexOfSuit(lSuit, false);
Card lFirstCard = tCards.get(lFirstIndex);
Card lLastCard = tCards.get(lLastIndex);
// Wenn nur eine Karte der Farbe und diese ist nicht das Ass
if (lFirstIndex == lLastIndex) {
if (lFirstCard.getRank() != Rank.ACE) {
// Wenn sie Punkte bringt dann druecken
if (lFirstCard.getPoints() != 0) {
t1ToDiscard.add(lFirstCard);
} else {
tDiscardCards.add(lFirstCard);
}
}
}
// Wenn der Spieler 2 Karten der Farbe auf der Hand hat
else if (lLastIndex - lFirstIndex == 1) {
// Wenn die hohe Karte das Ass ist
if (lFirstCard.getRank() == Rank.ACE) {
// Wenn die niedrige Karte die 10 oder der K ist
if (tCards.get(lFirstIndex + 1).getRank() == Rank.TEN
|| tCards.get(lFirstIndex + 1).getRank() == Rank.KING) {
continue;
} else {
t1ToDiscard.add(lLastCard);
}
}
// Wenn die hohe Karte die 10 ist
else if (lFirstCard.getRank() == Rank.TEN) {
continue;
}
}
// Wenn der Spieler >=3 Karten der Farbe auf der Hand hat
else {
if (lFirstCard.getRank() == Rank.ACE) {
// Wenn A, 10, K -> A und 10 druecken
if (lLastCard.getRank() == Rank.KING) {
CardList t = new CardList();
t.add(lFirstCard);
t.add(tCards.get(lFirstIndex + 1));
t2ToDiscard.add(t);
t1PossibleDiscard.add(lFirstCard);
}
// Wenn zweite Karte eine 10 ->
else if (tCards.get(lFirstIndex + 1).getRank() == Rank.TEN) {
t1ToDiscard.add(lFirstCard);
} else {
CardList t = new CardList();
t.add(lFirstCard);
t.add(tCards.get(lFirstIndex + 1));
t2PossibleDiscard.add(t);
t1PossibleDiscard.add(lFirstCard);
}
} else if (lFirstCard.getRank() == Rank.TEN) {
// Wenn 10, K -> 10 druecken
if (lLastCard.getRank() == Rank.KING) {
t1ToDiscard.add(lFirstCard);
} else if (tCards.get(lFirstIndex + 1).getRank() == Rank.QUEEN) {
t1ToDiscard.add(tCards.get(lFirstIndex + 1));
} else {
t1PossibleDiscard.add(lLastCard);
}
}
CardList t = new CardList();
t.add(lFirstCard);
t.add(tCards.get(lFirstIndex + 1));
t2PossibleDiscard.add(t);
}
}
// Wenn 2x blank moeglich
while (tDiscardCards.size() > 2) {
tDiscardCards.remove((int) (Math.random() * tDiscardCards.size()));
}
if (tDiscardCards.size() == 0) {
for (Card lCardList : t1ToDiscard) {
tDiscardCards.add(lCardList);
}
for (Card lCardList : t1PossibleDiscard) {
tDiscardCards.add(lCardList);
}
while (tDiscardCards.size() > 2) {
tDiscardCards.remove(tDiscardCards.size() - 1);
}
}
if (tDiscardCards.size() == 1) {
if (!t1ToDiscard.isEmpty()) {
tDiscardCards
.add(t1ToDiscard.get((int) (Math.random() * t1ToDiscard
.size())));
}
if (tDiscardCards.size() != 2 && !t1PossibleDiscard.isEmpty()) {
tDiscardCards
.add(t1PossibleDiscard.get((int) (Math.random() * t1PossibleDiscard
.size())));
}
}
if (tDiscardCards.size() != 2 && !t2ToDiscard.isEmpty()) {
tDiscardCards = t2ToDiscard.get((int) (Math.random() * t2ToDiscard
.size()));
}
if (tDiscardCards.size() != 2 && !t2PossibleDiscard.isEmpty()) {
tDiscardCards = t2PossibleDiscard
.get((int) (Math.random() * t2PossibleDiscard.size()));
}
return tDiscardCards;
}
}