diff --git a/out/production/sp21-cs242-assignment1/CardManager.class b/out/production/sp21-cs242-assignment1/CardManager.class index 3b553b8e9cce60d30360c982042f9805d8b9eeae..684095064f264898370a598bceaec72487f22763 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/CardParser.class b/out/production/sp21-cs242-assignment1/CardParser.class index 91bfe36bc32cfed6704cc7ea4288d3f3586384d5..6c9bc09a1dea0efd95dc1d18883109a6b2fa6a96 100644 Binary files a/out/production/sp21-cs242-assignment1/CardParser.class and b/out/production/sp21-cs242-assignment1/CardParser.class differ diff --git a/out/production/sp21-cs242-assignment1/CmdUI.class b/out/production/sp21-cs242-assignment1/CmdUI.class index 9e4a053d8df256d5f7e95477d0e4e5fc5397a49c..8a634436ce8e7364bbb853031ae91061a657f900 100644 Binary files a/out/production/sp21-cs242-assignment1/CmdUI.class and b/out/production/sp21-cs242-assignment1/CmdUI.class differ diff --git a/out/production/sp21-cs242-assignment1/Game.class b/out/production/sp21-cs242-assignment1/Game.class index d5c853fa460b7ce595a9af05c64a4e50bd7a828d..4058f84c7860f8f236114c72a56bd777e7b3ccd2 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/Player.class b/out/production/sp21-cs242-assignment1/Player.class index d3baf62c663311ac8df80a26813c027b722e1778..a7f7f5cb0581e12e6fe2b2ac52b116e83a8c105c 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/RuleController.class b/out/production/sp21-cs242-assignment1/RuleController.class index 4cd51c49ff5cbf5b60e2373a96937b5d4bb4fb78..176269a21e1de069cbaeeff448911a44caf29392 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/test/sp21-cs242-assignment1/PlayerTest.class b/out/test/sp21-cs242-assignment1/PlayerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..014a5f9933fb477faa6762523256715174095f82 Binary files /dev/null and b/out/test/sp21-cs242-assignment1/PlayerTest.class differ diff --git a/out/test/sp21-cs242-assignment1/RuleControllerTest.class b/out/test/sp21-cs242-assignment1/RuleControllerTest.class index 9999d71a7bba788c9c2c9c63cffae4034f977b94..793251c85707ab4b1dbfc5249475c7a9f8342b6a 100644 Binary files a/out/test/sp21-cs242-assignment1/RuleControllerTest.class and b/out/test/sp21-cs242-assignment1/RuleControllerTest.class differ diff --git a/out/test/sp21-cs242-assignment1/cardDealTest.class b/out/test/sp21-cs242-assignment1/cardDealTest.class index 33cd2ce1b1236cb0a8e86a00b7ae092483bc6674..5c5c2ef16a4255dd6527815e558eee16eaf2d000 100644 Binary files a/out/test/sp21-cs242-assignment1/cardDealTest.class and b/out/test/sp21-cs242-assignment1/cardDealTest.class differ diff --git a/src/Test/PlayerTest.java b/src/Test/PlayerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..62be5d0539653e4524e7686a3ed377b37307bc4e --- /dev/null +++ b/src/Test/PlayerTest.java @@ -0,0 +1,85 @@ +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Random; + +import static org.junit.jupiter.api.Assertions.*; + + +/** + * The last part of test: + * Test the behavior of player when they ACTUALLY PLAY A CARD + * <p> + * Dependency: Game, ruleController (all test done), cardManager, CmdUI, GUI + * <p> + * Notice ruleController only judge validity & do update in game state + * and pass them as information for player. Thus whether player could behave + * correctly with given information should be tested. + */ + +class PlayerTest { + + @Test + /** + * Test when a card is played, it goes to the discard pile + * Run 100 times to cancel randomness + */ + void TestUserPlayWillGoToDiscardPile() { + for (int i = 0; i < 100; i++) { + Game game = new Game(1); + Player player = game.players.get(0); + player.drawCards(50); + + ArrayList<Integer> legalCards = player.findLegalCard(); + int cardToPlay = legalCards.get(0); + + // make sure this play is legal + assert (player.optionPlayOwnedCard(cardToPlay)); + + // make sure the top of discard of pile is the card just played + assert (game.gameCardManager.getDiscardPile().get(0) == cardToPlay); + + //make sure the player does not own this card anymore + assert (!player.getCards().contains(cardToPlay)); + } + } + + + /** + * Test the situation where player choose to draw a card + * Run 100 times to cancel randomness + */ + @Test + void testOptionDrawAndPlay() { + for (int i = 0; i < 100; i++) { + Game game = new Game(1); + Player player = game.players.get(0); + int cardPlayerWillGet = game.gameCardManager.getCardPile().get(0); + boolean newCardIsValid = game.ruler.isValidPlay(player, cardPlayerWillGet, false); + boolean played = player.optionDrawCardAndPlay(); + + if (newCardIsValid) { + // player should immediately play the card + assert (played); + assert (game.gameCardManager.getDiscardPile().get(0) == cardPlayerWillGet); + assert (!player.getCards().contains(cardPlayerWillGet)); + + } else { + assert (!played); + assert (player.getCards().contains(cardPlayerWillGet)); + } + } + } + + + @Test + /** + * Test 3 + * Test game will end when a user wins + */ + void testGameCloseWhenPlayerWin() { + Game game = new Game(1); + game.gameStart(); + assert(game.rounds == 7); + } +} \ No newline at end of file diff --git a/src/Test/RuleControllerTest.java b/src/Test/RuleControllerTest.java index ddae6d42ab23f69c801c2ff4c408014be80eae33..27607137b19199a896baa06d1a40f0f8d30c4fff 100644 --- a/src/Test/RuleControllerTest.java +++ b/src/Test/RuleControllerTest.java @@ -255,16 +255,12 @@ class RuleControllerTest { RuleController ruler = new RuleController(); Player player = playerAllCards(); // a player who owns all cards - setCurrentState(ruler, "red", "none", "8", 0); - - ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player); - ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player, - ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol()); - // current player plays red 0 + setCurrentState(ruler, "red", "none", "8", 0); ruler.isValidPlay(player, 25, true); assert(checkStateUpdatedCorrectly(ruler, "red", "none", "0", 0)); + // current player plays red skip card setCurrentState(ruler, "red", "none", "8", 0); ruler.isValidPlay(player, 20,true); @@ -356,6 +352,49 @@ class RuleControllerTest { } + /** + * 13. Test checkSkipAndDraw + * If a player should be skipped this round, he should then draw the cumulative penalty cards + * Behavior of card Manager is also checked in this test case + */ + @Test + void testCheckSkipAndDraw() { + Game game = new Game(1); + Player player = player_AllColor_Y_WildDraw4_Y(); // he cannot play wildDraw4 anyway + player.playerID = 2; + player.gameController = game; + player.ruler = game.ruler; + + // case 1: Red skip - player should not be skipped + int playerNumCheckPoint1 = player.getCards().size(); + int pileNumCheckPoint1 = game.gameCardManager.numCardLeft(); + setCurrentState(game.ruler, "red", "skip", "none", 3); + assert(game.ruler.checkSkipAndDraw(player)); + assert(player.getCards().size() == playerNumCheckPoint1); + assert(game.gameCardManager.numCardLeft() == pileNumCheckPoint1); + + // case2: Blue draw2 - player should be skipped and draw 2 + int playerNumCheckPoint2 = player.getCards().size(); + int pileNumCheckPoint2 = game.gameCardManager.numCardLeft(); + setCurrentState(game.ruler, "blue", "draw2", "none", 1); + game.ruler.increasePenaltyDraw(2); + assert(game.ruler.checkSkipAndDraw(player)); + assert(player.getCards().size() == playerNumCheckPoint2 + 2); + assert(game.gameCardManager.numCardLeft() == pileNumCheckPoint2 - 2); + game.ruler.resetPenaltyDraw(); + + + // case3: wildDraw4 - player should be skipped and draw 4 + int playerNumCheckPoint3 = player.getCards().size(); + int pileNumCheckPoint3 = game.gameCardManager.numCardLeft(); + setCurrentState(game.ruler, "blue", "draw2", "none", 2); + game.ruler.increasePenaltyDraw(4); + assert(game.ruler.checkSkipAndDraw(player)); + assert(player.getCards().size() == playerNumCheckPoint3 + 4); + assert (game.gameCardManager.numCardLeft() == pileNumCheckPoint3 - 4); + game.ruler.resetPenaltyDraw(); + + } @@ -433,7 +472,7 @@ class RuleControllerTest { * @return */ private Player player_Red_N_WildDraw4_Y() { - Player player = new Player(0, null); + Player player = new Player(0, null, true); player.addOneCard(26); // green 1 player.addOneCard(51); // blue 1 player.addOneCard(76); // yellow 1 @@ -448,7 +487,7 @@ class RuleControllerTest { * @return */ private Player player_AllColor_Y_WildDraw4_Y() { - Player player = new Player(0, null); + Player player = new Player(0, null, true); player.addOneCard(1); // red 1 player.addOneCard(26); // green 1 player.addOneCard(51); // blue 1 @@ -463,7 +502,7 @@ class RuleControllerTest { * This player should not be able to play wildDraw4 card */ private Player playerAllCards() { - Player player = new Player(0, null); + Player player = new Player(0, null, true); for (int i = 1; i <= 108; i++) { player.addOneCard(i); } @@ -485,6 +524,9 @@ class RuleControllerTest { ruler.setNextPlayerSkiplevel(level); } + /** + * check all game states are correct based on passed ground truth + */ private boolean checkStateUpdatedCorrectly(RuleController ruler, String truthColor, String truthSymbol, diff --git a/src/Test/cardDealTest.java b/src/Test/cardDealTest.java index 130070a1c3f65a0eb7fc48b8fb8aed5ce92e7d11..ef4a56a95d377e2fea8134803295791234a300c6 100644 --- a/src/Test/cardDealTest.java +++ b/src/Test/cardDealTest.java @@ -15,7 +15,8 @@ class cardDealTest { @Test /** - * Test card manager can initialize 108 cards correctly + * Test 1 + * make sure card manager can initialize 108 cards correctly */ void testInitialization() throws Exception { CardManager cardManager = new CardManager(); @@ -27,6 +28,7 @@ class cardDealTest { @Test /** + * Test 2 * Given n players, test behavior of initial deal, as well as behavior of draw pile * dependencies: Player.drawCards, CardManager.* */ @@ -54,6 +56,7 @@ class cardDealTest { @Test /** + * Test 3 * Test the behavior of Player.drawCards * under the condition where draw pile is not enough to draw, should draw from discard pile instead * case 1: completely draw from discard pile @@ -80,6 +83,7 @@ class cardDealTest { @Test /** + * Test 4 * Test the behavior of Player.drawCards * under the condition where draw pile is not enough to draw, should draw from discard pile instead * case 2: partially from draw pile, partially from discard pile diff --git a/src/UNO/CardManager.java b/src/UNO/CardManager.java index 949e5888392a6fd820b865342b24e17c5560cc27..5c8da460e3bdccfaf34f403586f117299f79fb01 100644 --- a/src/UNO/CardManager.java +++ b/src/UNO/CardManager.java @@ -87,9 +87,15 @@ public class CardManager { public int numLeftDiscardPile() { return discardPile.size(); } /** - * get all the cards currently in card pile as a Array list + * get all the cards currently in draw pile as a Array list */ public ArrayList<Integer> getCardPile() { return cardPile; } + /** + * get all the cards currently in discard pile as a Array list + */ + public ArrayList<Integer> getDiscardPile() { return discardPile; } + + } diff --git a/src/UNO/CardParser.java b/src/UNO/CardParser.java index b5d740b951d0f033a33791693856a328368dc21c..f43ce3a47d1e9b544ece8d1d8ba9ea5aab5a381f 100644 --- a/src/UNO/CardParser.java +++ b/src/UNO/CardParser.java @@ -110,7 +110,7 @@ class CardParser { * judge whether a card is a wild card - that is, whether user can decalre a color * @param cardID id of card */ - public boolean isWildCard(int cardID) { + public boolean isNonColorCard(int cardID) { return cardID > COLORED_CARD_NUM; } } \ No newline at end of file diff --git a/src/UNO/CmdUI.java b/src/UNO/CmdUI.java index 9da970239c7a9feddedc7a73aca19dbfc3815629..d73f23c6790bd177033c26abb2efeac8ca048d0e 100644 --- a/src/UNO/CmdUI.java +++ b/src/UNO/CmdUI.java @@ -1,5 +1,9 @@ import java.util.Scanner; + +/** + * Prepared for assignment 1.1 + */ public class CmdUI { Player player; CardParser parser; @@ -70,8 +74,7 @@ public class CmdUI { } } - if (parser.isWildCard(cardID)) { - // to implement + if (parser.isNonColorCard(cardID)) { int colorChosen = -1; while (colorChosen < 0) { promptChooseColor(); @@ -90,7 +93,7 @@ public class CmdUI { player.getCards().remove(chosenCardIndex); gameController.gameCardManager.insertOneCardToDiscardPile(cardID); - if (parser.isWildCard(cardID)) { + if (parser.isNonColorCard(cardID)) { // to implement int colorChosen = -1; while (colorChosen < 0) { diff --git a/src/UNO/Game.java b/src/UNO/Game.java index 7d5ec00f0540eb62d3018d436273b86cdb28f726..b42773b4c659e6a051ed825a8eda79ab5fe5bd71 100644 --- a/src/UNO/Game.java +++ b/src/UNO/Game.java @@ -10,7 +10,7 @@ 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 int rounds = 1; public RuleController ruler; public CardManager gameCardManager; public ArrayList<Player> players; @@ -31,7 +31,7 @@ public class Game { // playerID starts from 0 !!!!! for (int i = 0; i < playerNum; i++) { - Player player = new Player(i, this); + Player player = new Player(i, this, true); player.drawCards(INIT_DRAW); players.add(player); } @@ -63,7 +63,7 @@ public class Game { * Run one round of UNO * very import function ! */ - private void runOneRound() { + public void runOneRound() { ruler.reportCurrentState(this); System.out.println("It's now Player " + (currentPlayerID + 1) + "'s turn..."); Player currentPlayer = players.get(currentPlayerID); @@ -72,9 +72,21 @@ public class Game { } 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(); + + // left for assignment 1.1 +// 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++; +// } + + while (true) { + Player currentPlayer = players.get(currentPlayerID); + currentPlayer.runOneRoundForTesting(); + if (currentPlayer.playerWin()) { + break; + } updateNextPlayerID(); rounds++; } diff --git a/src/UNO/Player.java b/src/UNO/Player.java index cbf50ab50ec8a512853f5c4067a31c4593facc3f..4aa2296771bdfb3a9dad0ced6a79cf6373e27017 100644 --- a/src/UNO/Player.java +++ b/src/UNO/Player.java @@ -1,24 +1,24 @@ -import java.lang.reflect.Array; +import javax.xml.stream.FactoryConfigurationError; 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 Game gameController; + public RuleController ruler; public CmdUI prompterCmd; public GUI prompterGUI; - public Player(int ID, Game game) { + public Player(int ID, Game game, Boolean useCmdAsUI) { playerID = ID; - cards = new ArrayList<Integer>(); + cards = new ArrayList<>(); gameController = game; ruler = (game != null) ? game.ruler : null; // for testing purpose parser = (game != null) ? RuleController.parser : null; - prompterCmd = new CmdUI(this); - prompterGUI = null; + prompterCmd = useCmdAsUI ? new CmdUI(this) : null; + prompterGUI = useCmdAsUI ? null : new GUI(this); } /** @@ -37,7 +37,7 @@ public class Player { 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("[" + (i + 1) + "] " + parser.parseCardID(cards.get(i))); } System.out.println("===================================================================\n\n"); @@ -48,7 +48,8 @@ public class Player { } /** - * iterate through cards and see if any of the player's cards are playable + * 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() { @@ -60,7 +61,8 @@ public class Player { } /** - * iterate through cards and see if any of the player's cards are playable + * 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() { @@ -81,7 +83,7 @@ public class Player { 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("[" + (i + 1) + "] " + parser.parseCardID(legals.get(i))); } System.out.println("===================================================================\n\n"); @@ -93,9 +95,9 @@ public class Player { } - /** * getter for cards (private attribute for tracking all cards owned by a player) + * * @return the cards of the player */ public ArrayList<Integer> getCards() { @@ -104,6 +106,7 @@ public class Player { /** + * Prepared for assignment1.1 * Caller for player to play one round * skip, draw, play ... all behavior will be handled by this function */ @@ -116,10 +119,11 @@ public class Player { } /** - * Playing with cmd as UI + * Prepared for assignment1.1 + * Playing with cmd as UI */ public void playOneRoundCmd() { - if (ruler.checkSkipandDraw(this)) { + if (ruler.checkSkipAndDraw(this)) { prompterCmd.printForcedSkip(); } else { prompterCmd.promptTakeAction(); @@ -128,10 +132,11 @@ public class Player { } /** - * TO BE IMPLEMENTED... + * Prepared for assignment1.2 + * TO BE IMPLEMENTED... */ public void playOneRoundGUI() { -// if (ruler.shoudPlayerBeSkipped(this)) { +// if (ruler.shouldPlayerBeSkipped(this)) { // prompterGUI // } else {} @@ -144,4 +149,69 @@ public class Player { cards.add(cardID); } + /** + * For Assignment1.0 only + * Implement the logic of player playing a card. + * + * @Return whether this player successfully played the indicated card + */ + public boolean optionPlayOwnedCard(int cardID) { + if (ruler.checkSkipAndDraw(this)) { + return false; + } else { + + if (ruler.isValidPlay(this, cardID, true)) { + // maintain list-cards and discardPile + cards.remove(cards.indexOf(cardID)); + gameController.gameCardManager.insertOneCardToDiscardPile(cardID); + + if (parser.isNonColorCard(cardID)) { + // instead of choose colors + // we will forcefully set it to red for assignment 0 to make testing easier + ruler.setMatchableColor(parser.colorDict.get(1)); + } + + return true; + } + return false; + } + } + + /** + * For Assignment1.0 only + * Implement the logic when player choose to draw a card + * + * @Return whether this player successfully played the newly drawn card + */ + public boolean optionDrawCardAndPlay() { + if (ruler.checkSkipAndDraw(this)) { + return false; + } + drawCards(1); + int chosenCardIndex = getCards().size() - 1; + int newCard = cards.get(chosenCardIndex); + + if(ruler.isValidPlay(this, newCard, true)) { + cards.remove(chosenCardIndex); + gameController.gameCardManager.insertOneCardToDiscardPile(newCard); + + if (parser.isNonColorCard(newCard)) { + // instead of choose colors + // we will forcefully set it to red for assignment 0 to make testing easier + ruler.setMatchableColor(parser.colorDict.get(1)); + } + + return true; + } + return false; // played will be skipped + } + + public boolean playerWin() { + return cards.isEmpty(); + } + + public void runOneRoundForTesting() { + cards.remove(0); + } + } \ No newline at end of file diff --git a/src/UNO/RuleController.java b/src/UNO/RuleController.java index c84d31004da333242e078a7e09b4684617de1241..7113e49ac8d0cfe977e687227772eaaa2bcc1bbd 100644 --- a/src/UNO/RuleController.java +++ b/src/UNO/RuleController.java @@ -39,7 +39,7 @@ public class RuleController { * @param player the pending player * @return bool */ - public boolean checkSkipandDraw(Player player) { + public boolean checkSkipAndDraw(Player player) { if (nextPlayerSkipLevel == 3) { assert cumulativePenaltyDraw == 0; nextPlayerSkipLevel = 0; // the next player should no longer be skipped