diff --git a/out/production/sp21-cs242-assignment1/Card.class b/out/production/sp21-cs242-assignment1/Card.class deleted file mode 100644 index 06ab6b9ad0af4a07908a8de0da93e50ff6186042..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/Card.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/CardManager.class b/out/production/sp21-cs242-assignment1/CardManager.class index 974ab506a519a9115a45ae055c217c3654e2407b..688ff8e76e90e2dbfd7b94fe66d7016924b47aa2 100644 Binary files a/out/production/sp21-cs242-assignment1/CardManager.class and b/out/production/sp21-cs242-assignment1/CardManager.class differ diff --git a/out/production/sp21-cs242-assignment1/CmdUI.class b/out/production/sp21-cs242-assignment1/CmdUI.class new file mode 100644 index 0000000000000000000000000000000000000000..77e01a48c317fef0d98274cbf4e604c9d13d93e3 Binary files /dev/null and b/out/production/sp21-cs242-assignment1/CmdUI.class differ diff --git a/out/production/sp21-cs242-assignment1/FunctionalCard.class b/out/production/sp21-cs242-assignment1/FunctionalCard.class deleted file mode 100644 index a4e16eae874d691483e7e1c6a5672e770310e5f0..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/FunctionalCard.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/GUI.class b/out/production/sp21-cs242-assignment1/GUI.class new file mode 100644 index 0000000000000000000000000000000000000000..522f938ed17184c57cce32dea7935c3b44d68129 Binary files /dev/null and b/out/production/sp21-cs242-assignment1/GUI.class differ diff --git a/out/production/sp21-cs242-assignment1/Game.class b/out/production/sp21-cs242-assignment1/Game.class index 7f3e15de9302c0b8df639fe6a9f0d75bd3d619b5..cc388d7240516f9285c00e9039b390411bff60f3 100644 Binary files a/out/production/sp21-cs242-assignment1/Game.class and b/out/production/sp21-cs242-assignment1/Game.class differ diff --git a/out/production/sp21-cs242-assignment1/Main.class b/out/production/sp21-cs242-assignment1/Main.class index b12c256cfd7b300357d11523dd86ac945017b47a..5d1ffd422a79c99acab364e4815d253e3094eaf1 100644 Binary files a/out/production/sp21-cs242-assignment1/Main.class and b/out/production/sp21-cs242-assignment1/Main.class differ diff --git a/out/production/sp21-cs242-assignment1/NormalCard.class b/out/production/sp21-cs242-assignment1/NormalCard.class deleted file mode 100644 index c0d199f37e0e3d28ce2f7f992f7f3fd0890ee328..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/NormalCard.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/Player.class b/out/production/sp21-cs242-assignment1/Player.class index 78a74895f68428c4acc365e4b6f881ba2a12fb13..c564c36c856a9e56c1b305b4a260500413ed55c3 100644 Binary files a/out/production/sp21-cs242-assignment1/Player.class and b/out/production/sp21-cs242-assignment1/Player.class differ diff --git a/out/production/sp21-cs242-assignment1/ReverseCard.class b/out/production/sp21-cs242-assignment1/ReverseCard.class deleted file mode 100644 index fc60d312aacd23517ab823ac46b779f96d1751e4..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/ReverseCard.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/RuleController.class b/out/production/sp21-cs242-assignment1/RuleController.class index 34888e1961552e77bf1cf5ad04da7afffd917e93..de13870341e7e8e6003d598eeecd6ecb1d335618 100644 Binary files a/out/production/sp21-cs242-assignment1/RuleController.class and b/out/production/sp21-cs242-assignment1/RuleController.class differ diff --git a/out/production/sp21-cs242-assignment1/SkipCard.class b/out/production/sp21-cs242-assignment1/SkipCard.class deleted file mode 100644 index c02cd7f71a286e5ad19fc39853c9ed34c6c78034..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/SkipCard.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/WildDrawFourCard.class b/out/production/sp21-cs242-assignment1/WildDrawFourCard.class deleted file mode 100644 index 9e4b901ea9654b97769f1f8bc75fdb65fca62b70..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/WildDrawFourCard.class and /dev/null differ diff --git a/out/production/sp21-cs242-assignment1/WirdCard.class b/out/production/sp21-cs242-assignment1/WirdCard.class deleted file mode 100644 index 4a01bfee7641af2b98a6d1d4dd195e295e5540e0..0000000000000000000000000000000000000000 Binary files a/out/production/sp21-cs242-assignment1/WirdCard.class and /dev/null differ diff --git a/sp21-cs242-assignment1.iml b/sp21-cs242-assignment1.iml index 9465dd864f0bbf5233af344982a638534a50275d..1be80785894dc1b3060b760ebcc1ffc2e9ab188f 100644 --- a/sp21-cs242-assignment1.iml +++ b/sp21-cs242-assignment1.iml @@ -3,7 +3,8 @@ <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/Test" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/UNO" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> diff --git a/src/Card.java b/src/Card.java deleted file mode 100644 index cc897fc99a2c8605a4e593ff410e0d59dbce4243..0000000000000000000000000000000000000000 --- a/src/Card.java +++ /dev/null @@ -1,51 +0,0 @@ -import java.util.*; - -public abstract class Card { - protected String color; - public int cardID; -} - - - - - - - - - - -abstract class FunctionalCard extends Card { - abstract void changeGameState(); - -} - -class SkipCard extends FunctionalCard { - void changeGameState() {}; -} - - -class ReverseCard extends FunctionalCard { - void changeGameState() {}; -} - - -class WirdCard extends FunctionalCard { - void changeGameState() {}; -} - -class WildDrawFourCard extends FunctionalCard { - void changeGameState() {}; -} - - - -class NormalCard extends Card { - protected int number; - public NormalCard(String color, int number) { - color = color; - number = number; - } - - - -} \ No newline at end of file diff --git a/src/Game.java b/src/Game.java deleted file mode 100644 index 23d3a9bc2d27620038cdab60b2f36f072f9f7f5b..0000000000000000000000000000000000000000 --- a/src/Game.java +++ /dev/null @@ -1,100 +0,0 @@ -import java.util.*; - - -/** - * The class for recording Game state - * Organize the interactions among all other classes - */ - -public class Game { - private static CardParser parser = new CardParser(); - private static final int INIT_DRAW = 7; // every player get 7 cards at beginning - - public RuleController ruler; - public CardManager gameCardManager; - public ArrayList<Player> players; - private int currentPlayerID; // playerID starts from 0 !!!!! - private boolean isClockWise = true; // if clockwise, next player will be the element in player list - - - /** - * Default constructor for Game object - * @param playerNum - */ - public Game(int playerNum) { - gameCardManager = new CardManager(); - ruler = new RuleController(); - decideFirstPlayerID(playerNum); - - System.out.println("Loading player information..."); - players = new ArrayList<Player>(); - - // playerID starts from 0 !!!!! - for (int i = 0; i < playerNum; i++) { - Player player = new Player(i); - player.drawCards(gameCardManager, INIT_DRAW); - players.add(player); - } - } - - - /** - * Randomly decide the start position of this game - * @param playerNum total number of players - */ - private void decideFirstPlayerID(int playerNum) { - Random rand = new Random(); - currentPlayerID = rand.nextInt(playerNum + 1); - } - - /** - * decide the ID of next player - * @return ID of next player - */ - private int decideNextPlayerID() { - if (isClockWise) { - return (currentPlayerID + 1) % players.size(); - } else { - return (currentPlayerID - 1 + players.size()) % players.size(); - } - } - - /** - * Run one round of UNO - * very import function ! - */ - private void runOneRound() { - System.out.println("Player " + currentPlayerID + 1 + ", It's your turn!"); - System.out.println("Please choose one card to play..."); - Player currentPlayer = players.get(currentPlayerID); - int chosenCardID = currentPlayer.playCardWithCmd(this); - currentPlayerID = decideNextPlayerID(); // update the player for next round - - } - - - - private void changeGameState(String color, String function) { - // change color - if (color.equals("none")) { -// currentAllowedColor = "all"; - } - - // handle function - if (function.equals("skip")) { - currentPlayerID = decideNextPlayerID(); - - } else if (function.equals("draw2")) { - Player nextPlayer = players.get(decideNextPlayerID()); - nextPlayer.drawCards(gameCardManager, 2); - - } else if (function.equals("wildDraw4")) { - - } else if (function.equals("reverse")) { - isClockWise = !isClockWise; // flip - } else if (function.equals("wild")) { - - } - } - -} diff --git a/src/Main.java b/src/Main.java deleted file mode 100644 index 0003900ab0b446ed9bfa98b72be380f0382af669..0000000000000000000000000000000000000000 --- a/src/Main.java +++ /dev/null @@ -1,9 +0,0 @@ -public class Main { - public static void main(String[] args) { - - - } - - - -} \ No newline at end of file diff --git a/src/Player.java b/src/Player.java deleted file mode 100644 index 6a04fa1fff32fa51be09b97ffb8e3e06102c8361..0000000000000000000000000000000000000000 --- a/src/Player.java +++ /dev/null @@ -1,174 +0,0 @@ -import java.lang.reflect.Array; -import java.util.*; - -public class Player { - private static CardParser parser = new CardParser(); - private ArrayList<Integer> cards; - public int playerID; - - - public Player(int ID) { - playerID = ID; - cards = new ArrayList<Integer>(); - } - - /** - * FOR CMD LINE DEBUGGING - * When it's this player's turn, the player must pick one card to play - * @return the ** cardID ** of the chosen card - */ - public int playCardWithCmd(Game gameController) { - // instruct player to take a move - Scanner inputScanner = new Scanner(System.in); - boolean receivedValidInput = false; - int action = -1; - while (!receivedValidInput) { - promptChooseAction(); - String input = inputScanner.nextLine(); - try { - action = Integer.parseInt(input); - if (action == 1 || action == 2) { - receivedValidInput = true; - } - - } catch (Exception e) { - System.out.println("Please choose action from 1 or 2"); - } - } - - if (action == 1) { - return actionOneCmd(gameController); - } else if (action == 2){ - return actionTwoCmd(gameController); - } - - return -1; // user did not play valid card - } - - private int actionOneCmd(Game gameController) { - - int cardID = -1; - while (true) { - printCardsInHand(); - System.out.println("Which card would you like to play? Please input the corresponding card number..."); - - int chosenCardIndex = -1; - - while (chosenCardIndex < 0) { - chosenCardIndex = getInputtedCardIndex(); - } - cardID = cards.get(chosenCardIndex); - if(gameController.ruler.isValidPlay(this, cardID, true)) { - // maintain list-cards and discardPile - cards.remove(chosenCardIndex); - gameController.gameCardManager.insertOneCardToDiscardPile(cardID); - break; - } - } - - if (parser.isWildCard(cardID)) { - // to implement - } - return cardID; - } - - private int actionTwoCmd (Game gameController) { - drawCards(gameController.gameCardManager, 1); // draw new card and add it to cards in hand - int chosenCardIndex = cards.size() - 1; - int cardID = cards.get(chosenCardIndex); - if(gameController.ruler.isValidPlay(this, cardID, true)) { - cards.remove(chosenCardIndex); - gameController.gameCardManager.insertOneCardToDiscardPile(cardID); - - if (parser.isWildCard(cardID)) { - // to implement - } - return cardID; - } else { - System.out.println("Sorry, the newly drawn card is not playable :("); - return -1; - } - } - - - /** - * helper function for playCardWithCommandLine - * parse the integer inputted by the player - * @return the ** INDEX ** of card in list cards that user chooses - */ - public int getInputtedCardIndex() { - Scanner inputScanner = new Scanner(System.in); - String input = inputScanner.nextLine(); - int decision; - try { - decision = Integer.parseInt(input) - 1; // -1 because we want the index in cards list - } catch (Exception e) { - System.out.println("Please input the number corresponding the card you choose!"); - return -1; - } - - if (decision < 0 || decision >= cards.size()) { - System.out.println("The card you chose does not exist!"); - return -1; - } - return decision; - } - - - /** - * FOR real playing scenarios with GUI - * When it's this player's turn, the player must pick one card to play - */ - public int playCardWithGUI() { - // not implemented yet. - return 0; - } - - /** - * - * @param dealer a cardManager object that is responsible to deal cards - * @param numToDraw number of cards to draw - */ - public void drawCards(CardManager dealer, int numToDraw) { - ArrayList<Integer> newCards = dealer.drawCards(numToDraw); // Initial draw - cards.addAll(newCards); - } - - /** - * print all cards that this user has - */ - public void printCardsInHand() { - if (!cards.isEmpty()) { - System.out.println("\n\n==================================================================="); - System.out.println("Currently have " + cards.size() + " cards:"); - for (int i = 0; i < cards.size(); i++) { - System.out.println("[" + (i + 1) +"] " + parser.parseCardID(cards.get(i))); - } - System.out.println("===================================================================\n\n"); - - } else { - System.out.println("============================================================"); - System.out.println("You got not cards in hands! Congrats, you are the winner!"); - } - } - - /** - * getter for cards (private attribute for tracking all cards owned by a player) - * @return - */ - public ArrayList<Integer> getCards() { - return cards; - } - - - /** - * prompt player to take action current round - */ - private void promptChooseAction() { - printCardsInHand(); - System.out.println("Please choose you action this round:"); - System.out.println("[1] Play a owned card"); - System.out.println("[2] Draw a card and play it if possible"); - } - -} \ No newline at end of file diff --git a/src/RuleController.java b/src/RuleController.java deleted file mode 100644 index 2447ccc3bb71910da4f4b1422dc9383754ea8c6d..0000000000000000000000000000000000000000 --- a/src/RuleController.java +++ /dev/null @@ -1,105 +0,0 @@ -import java.util.ArrayList; - -public class RuleController { - private static CardParser parser = new CardParser(); - private String currentAllowedColor = "all"; - private String currentAllowedNumber = "all"; - private String currentAllowedSymbol = "all"; - - - /** - * 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) { - - 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 (checkAttrMatch(currentAllowedColor, color)) - valid = true; - if (checkAttrMatch(currentAllowedNumber, content) || checkAttrMatch(currentAllowedSymbol, content)) - valid = true; - if (content.equals("wildDraw4")) { - valid = checkDraw4IsLegal(player); - } - - if (valid && updateIfValid) { - currentAllowedColor = color.equals("NA") ? "none" : color; // wildcard played - if (type.equals("sym")) { - currentAllowedSymbol = content; - currentAllowedNumber = "none"; - } else if (type.equals("num")) { - currentAllowedNumber = content; - currentAllowedSymbol = "none"; - } - } - return false; - } - - private boolean checkAttrMatch(String legalString, String stringToCheck) { - return legalString.equals("all") || stringToCheck.equals(legalString); - } - - /** - * Check whether a player have currently allowed 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]; - if (color.equals(currentAllowedNumber)) return false; - } - return true; - } - - /** - * Getter and Setter for Allowed number - */ - - public String getAllowedNumber() { - return currentAllowedNumber; - } - - public void getAllowedNumber(String number) { - currentAllowedColor = number; - } - - /** - * Getter and Setter for Allowed Symbol - */ - - public String getCurrentAllowedSymbol() { - return currentAllowedSymbol; - } - - public void setAllowedSymbol(String symbol) { - currentAllowedSymbol = symbol; - } - - - /** - * Getter and Setter for Allowed Color - */ - - public String getAllowedColor() { - return currentAllowedColor; - } - - public void setAllowedColor(String color) { - currentAllowedColor = color; - } - -} - - - - - diff --git a/src/CardManager.java b/src/UNO/CardManager.java similarity index 91% rename from src/CardManager.java rename to src/UNO/CardManager.java index ee016b54e79ffd826ae499f2f6d11f16f1f22463..5d14b79141dd02cf6eeeeb7818cb3f8c6279528b 100644 --- a/src/CardManager.java +++ b/src/UNO/CardManager.java @@ -8,6 +8,7 @@ public class CardManager { public CardManager() { parser = new CardParser(); + discardPile = new ArrayList<>(); initializeCardPile(); printCardPile(); } @@ -76,7 +77,11 @@ public class CardManager { discardPile.add(0, cardID); } -} + /** + * Return number of card left on discard pile + */ + public int numLeftDiscardPile() { return discardPile.size(); } +} diff --git a/src/CardParser.java b/src/UNO/CardParser.java similarity index 100% rename from src/CardParser.java rename to src/UNO/CardParser.java diff --git a/src/UNO/CmdUI.java b/src/UNO/CmdUI.java new file mode 100644 index 0000000000000000000000000000000000000000..9840bb7978f0e7e577c8d7c5ef848f4255475378 --- /dev/null +++ b/src/UNO/CmdUI.java @@ -0,0 +1,185 @@ +import java.util.Scanner; + +public class CmdUI { + Player player; + CardParser parser; + public static Game gameController; + public static RuleController ruler; + + public CmdUI(Player p) { + player = p; + parser = player.parser; + gameController = player.gameController; + ruler = gameController.ruler; + + } + + /** + * FOR CMD LINE DEBUGGING + * When it's this player's turn, the player must pick one card to play + * @return the ** cardID ** of the chosen card + */ + public int promptTakeAction() { + // instruct player to take a move + Scanner inputScanner = new Scanner(System.in); + boolean receivedValidInput = false; + int action = -1; + while (!receivedValidInput) { + promptChooseAction(); + String input = inputScanner.nextLine(); + try { + action = Integer.parseInt(input); + if (action == 1 || action == 2) { + receivedValidInput = true; + } + + } catch (Exception e) { + System.out.println("Please choose action from 1 or 2"); + } + } + + if (action == 1) { + return actionOne(); + } else if (action == 2){ + return actionTwo(); + } + + return -1; // user did not play valid card + } + + private int actionOne() { + + int cardID = -1; + while (true) { + player.printCardsInHand(); + player.printLegalCards(); + System.out.println("Which card would you like to play? Please input the corresponding card number..."); + + int chosenCardIndex = -1; + + while (chosenCardIndex < 0) { + chosenCardIndex = getInputCardIndex(); + } + cardID = player.getCards().get(chosenCardIndex); + if(gameController.ruler.isValidPlay(player, cardID, true)) { + // maintain list-cards and discardPile + player.getCards().remove(chosenCardIndex); + gameController.gameCardManager.insertOneCardToDiscardPile(cardID); + break; + } + } + + if (parser.isWildCard(cardID)) { + // to implement + int colorChosen = -1; + while (colorChosen < 0) { + promptChooseColor(); + colorChosen = getInputColor(); + } + ruler.setAllowedColor(parser.colorDict.get(colorChosen)); + } + return cardID; + } + + private int actionTwo () { + player.drawCards( 1); // draw new card and add it to cards in hand + int chosenCardIndex = player.getCards().size() - 1; + int cardID = player.getCards().get(chosenCardIndex); + if(ruler.isValidPlay(player, cardID, true)) { + player.getCards().remove(chosenCardIndex); + gameController.gameCardManager.insertOneCardToDiscardPile(cardID); + + if (parser.isWildCard(cardID)) { + // to implement + int colorChosen = -1; + while (colorChosen < 0) { + promptChooseColor(); + colorChosen = getInputColor(); + } + gameController.ruler.setAllowedColor(parser.colorDict.get(colorChosen)); + } + + return cardID; + } else { + System.out.println("Sorry, the newly drawn card is not playable :("); + return -1; + } + } + + + /** + * helper function for playCardWithCommandLine + * parse the integer inputted by the player + * @return the ** INDEX ** of card in list cards that user chooses + */ + private int getInputCardIndex() { + Scanner inputScanner = new Scanner(System.in); + String input = inputScanner.nextLine(); + int decision; + try { + decision = Integer.parseInt(input) - 1; // -1 because we want the index in cards list + } catch (Exception e) { + System.out.println("Please input the number corresponding the card you choose!"); + return -1; + } + + if (decision < 0 || decision >= player.getCards().size()) { + System.out.println("The card you chose does not exist!"); + return -1; + } + return decision; + } + + /** + * helper function for playCardWithCommandLine + * parse the integer inputted by the player + * @return the ** INDEX ** of card in list cards that user chooses + */ + private int getInputColor() { + Scanner inputScanner = new Scanner(System.in); + String input = inputScanner.nextLine(); + int decision; + try { + decision = Integer.parseInt(input); + } catch (Exception e) { + System.out.println("Please input the number corresponding the card you choose!"); + return -1; + } + + if (decision != 1 && decision != 2 && decision != 3 && decision != 4) { + System.out.println("Please choose a valid color (represented by number)!"); + return -1; + } + return decision; + } + + /** + * prompt player to take action current round + */ + private void promptChooseAction() { + player.printCardsInHand(); + System.out.println("The following are playable:"); + player.printLegalCards(); + System.out.println("Please choose you action this round:"); + System.out.println("[1] Play a owned card"); + System.out.println("[2] Draw a card and play it if possible"); + System.out.println("Please input 1 or 2 : "); + } + + private void promptChooseColor() { + player.printCardsInHand(); + System.out.println("Please pick a color for the next rounds:"); + System.out.println("[1] Red"); + System.out.println("[2] Green"); + System.out.println("[3] Blue"); + System.out.println("[4] Yellow"); + System.out.println("Please choose one from above: "); + } + + public void printForcedSkip() { + System.out.println("Sorry, you lost this turn and was forcefully skipped."); + } + + + +} diff --git a/src/UNO/GUI.java b/src/UNO/GUI.java new file mode 100644 index 0000000000000000000000000000000000000000..db4a77649fb48397ff770ebed2dcedb0133a17af --- /dev/null +++ b/src/UNO/GUI.java @@ -0,0 +1,16 @@ +public class GUI { + Player player; + public GUI(Player player) { + player = player; + } + + + /** + * FOR real playing scenarios with GUI + * When it's this player's turn, the player must pick one card to play + */ + public int playCardWithGUI() { + // not implemented yet. + return 0; + } +} diff --git a/src/UNO/Game.java b/src/UNO/Game.java new file mode 100644 index 0000000000000000000000000000000000000000..59c4676261c343600184c771f2e75d059d619818 --- /dev/null +++ b/src/UNO/Game.java @@ -0,0 +1,82 @@ +import java.util.*; + + +/** + * The class for recording Game state + * Organize the interactions among all other classes + */ + +public class Game { + private static CardParser parser = new CardParser(); + private static final int INIT_DRAW = 7; // every player get 7 cards at beginning + + private int rounds = 1; + public RuleController ruler; + public CardManager gameCardManager; + public ArrayList<Player> players; + private int currentPlayerID; // playerID starts from 0 !!!!! + + /** + * Default constructor for Game object + * @param playerNum + */ + public Game(int playerNum) { + gameCardManager = new CardManager(); + ruler = new RuleController(); + decideFirstPlayerID(playerNum); + + System.out.println("Loading player information..."); + players = new ArrayList<Player>(); + + // playerID starts from 0 !!!!! + for (int i = 0; i < playerNum; i++) { + Player player = new Player(i, this); + player.drawCards(INIT_DRAW); + players.add(player); + } + } + + + /** + * Randomly decide the start position of this game + * @param playerNum total number of players + */ + private void decideFirstPlayerID(int playerNum) { + Random rand = new Random(); + currentPlayerID = rand.nextInt(playerNum); // -1 to convert to index + } + + + /** + * find the next player in the sequence, regardless whether he or not should be skipped + */ + private void updateNextPlayerID() { + if (ruler.getIsClockwise()) { + currentPlayerID = (currentPlayerID + 1) % players.size(); + } else { + currentPlayerID = (currentPlayerID - 1 + players.size()) % players.size(); + } + + } + + /** + * Run one round of UNO + * very import function ! + */ + private void runOneRound() { + ruler.reportCurrentState(this); + System.out.println("It's now Player " + (currentPlayerID + 1) + "'s turn..."); + Player currentPlayer = players.get(currentPlayerID); + currentPlayer.playOneRound(); + + } + + public void gameStart() { + while (rounds <= 2) { + System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n<<<<<<<<<<<<<<<<<<<<<<<< Round" + rounds + " >>>>>>>>>>>>>>>>>>>>>>>>>\n\n"); + runOneRound(); + updateNextPlayerID(); + rounds++; + } + } +} diff --git a/src/UNO/Main.java b/src/UNO/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..932aca16afb970c3c3bf562b2cadaa33eb02f386 --- /dev/null +++ b/src/UNO/Main.java @@ -0,0 +1,15 @@ +public class Main { + public static void main(String[] args) { +// CardParser parser = new CardParser(); +// String desc = parser.parseCardID(60); +// System.out.println(parser.parseCardDescription(desc)[0]); + System.out.println("--------------------------------------------------- Game Start ---------------------------------------------------"); + Game uno = new Game(6); + uno.gameStart(); + + + } + + + +} \ No newline at end of file diff --git a/src/UNO/Player.java b/src/UNO/Player.java new file mode 100644 index 0000000000000000000000000000000000000000..a8756a233349de1a0c5b14bdff36c2902cf14cfc --- /dev/null +++ b/src/UNO/Player.java @@ -0,0 +1,142 @@ +import java.lang.reflect.Array; +import java.util.*; + +public class Player { + public static CardParser parser; + private final ArrayList<Integer> cards; + public int playerID; + public static Game gameController; + public static RuleController ruler; + public CmdUI prompterCmd; + public GUI prompterGUI; + + + public Player(int ID, Game game) { + playerID = ID; + cards = new ArrayList<Integer>(); + gameController = game; + ruler = game.ruler; + parser = RuleController.parser; + prompterCmd = new CmdUI(this); + prompterGUI = null; + } + + /** + * @param numToDraw number of cards to draw + */ + public void drawCards(int numToDraw) { + ArrayList<Integer> newCards = gameController.gameCardManager.drawCards(numToDraw); // Initial draw + cards.addAll(newCards); + } + + /** + * print all cards that this user has + */ + public void printCardsInHand() { + if (!cards.isEmpty()) { + System.out.println("\n\n==================================================================="); + System.out.println("Currently have " + cards.size() + " cards:"); + for (int i = 0; i < cards.size(); i++) { + System.out.println("[" + (i + 1) +"] " + parser.parseCardID(cards.get(i))); + } + System.out.println("===================================================================\n\n"); + + } else { + System.out.println("============================================================"); + System.out.println("You got not cards in hands! Congrats, you are the winner!"); + } + } + + /** + * iterate through cards and see if any of the player's cards are playable + * @return an array list of legal cards + */ + public ArrayList<Integer> findLegalCard() { + ArrayList<Integer> legalCards = new ArrayList<>(); + for (int cardID : cards) { + if (ruler.isValidPlay(this, cardID, false)) legalCards.add(cardID); + } + return legalCards; + } + + /** + * iterate through cards and see if any of the player's cards are playable + * @return an array list of the **indices** of legal cards from all cards + */ + public ArrayList<Integer> findLegalCardIndex() { + ArrayList<Integer> legalCardIndices = new ArrayList<>(); + for (int i = 0; i < cards.size(); i++) { + if (ruler.isValidPlay(this, cards.get(i), false)) legalCardIndices.add(i); + } + return legalCardIndices; + } + + + /** + * print all cards that this user has + */ + public void printLegalCards() { + ArrayList<Integer> legals = findLegalCard(); + if (!legals.isEmpty()) { + System.out.println("\n\n==================================================================="); + System.out.println("The following cards are playable in current turn:"); + for (int i = 0; i < legals.size(); i++) { + System.out.println("[" + (i + 1) +"] " + parser.parseCardID(legals.get(i))); + } + System.out.println("===================================================================\n\n"); + + } else { + System.out.println("\n\n============================================================"); + System.out.println("You don't have any playable cards now."); + System.out.println("============================================================\n\n"); + } + } + + + + /** + * getter for cards (private attribute for tracking all cards owned by a player) + * @return the cards of the player + */ + public ArrayList<Integer> getCards() { + return cards; + } + + + /** + * Caller for player to play one round + * skip, draw, play ... all behavior will be handled by this function + */ + public void playOneRound() { + if (prompterGUI != null) { + playOneRoundGUI(); + } else if (prompterCmd != null) { + playOneRoundCmd(); + } + } + + /** + * Playing with cmd as UI + */ + public void playOneRoundCmd() { + if (ruler.checkSkipandDraw(this)) { + prompterCmd.printForcedSkip(); + } else { + prompterCmd.promptTakeAction(); + } + + } + + /** + * TO BE IMPLEMENTED... + */ + public void playOneRoundGUI() { +// if (ruler.shoudPlayerBeSkipped(this)) { +// prompterGUI +// } else {} + + } + + + +} \ No newline at end of file diff --git a/src/UNO/RuleController.java b/src/UNO/RuleController.java new file mode 100644 index 0000000000000000000000000000000000000000..06d923b0965462749119f2f2d6124ea44858d93e --- /dev/null +++ b/src/UNO/RuleController.java @@ -0,0 +1,257 @@ +import java.util.ArrayList; + +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 = "all"; + private String currentMatchableNumber = "all"; + private String currentMatchableSymbol = "all"; + private int nextPlayerSkipLevel = 0; + private int cumulativePenaltyDraw = 0; + private boolean isClockWise = true; // if clockwise, next player will be the element in player list + + + /** + * 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) { + + String cardDescription = parser.parseCardID(cardID); + String[] result = parser.parseCardDescription(cardDescription); + String color = result[0]; + String type = result[1]; + String content = result[2]; + + // pending skip + if (nextPlayerSkipLevel == 4) { + return false; + } else if (nextPlayerSkipLevel == 3) { + return content.equals("wildDraw4"); // not sure - check draw 4 legal? + } else if (nextPlayerSkipLevel == 2) { + return content.equals("wildDraw4") || content.equals("draw2"); + } + + boolean valid = false; + // if not skipped, first consider wild + if (content.equals("wild")) { + valid = true; // can be used unconditionally + } + + // then consider wildDraw4 + if (content.equals("wildDraw4")) { + valid = checkDraw4IsLegal(player); + } + + // then other colored cards + if (checkAttrMatch(currentMatchableColor, color)) + valid = true; + if (checkAttrMatch(currentMatchableNumber, content) || checkAttrMatch(currentMatchableSymbol, content)) + valid = true; + + if (valid && updateIfValid) { + updateRule(color, type, content); + } + + return valid; + } + + private void updateRule(String color, String type, String content) { + setAllowedColor(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 = "all"; //?? + + } 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"; // color will be declared by player later + + } 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(currentMatchableNumber)) return false; + } + return true; + } + + /** + * Getter and Setter for Allowed number + */ + + public String getMatchableNumber() { + return currentMatchableNumber; + } + + public void getAllowedNumber(String number) { + currentMatchableColor = number; + } + + /** + * Getter and Setter for Allowed Symbol + */ + + public String getCurrentMatchableSymbol() { + return currentMatchableSymbol; + } + + public void setAllowedSymbol(String symbol) { + currentMatchableSymbol = symbol; + } + + + /** + * Getter and Setter for Allowed Color + */ + + public String getMatchableColor() { + return currentMatchableColor; + } + + public void setAllowedColor(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 : " + getCurrentMatchableSymbol()); + 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."); + } + + + +} + + + + +