import java.util.ArrayList; import java.util.Random; public class RuleController { public static CardParser parser = new CardParser(); /** IMPORTANT * As long as one of the following three is matchable, the play will be considered as legal */ private String currentMatchableColor; private String currentMatchableNumber; private String currentMatchableSymbol = "none"; // Card played in the first round can only match by either color or number private int nextPlayerSkipLevel = 0; private int cumulativePenaltyDraw = 0; private boolean isClockWise = true; // if clockwise, next player will be the element in player list public RuleController() { // initialize first matched color and number boolean chosen = false; while (!chosen) { Random rand = new Random(); int cardID = rand.nextInt(100) + 1; // generate a number from 1 - 100. these are colored cards String desc = parser.parseCardID(cardID); String[] result = parser.parseCardDescription(desc); String color = result[0]; String type = result[1]; String content = result[2]; if (type.equals("num")) { currentMatchableColor = color; currentMatchableNumber = content; chosen = true; } } } /** * judge whether a pending player should be skipped * @param player the pending player * @return bool */ public boolean checkSkipandDraw(Player player) { if (nextPlayerSkipLevel == 3) { assert cumulativePenaltyDraw == 0; nextPlayerSkipLevel = 0; // the next player should no longer be skipped return true; } else if (nextPlayerSkipLevel != 0) { // player will be skipped and forced to draw cards // unless they have draw2 / wildDraw4 cards assert cumulativePenaltyDraw != 0; boolean toSkip = player.findLegalCard().isEmpty(); if (toSkip) { nextPlayerSkipLevel = 0; player.drawCards(cumulativePenaltyDraw); resetPenaltyDraw(); } return toSkip; } else { return false; } } /** * Given a card played by the user, judge whether this play is valid * that is, all cards must be played following the rule * @return whether the play is valid */ public boolean isValidPlay(Player player, int cardID, boolean updateIfValid) { assert(player != null); String cardDescription = parser.parseCardID(cardID); String[] result = parser.parseCardDescription(cardDescription); String color = result[0]; String type = result[1]; String content = result[2]; boolean valid = false; if (nextPlayerSkipLevel == 3) { valid = false; } else if (nextPlayerSkipLevel == 2) { if (content.equals("wildDraw4")) { valid = checkDraw4IsLegal(player); } else { valid = false; } } else if (nextPlayerSkipLevel == 1) { if (content.equals("wildDraw4")) { valid = checkDraw4IsLegal(player); } else { return content.equals("draw2"); // draw 2 is always allowed in this case } } else { // if not skipped, first consider wild card if (content.equals("wild")) { valid = true; // can be used unconditionally } // then other colored cards if (checkAttrMatch(currentMatchableColor, color)) valid = true; if (checkAttrMatch(currentMatchableNumber, content) || checkAttrMatch(currentMatchableSymbol, content)) valid = true; // check wildDraw4 at last if (content.equals("wildDraw4")) { valid = checkDraw4IsLegal(player); } } if (valid && updateIfValid) { updateRule(color, type, content); } return valid; } private void updateRule(String color, String type, String content) { setMatchableColor(color); // wildcard "NA" - this will be updated later by player declaring the color if (type.equals("sym")) { if (content.equals("skip")) { setNextPlayerSkiplevel(3); // the states after the skip.. (supposed skipping is done) currentMatchableSymbol = content; //?? currentMatchableNumber = "none"; //?? } else if (content.equals("draw2")) { // next round match by either COL or SYM setNextPlayerSkiplevel(1); increasePenaltyDraw(2); currentMatchableSymbol = content; currentMatchableNumber = "none"; } else if (content.equals("wildDraw4")) { // next round match only by COL setNextPlayerSkiplevel(2); increasePenaltyDraw(4); currentMatchableSymbol = "none"; currentMatchableNumber = "none"; } else if (content.equals("reverse")) { // next round match by either COL or SYM changeGameOrder(); setNextPlayerSkiplevel(0); currentMatchableSymbol = content; currentMatchableNumber = "none"; } else if (content.equals("wild")) { // next round can only match by COL currentMatchableSymbol = "none"; currentMatchableNumber = "none"; } } else if (type.equals("num")) { // next round match by either COL or NUM setNextPlayerSkiplevel(0); currentMatchableNumber = content; currentMatchableSymbol = "none"; } } private boolean checkAttrMatch(String legalString, String stringToCheck) { return legalString.equals("all") || stringToCheck.equals(legalString); } /** * Check whether a player have currently matchable **color** when attempting to play wild draw 4 */ private boolean checkDraw4IsLegal(Player player) { ArrayList<Integer> cards = player.getCards(); for (int i = 0; i < cards.size(); i++) { int cardID = cards.get(i); String cardDescription = parser.parseCardID(cardID); String color = parser.parseCardDescription(cardDescription)[0]; // color of wild cards are NA if (color.equals(currentMatchableColor)) { return false; } } return true; } /** * Getter and Setter for Allowed number */ public String getMatchableNumber() { return currentMatchableNumber; } public void setMatchableNumber(String number) { currentMatchableNumber = number; } /** * Getter and Setter for Allowed Symbol */ public String getMatchableSymbol() { return currentMatchableSymbol; } public void setMatchableSymbol(String symbol) { currentMatchableSymbol = symbol; } /** * Getter and Setter for Allowed Color */ public String getMatchableColor() { return currentMatchableColor; } public void setMatchableColor(String color) { currentMatchableColor = color; } /** * Getter and setter for nextPlayerIsSkipped * level 0: next player won't be skipped * level 1: next player can play a either a draw2 card or a wildDraw4 card to avoid being skipped * level 2: next player can only play a wildDraw4 card of any color to avoid being skipped * level 3: next player will be skipped anyway. */ public int getNextPlayerSkiplevel() { return nextPlayerSkipLevel; } public void setNextPlayerSkiplevel(int level) { nextPlayerSkipLevel = level; } private String descSkipLevel() { if (nextPlayerSkipLevel == 0) { return("level 0: player won't be skipped"); } else if (nextPlayerSkipLevel == 1) { return("level 1: player can play a either a draw2 card or a wildDraw4 card to avoid being skipped"); } else if (nextPlayerSkipLevel == 2) { return("level 2: next player can only play a wildDraw4 card of any color to avoid being skipped"); } else { return("level 3: next player will be skipped anyway."); } } /** * Getter and setter for cumulativePenaltyDraw * @return */ public int getPenaltyDraw() { return cumulativePenaltyDraw; } public void resetPenaltyDraw() { cumulativePenaltyDraw = 0; } public void increasePenaltyDraw(int num) { cumulativePenaltyDraw += num; } /** * Getter and setter for isClockwise * @return */ public boolean getIsClockwise() { return isClockWise; } public void changeGameOrder() { isClockWise = !isClockWise; } public void reportCurrentState(Game gameController) { System.out.println("============================= Game State Report ========================================"); System.out.println("Current matchable color : " + getMatchableColor()); System.out.println("Current matchable number : " + getMatchableNumber()); System.out.println("Current matchable symbol : " + getMatchableSymbol()); System.out.println("Game order : " + (getIsClockwise() ? "clockwise" : "counterclockwise")); System.out.println("Player skip level is " + descSkipLevel()); System.out.println("Player will be forced to draw " + cumulativePenaltyDraw + " cards"); System.out.println("There are " + gameController.gameCardManager.numCardLeft() + " cards in the card pile."); System.out.println("There are " + gameController.gameCardManager.numLeftDiscardPile() + " cards in the discard pile."); } }