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.");
+    }
+    
+
+
+}
+
+
+
+
+