Commit 3931667b authored by sphexoo's avatar sphexoo

Refactored main class. Added Statistics class to track wins. Splitted AI...

Refactored main class. Added Statistics class to track wins. Splitted AI functionality in multiple player classes.
parent 79327cf9
......@@ -23,11 +23,16 @@
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="ignore.virus.scanning.warn.message" value="true" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="project.structure.last.edited" value="Modules" />
<property name="project.structure.proportion" value="0.0" />
<property name="project.structure.side.proportion" value="0.0" />
</component>
<component name="RecentsManager">
<key name="CopyClassDialog.RECENTS_KEY">
<recent name="players" />
<recent name="" />
</key>
<key name="CreateClassDialog.RecentsKey">
<recent name="" />
</key>
......@@ -82,41 +87,45 @@
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state x="322" y="93" key="#Project_Structure/0.0.1920.1040@0.0.1920.1040" timestamp="1588687390551" />
<state width="468" height="466" key="DebuggerActiveHint" timestamp="1588936167247">
<state width="468" height="466" key="DebuggerActiveHint" timestamp="1589460391739">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="468" height="466" key="DebuggerActiveHint/0.0.1920.1040@0.0.1920.1040" timestamp="1589460391739" />
<state width="1877" height="385" key="GridCell.Tab.0.bottom" timestamp="1589461662507">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="468" height="466" key="DebuggerActiveHint/0.0.1920.1040@0.0.1920.1040" timestamp="1588936167247" />
<state width="1877" height="227" key="GridCell.Tab.0.bottom" timestamp="1588941343441">
<state width="1877" height="385" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589461662507" />
<state width="1877" height="385" key="GridCell.Tab.0.center" timestamp="1589461662507">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="227" key="GridCell.Tab.0.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1588941343441" />
<state width="1877" height="227" key="GridCell.Tab.0.center" timestamp="1588941343441">
<state width="1877" height="385" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589461662507" />
<state width="1877" height="385" key="GridCell.Tab.0.left" timestamp="1589461662506">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="227" key="GridCell.Tab.0.center/0.0.1920.1040@0.0.1920.1040" timestamp="1588941343441" />
<state width="1877" height="227" key="GridCell.Tab.0.left" timestamp="1588941343441">
<state width="1877" height="385" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589461662506" />
<state width="1877" height="385" key="GridCell.Tab.0.right" timestamp="1589461662507">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="227" key="GridCell.Tab.0.left/0.0.1920.1040@0.0.1920.1040" timestamp="1588941343441" />
<state width="1877" height="227" key="GridCell.Tab.0.right" timestamp="1588941343441">
<state width="1877" height="385" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589461662507" />
<state width="1877" height="352" key="GridCell.Tab.1.bottom" timestamp="1589460582021">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1877" height="227" key="GridCell.Tab.0.right/0.0.1920.1040@0.0.1920.1040" timestamp="1588941343441" />
<state width="1073" height="173" key="GridCell.Tab.1.bottom" timestamp="1588936180713">
<state width="1877" height="352" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1589460582021" />
<state width="1877" height="352" key="GridCell.Tab.1.center" timestamp="1589460582021">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1073" height="173" key="GridCell.Tab.1.bottom/0.0.1920.1040@0.0.1920.1040" timestamp="1588936180713" />
<state width="1073" height="173" key="GridCell.Tab.1.center" timestamp="1588936180713">
<state width="1877" height="352" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1589460582021" />
<state width="1877" height="352" key="GridCell.Tab.1.left" timestamp="1589460582021">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1073" height="173" key="GridCell.Tab.1.center/0.0.1920.1040@0.0.1920.1040" timestamp="1588936180713" />
<state width="1073" height="173" key="GridCell.Tab.1.left" timestamp="1588936180713">
<state width="1877" height="352" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1589460582021" />
<state width="1877" height="352" key="GridCell.Tab.1.right" timestamp="1589460582021">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1073" height="173" key="GridCell.Tab.1.left/0.0.1920.1040@0.0.1920.1040" timestamp="1588936180713" />
<state width="1073" height="173" key="GridCell.Tab.1.right" timestamp="1588936180713">
<state width="1877" height="352" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1589460582021" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup" timestamp="1589461615549">
<screen x="0" y="0" width="1920" height="1040" />
</state>
<state width="1073" height="173" key="GridCell.Tab.1.right/0.0.1920.1040@0.0.1920.1040" timestamp="1588936180713" />
<state x="623" y="225" width="672" height="678" key="search.everywhere.popup/0.0.1920.1040@0.0.1920.1040" timestamp="1589461615549" />
</component>
</project>
\ No newline at end of file
import de.upb.isml.thegamef2f.engine.GameHistory;
import de.upb.isml.thegamef2f.engine.board.Game;
import de.upb.isml.thegamef2f.engine.player.BasicPlayer;
import de.upb.isml.thegamef2f.engine.player.Player;
import de.upb.isml.thegamef2f.engine.player.RandomPlayer;
import players.BasicPlayer;
import players.BasicPlayerV0;
import players.OptimizeTurnPlayer;
import players.RandomPlayer;
import util.Extensions.Statistics;
import java.util.Random;
public class MainClass {
public static void main(String[] args) {
var numGames = 100000.0;
var RandomPlayerWins = 0.0;
var BasicPlayerWins = 0.0;
// USER INPUT
var numGames = 100000;
Player p1 = new BasicPlayer();
Player p2 = new BasicPlayerV0();
Statistics stats = new Statistics(p1, p2);
Random rand = new Random();
for (int i = 0; i < numGames; i = i + 1) // run specified number of games
{
if (i % 1000 == 0)
{
System.out.println(i);
System.out.println("Games played: " + i);
}
Game game;
if (i % 2 == 0) // switch starting player after every game
{
game = new Game(new RandomPlayer(), new BasicPlayer(), rand.nextLong());
game = new Game(p1, p2, rand.nextLong());
}
else
{
game = new Game(new BasicPlayer(), new RandomPlayer(), rand.nextLong());
game = new Game(p2, p1, rand.nextLong());
}
Player winner = game.simulate(); // run games simulation
//game.getHistory().printHistory();
// track win statistics
if (winner.getName().equals("RandomPlayer"))
{
RandomPlayerWins += 1;
}
else if (winner.getName().equals("BasicPlayer"))
{
BasicPlayerWins += 1;
}
stats.TrackGame(winner, game.getHistory());
}
System.out.println("RandomPlayer Wins: " + RandomPlayerWins + " (" + RandomPlayerWins / numGames * 100.0 + " %)");
System.out.println("BasicPlayer Wins: " + BasicPlayerWins + " (" + BasicPlayerWins / numGames * 100.0 + " %)");
stats.PrintStats();
}
}
package players;
import java.util.*;
import de.upb.isml.thegamef2f.engine.CardPosition;
import de.upb.isml.thegamef2f.engine.GameState;
import de.upb.isml.thegamef2f.engine.Move;
import de.upb.isml.thegamef2f.engine.Placement;
import de.upb.isml.thegamef2f.engine.board.Card;
import de.upb.isml.thegamef2f.engine.player.Player;
import util.Extensions.ExtendedPlacement;
import util.Extensions.*;
/**
* Basic Player AI
* based on RandomPlayer from Alexander Tornede
* @author fjsauer
*
* Functionality:
* Special Method hierarchy:
* IsBackwardsTrick()
* IsHelpEnemyWithoutConsequences()
* IsSelectMoveByOrder()
* Choose random placement, if nothing else applies.
* Other rules:
* Do not play more than two cards per move.
*/
public class BasicPlayer implements Player {
private Random random;
private GameState latestGameState;
@Override
public void initialize(long randomSeed) {
this.random = new Random(randomSeed);
}
private Placement IsBackwardsTrick(GameState currentGameState, List<Placement> validPlacements)
{
/* checks if one of the valid placements is a BackwardsTrick placement.
* If true, placement is returned
* If false, null is returned */
for (Placement placement : validPlacements)
{
if( (currentGameState.getTopCardOnOwnAscendingDiscardPile().getNumber() == placement.getCard().getNumber() + 10 && placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE) ||
(currentGameState.getTopCardOnOwnDescendingDiscardPile().getNumber() == placement.getCard().getNumber() - 10 && placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE) )
{
return placement;
}
}
return null;
}
private Placement IsHelpEnemyWithoutConsequences(GameState currentGameState, List<Placement> validPlacements)
{
/* checks if one of the valid placements is a HelpEnemyWithoutConsequences placement.
* If true, placement is returned
* If false, null is returned */
for (Placement placement : validPlacements)
{
if( (currentGameState.getTopCardOnOpponentsAscendingDiscardPile().getNumber() == placement.getCard().getNumber() + 1 && placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) ||
(currentGameState.getTopCardOnOpponentsDescendingDiscardPile().getNumber() == placement.getCard().getNumber() - 1 && placement.getPosition() == CardPosition.OPPONENTS_DESCENDING_DISCARD_PILE) )
{
return placement;
}
}
return null;
}
private Placement IsSelectMoveByOrder(GameState currentGameState, List<Placement> validPlacements)
{
Placement bestPlacementASC = null;
Placement bestPlacementDES = null;
for (Placement placement : validPlacements)
{
if (placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE)
{
if (bestPlacementASC == null)
{
bestPlacementASC = placement;
}
if (bestPlacementASC.getCard().getNumber() > placement.getCard().getNumber())
{
bestPlacementASC = placement;
}
}
else if (placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE)
{
if (bestPlacementDES == null)
{
bestPlacementDES = placement;
}
if (bestPlacementDES.getCard().getNumber() < placement.getCard().getNumber())
{
bestPlacementDES = placement;
}
}
}
int rand = random.nextInt(2);
if (rand == 0 && bestPlacementASC != null)
{
return bestPlacementASC;
}
else if (rand == 1 && bestPlacementDES != null)
{
return bestPlacementDES;
}
return null;
}
@Override
public Move computeMove(GameState newGameState) {
latestGameState = newGameState;
boolean placedOnOpponentsPiles = false;
List<Placement> placementsOfMove = new ArrayList<>();
// as long as we still have hand cards
while (!latestGameState.getHandCards().isEmpty()) {
List<Placement> validPlacements = new ArrayList<>();
// compute all valid placements
for (Card card : latestGameState.getHandCards()) {
for (CardPosition position : CardPosition.values()) {
Placement placement = new Placement(card, position);
if (isPlacementValid(placement, latestGameState, !placedOnOpponentsPiles)) {
validPlacements.add(placement);
}
}
}
// if we cannot find a valid placement anymore, we can stop here and return the
// ones we have so far
if (validPlacements.isEmpty()) {
return new Move(placementsOfMove);
}
//Pick next placement
Placement nextPlacement = null;
nextPlacement = IsBackwardsTrick(latestGameState, validPlacements); // perform backwards trick if available
if (nextPlacement == null)
nextPlacement = IsHelpEnemyWithoutConsequences(latestGameState, validPlacements); // perform help enemy if available
if (nextPlacement == null)
nextPlacement = IsSelectMoveByOrder(latestGameState, validPlacements); // perform card sorting to choose best possible card
if (nextPlacement == null)
nextPlacement = validPlacements.get(random.nextInt(validPlacements.size())); // pick random move if no special rule applied
if (nextPlacement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE
|| nextPlacement.getPosition() == CardPosition.OPPONENTS_DESCENDING_DISCARD_PILE) {
placedOnOpponentsPiles = true;
}
// add this random placement to the placements for our move
placementsOfMove.add(nextPlacement);
// return if two moves are selected
if (placementsOfMove.size() >= 2)
{
return new Move(placementsOfMove);
}
// update the view we have on the game to make sure that the next set of valid
// placements is indeed valid
latestGameState = computeNewGameStateAfterPlacement(latestGameState, nextPlacement);
}
return new Move(placementsOfMove);
}
private GameState computeNewGameStateAfterPlacement(GameState gameStatePriorToPlacement, Placement placement) {
List<Card> handCards = new ArrayList<>(gameStatePriorToPlacement.getHandCards());
handCards.remove(placement.getCard());
List<Card> cardsOnOwnAscendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOwnAscendingDiscardPile());
if (placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE) {
cardsOnOwnAscendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOwnDescendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOwnDescendingDiscardPile());
if (placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE) {
cardsOnOwnDescendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOpponentsAscendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOpponentsAscendingDiscardPile());
if (placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) {
cardsOnOpponentsAscendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOpponentsDescendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOpponentsDescendingDiscardPile());
if (placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) {
cardsOnOpponentsDescendingDiscardPile.add(placement.getCard());
}
return new GameState(handCards, cardsOnOwnAscendingDiscardPile, cardsOnOwnDescendingDiscardPile,
cardsOnOpponentsAscendingDiscardPile, cardsOnOpponentsDescendingDiscardPile);
}
private boolean isPlacementValid(Placement placement, GameState gameState, boolean placingOnOpponentPilesAllowed) {
switch (placement.getPosition()) {
case OPPONENTS_ASCENDING_DISCARD_PILE:
return placingOnOpponentPilesAllowed
? canPlaceCardOnOpponentsAscendingDiscardPile(placement.getCard(), gameState)
: false;
case OPPONENTS_DESCENDING_DISCARD_PILE:
return placingOnOpponentPilesAllowed
? canPlaceCardOnOpponentsDescendingDiscardPile(placement.getCard(), gameState)
: false;
case OWN_ASCENDING_DISCARD_PILE:
return canPlaceCardOnOwnAscendingDiscardPile(placement.getCard(), gameState);
case OWN_DESCENDING_DISCARD_PILE:
return canPlaceCardOnOwnDescendingDiscardPile(placement.getCard(), gameState);
}
return false;
}
private boolean canPlaceCardOnOwnAscendingDiscardPile(Card card, GameState gameState) {
return gameState.getTopCardOnOwnAscendingDiscardPile().isSmallerThan(card)
|| gameState.getTopCardOnOwnAscendingDiscardPile().is10LargerThan(card);
}
private boolean canPlaceCardOnOwnDescendingDiscardPile(Card card, GameState gameState) {
return card.isSmallerThan(gameState.getTopCardOnOwnDescendingDiscardPile())
|| card.is10LargerThan(gameState.getTopCardOnOwnDescendingDiscardPile());
}
private boolean canPlaceCardOnOpponentsAscendingDiscardPile(Card card, GameState gameState) {
return card.isSmallerThan(gameState.getTopCardOnOpponentsAscendingDiscardPile());
}
private boolean canPlaceCardOnOpponentsDescendingDiscardPile(Card card, GameState gameState) {
return gameState.getTopCardOnOpponentsDescendingDiscardPile().isSmallerThan(card);
}
@Override
public String toString() {
return getName();
}
}
\ No newline at end of file
package players;
import de.upb.isml.thegamef2f.engine.CardPosition;
import de.upb.isml.thegamef2f.engine.GameState;
import de.upb.isml.thegamef2f.engine.Move;
import de.upb.isml.thegamef2f.engine.Placement;
import de.upb.isml.thegamef2f.engine.board.Card;
import de.upb.isml.thegamef2f.engine.player.Player;
import util.Extensions.ExtendedPlacement;
import util.Extensions.StackValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* Basic Player AI
* based on RandomPlayer from Alexander Tornede
* @author fjsauer
*
* Functionality:
* Special Method hierarchy:
* IsBackwardsTrick()
* IsHelpEnemyWithoutConsequences()
* IsSelectMoveByOrder()
* Choose random placement, if nothing else applies.
* Other rules:
* None
*/
public class BasicPlayerV0 implements Player {
private Random random;
private GameState latestGameState;
@Override
public void initialize(long randomSeed) {
this.random = new Random(randomSeed);
}
private Placement IsBackwardsTrick(GameState currentGameState, List<Placement> validPlacements)
{
/* checks if one of the valid placements is a BackwardsTrick placement.
* If true, placement is returned
* If false, null is returned */
for (Placement placement : validPlacements)
{
if( (currentGameState.getTopCardOnOwnAscendingDiscardPile().getNumber() == placement.getCard().getNumber() + 10 && placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE) ||
(currentGameState.getTopCardOnOwnDescendingDiscardPile().getNumber() == placement.getCard().getNumber() - 10 && placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE) )
{
return placement;
}
}
return null;
}
private Placement IsHelpEnemyWithoutConsequences(GameState currentGameState, List<Placement> validPlacements)
{
/* checks if one of the valid placements is a HelpEnemyWithoutConsequences placement.
* If true, placement is returned
* If false, null is returned */
for (Placement placement : validPlacements)
{
if( (currentGameState.getTopCardOnOpponentsAscendingDiscardPile().getNumber() == placement.getCard().getNumber() + 1 && placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) ||
(currentGameState.getTopCardOnOpponentsDescendingDiscardPile().getNumber() == placement.getCard().getNumber() - 1 && placement.getPosition() == CardPosition.OPPONENTS_DESCENDING_DISCARD_PILE) )
{
return placement;
}
}
return null;
}
private Placement IsSelectMoveByOrder(GameState currentGameState, List<Placement> validPlacements)
{
Placement bestPlacementASC = null;
Placement bestPlacementDES = null;
for (Placement placement : validPlacements)
{
if (placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE)
{
if (bestPlacementASC == null)
{
bestPlacementASC = placement;
}
if (bestPlacementASC.getCard().getNumber() > placement.getCard().getNumber())
{
bestPlacementASC = placement;
}
}
else if (placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE)
{
if (bestPlacementDES == null)
{
bestPlacementDES = placement;
}
if (bestPlacementDES.getCard().getNumber() < placement.getCard().getNumber())
{
bestPlacementDES = placement;
}
}
}
int rand = random.nextInt(2);
if (rand == 0 && bestPlacementASC != null)
{
return bestPlacementASC;
}
else if (rand == 1 && bestPlacementDES != null)
{
return bestPlacementDES;
}
return null;
}
@Override
public Move computeMove(GameState newGameState) {
latestGameState = newGameState;
boolean placedOnOpponentsPiles = false;
List<Placement> placementsOfMove = new ArrayList<>();
// as long as we still have hand cards
while (!latestGameState.getHandCards().isEmpty()) {
List<Placement> validPlacements = new ArrayList<>();
// compute all valid placements
for (Card card : latestGameState.getHandCards()) {
for (CardPosition position : CardPosition.values()) {
Placement placement = new Placement(card, position);
if (isPlacementValid(placement, latestGameState, !placedOnOpponentsPiles)) {
validPlacements.add(placement);
}
}
}
// if we cannot find a valid placement anymore, we can stop here and return the
// ones we have so far
if (validPlacements.isEmpty()) {
return new Move(placementsOfMove);
}
//Pick next placement
Placement nextPlacement = null;
nextPlacement = IsBackwardsTrick(latestGameState, validPlacements); // perform backwards trick if available
if (nextPlacement == null)
nextPlacement = IsHelpEnemyWithoutConsequences(latestGameState, validPlacements); // perform help enemy if available
if (nextPlacement == null)
nextPlacement = IsSelectMoveByOrder(latestGameState, validPlacements); // perform card sorting to choose best possible card
if (nextPlacement == null)
nextPlacement = validPlacements.get(random.nextInt(validPlacements.size())); // pick random move if no special rule applied
if (nextPlacement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE
|| nextPlacement.getPosition() == CardPosition.OPPONENTS_DESCENDING_DISCARD_PILE) {
placedOnOpponentsPiles = true;
}
// add this random placement to the placements for our move
placementsOfMove.add(nextPlacement);
// update the view we have on the game to make sure that the next set of valid
// placements is indeed valid
latestGameState = computeNewGameStateAfterPlacement(latestGameState, nextPlacement);
}
return new Move(placementsOfMove);
}
private GameState computeNewGameStateAfterPlacement(GameState gameStatePriorToPlacement, Placement placement) {
List<Card> handCards = new ArrayList<>(gameStatePriorToPlacement.getHandCards());
handCards.remove(placement.getCard());
List<Card> cardsOnOwnAscendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOwnAscendingDiscardPile());
if (placement.getPosition() == CardPosition.OWN_ASCENDING_DISCARD_PILE) {
cardsOnOwnAscendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOwnDescendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOwnDescendingDiscardPile());
if (placement.getPosition() == CardPosition.OWN_DESCENDING_DISCARD_PILE) {
cardsOnOwnDescendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOpponentsAscendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOpponentsAscendingDiscardPile());
if (placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) {
cardsOnOpponentsAscendingDiscardPile.add(placement.getCard());
}
List<Card> cardsOnOpponentsDescendingDiscardPile = new ArrayList<>(
gameStatePriorToPlacement.getCardsOnOpponentsDescendingDiscardPile());
if (placement.getPosition() == CardPosition.OPPONENTS_ASCENDING_DISCARD_PILE) {
cardsOnOpponentsDescendingDiscardPile.add(placement.getCard());
}
return new GameState(handCards, cardsOnOwnAscendingDiscardPile, cardsOnOwnDescendingDiscardPile,
cardsOnOpponentsAscendingDiscardPile, cardsOnOpponentsDescendingDiscardPile);
}
private boolean isPlacementValid(Placement placement, GameState gameState, boolean placingOnOpponentPilesAllowed) {
switch (placement.getPosition()) {
case OPPONENTS_ASCENDING_DISCARD_PILE:
return placingOnOpponentPilesAllowed
? canPlaceCardOnOpponentsAscendingDiscardPile(placement.getCard(), gameState)
: false;
case OPPONENTS_DESCENDING_DISCARD_PILE:
return placingOnOpponentPilesAllowed
? canPlaceCardOnOpponentsDescendingDiscardPile(placement.getCard(), gameState)
: false;
case OWN_ASCENDING_DISCARD_PILE:
return canPlaceCardOnOwnAscendingDiscardPile(placement.getCard(), gameState);
case OWN_DESCENDING_DISCARD_PILE:
return canPlaceCardOnOwnDescendingDiscardPile(placement.getCard(), gameState);
}
return false;
}
private boolean canPlaceCardOnOwnAscendingDiscardPile(Card card, GameState gameState) {
return gameState.getTopCardOnOwnAscendingDiscardPile().isSmallerThan(card)
|| gameState.getTopCardOnOwnAscendingDiscardPile().is10LargerThan(card);
}