diff --git a/out/production/sp21-cs242-assignment1/CardManager.class b/out/production/sp21-cs242-assignment1/CardManager.class index 688ff8e76e90e2dbfd7b94fe66d7016924b47aa2..bc11383ba8d1990ad64464166c8f003d107eeb1e 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 index 77e01a48c317fef0d98274cbf4e604c9d13d93e3..ed707a5d848bfe36341a91d099a66097d3c0b42e 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/Player.class b/out/production/sp21-cs242-assignment1/Player.class index c564c36c856a9e56c1b305b4a260500413ed55c3..d3baf62c663311ac8df80a26813c027b722e1778 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 de13870341e7e8e6003d598eeecd6ecb1d335618..cfcfdeff757385315eabfaa03c3974a8e2ef7467 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/CardParserTest.class b/out/test/sp21-cs242-assignment1/CardParserTest.class new file mode 100644 index 0000000000000000000000000000000000000000..794bde9fda9c4cea84edd5984070d1824519a500 Binary files /dev/null and b/out/test/sp21-cs242-assignment1/CardParserTest.class differ diff --git a/out/test/sp21-cs242-assignment1/RuleControllerTest.class b/out/test/sp21-cs242-assignment1/RuleControllerTest.class new file mode 100644 index 0000000000000000000000000000000000000000..da9badcd08ce0b4609ba10d8fa911d6f7329d88c Binary files /dev/null and b/out/test/sp21-cs242-assignment1/RuleControllerTest.class differ diff --git a/sp21-cs242-assignment1.iml b/sp21-cs242-assignment1.iml index 1be80785894dc1b3060b760ebcc1ffc2e9ab188f..98ef08307774761d459a21dc4a8deea06f268abe 100644 --- a/sp21-cs242-assignment1.iml +++ b/sp21-cs242-assignment1.iml @@ -3,10 +3,45 @@ <component name="NewModuleRootManager" inherit-compiler-output="true"> <exclude-output /> <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/src/Test" type="java-test-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/Test" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/src/UNO" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library" exported=""> + <library> + <CLASSES> + <root url="jar://$USER_HOME$/.jdks/external/junit-4.13.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> + <orderEntry type="module-library" exported=""> + <library> + <CLASSES> + <root url="file://$USER_HOME$/.jdks/external" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + <jarDirectory url="file://$USER_HOME$/.jdks/external" recursive="false" /> + </library> + </orderEntry> + <orderEntry type="module-library" scope="TEST"> + <library name="JUnit5.4"> + <CLASSES> + <root url="jar://$USER_HOME$/.jdks/external/junit-jupiter-5.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/junit-jupiter-api-5.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/apiguardian-api-1.0.0.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/opentest4j-1.1.1.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/junit-platform-commons-1.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/junit-jupiter-params-5.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/junit-jupiter-engine-5.4.2.jar!/" /> + <root url="jar://$USER_HOME$/.jdks/external/junit-platform-engine-1.4.2.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> </component> </module> \ No newline at end of file diff --git a/src/Test/CardParserTest.java b/src/Test/CardParserTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c4a698c7e06566222d006246150aec3cc378757 --- /dev/null +++ b/src/Test/CardParserTest.java @@ -0,0 +1,18 @@ +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class CardParserTest { + + @Test + void parseCardID() { + } + + @Test + void parseCardDescription() { + } + + @Test + void isWildCard() { + } +} \ No newline at end of file diff --git a/src/Test/RuleControllerTest.java b/src/Test/RuleControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9c2820484a4f1ac4a184011f438db40821811f5e --- /dev/null +++ b/src/Test/RuleControllerTest.java @@ -0,0 +1,190 @@ +import com.sun.source.tree.UsesTree; +import org.junit.Rule; +import org.junit.jupiter.api.Test; + +import javax.print.attribute.standard.Finishings; +import java.lang.invoke.VarHandle; +import java.util.ArrayList; +import java.util.Random; + + +// Test for judging whether a play is legal + +class RuleControllerTest { + private static CardParser parser = new CardParser(); + + @Test // if current allowed color is red, player with no red cards can use wild Draw 4 + void test_Player_Red_N_WildDraw4_Y_UseWildDraw4() throws Exception { + Player player = player_Red_N_WildDraw4_Y(); + RuleController ruler = new RuleController(); + + ruler.setNextPlayerSkiplevel(0); + ruler.setAllowedColor("red"); + ruler.setAllowedSymbol("skip"); + assert(ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("red"); + ruler.setAllowedSymbol("reverse"); + assert(ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("blue"); + ruler.setAllowedSymbol("skip"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("blue"); + ruler.setAllowedSymbol("reverse"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("green"); + ruler.setAllowedSymbol("skip"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("green"); + ruler.setAllowedSymbol("reverse"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("yellow"); + assert(!ruler.isValidPlay(player, 105, false)); + + + + } + + @Test // test player with all colors cannot use wildDraw4 anyway + void test_Player_AllColors_CannotUseWildDraw4() throws Exception { + Player player = player_AllColor_Y_WildDraw4_Y(); + RuleController ruler = new RuleController(); + + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setNextPlayerSkiplevel(0); + ruler.setAllowedColor("red"); + ruler.setAllowedSymbol("skip"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("red"); + ruler.setAllowedSymbol("reverse"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("blue"); + ruler.setAllowedSymbol("skip"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("blue"); + ruler.setAllowedSymbol("reverse"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("green"); + ruler.setAllowedSymbol("skip"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("green"); + ruler.setAllowedSymbol("reverse"); + assert(!ruler.isValidPlay(player, 105, false)); + + ruler.setAllowedColor("yellow"); + assert(!ruler.isValidPlay(player, 105, false)); + + + } + + + +// @Test // Last round a red 8 played +// void testRed8() throws Exception { +// RuleController ruler = new RuleController(); +// +// } + + + /** + * + */ + void testInitializer() {} + + /** + * extract all the cards that the ruler think as legal + */ + private ArrayList<Integer> getAllLegalCardsByRuler(RuleController ruler, ArrayList<Integer> cards) { + ArrayList<Integer> validList = new ArrayList<>(); + for (int cardID : cards) { + if (ruler.isValidPlay(null, cardID, false)) { + validList.add(cardID); + } + } + return validList; + } + + /** + * extract all the cards that are indeed legal (ground true generator) + */ + private ArrayList<Integer> groundTruthGenerator(RuleController ruler, + ArrayList<Integer> cards, + String color, String number, + String symbol) { + for (int cardID : cards) { + String cardDescription = parser.parseCardID(cardID); + String[] result = parser.parseCardDescription(cardDescription); + String cardCol = result[0]; + String cardType = result[1]; + String cardContent = result[2]; + + if (ruler.getNextPlayerSkiplevel() == 3) { + continue; // skip card played. any card will be illegal + } else if (ruler.getNextPlayerSkiplevel() == 2) { + // only wildDraw4 if allowed + if (cardContent.equals("wildDraw4") || cardContent.equals("wildDraw2")); + } else if (ruler.getNextPlayerSkiplevel() == 1) { + // both draw 2 and wild draw 4 are allowed + } else { + // cards with any one of color, number or symbol matched are playable + + } + } +// if (ruler.getSkip) + return null; + } + + /** + * construct a player who has no red cards, but a wildDraw4 card + * This player should be able to play wildDraw4 card if current allowed color is red + * @return + */ + private Player player_Red_N_WildDraw4_Y() { + Player player = new Player(0, null); + player.addOneCard(26); // green 1 + player.addOneCard(51); // blue 1 + player.addOneCard(76); // yellow 1 + player.addOneCard(101); // wild + player.addOneCard(105); // wildDraw 4 + return player; + } + + /** + * Construct a player who owns all colors, and also a wildDraw4 card + * This player should not be able to play wildDraw4 card. + * @return + */ + private Player player_AllColor_Y_WildDraw4_Y() { + Player player = new Player(0, null); + player.addOneCard(1); // red 1 + player.addOneCard(26); // green 1 + player.addOneCard(51); // blue 1 + player.addOneCard(76); // yellow 1 + player.addOneCard(101); // wild + player.addOneCard(105); // wildDraw 4 + return player; + } + + /** + * Construct a magic player who own ALL CARDS + * This player should not be able to play wildDraw4 card + */ + private Player playerAllCards() { + Player player = new Player(0, null); + for (int i = 1; i <= 108; i++) { + player.addOneCard(i); + } + return player; + } +} \ No newline at end of file diff --git a/src/UNO/CardManager.java b/src/UNO/CardManager.java index 5d14b79141dd02cf6eeeeb7818cb3f8c6279528b..4dcf7bf573898c429317ecbd1b18729e98f65de7 100644 --- a/src/UNO/CardManager.java +++ b/src/UNO/CardManager.java @@ -83,5 +83,10 @@ public class CardManager { */ public int numLeftDiscardPile() { return discardPile.size(); } + /** + * get all the cards currently in card pile as a Array list + */ + public ArrayList<Integer> getCardPile() { return cardPile; } + } diff --git a/src/UNO/CmdUI.java b/src/UNO/CmdUI.java index 9840bb7978f0e7e577c8d7c5ef848f4255475378..75d95c81f7a1498cf9dad8ce565c7fddfb66bade 100644 --- a/src/UNO/CmdUI.java +++ b/src/UNO/CmdUI.java @@ -8,9 +8,10 @@ public class CmdUI { public CmdUI(Player p) { player = p; - parser = player.parser; gameController = player.gameController; - ruler = gameController.ruler; + parser = (gameController != null) ? player.parser : null; + + ruler = (gameController != null) ? gameController.ruler : null; } diff --git a/src/UNO/Game.java b/src/UNO/Game.java index 59c4676261c343600184c771f2e75d059d619818..9009612e6474858a406bc2728df8b87ba207376b 100644 --- a/src/UNO/Game.java +++ b/src/UNO/Game.java @@ -43,7 +43,7 @@ public class Game { */ private void decideFirstPlayerID(int playerNum) { Random rand = new Random(); - currentPlayerID = rand.nextInt(playerNum); // -1 to convert to index + currentPlayerID = rand.nextInt(playerNum); } diff --git a/src/UNO/Player.java b/src/UNO/Player.java index a8756a233349de1a0c5b14bdff36c2902cf14cfc..cbf50ab50ec8a512853f5c4067a31c4593facc3f 100644 --- a/src/UNO/Player.java +++ b/src/UNO/Player.java @@ -15,8 +15,8 @@ public class Player { playerID = ID; cards = new ArrayList<Integer>(); gameController = game; - ruler = game.ruler; - parser = RuleController.parser; + ruler = (game != null) ? game.ruler : null; // for testing purpose + parser = (game != null) ? RuleController.parser : null; prompterCmd = new CmdUI(this); prompterGUI = null; } @@ -137,6 +137,11 @@ public class Player { } - + /** + * For Test only. Add one card with Given card ID to the player's cards + */ + public void addOneCard(int cardID) { + cards.add(cardID); + } } \ No newline at end of file diff --git a/src/UNO/RuleController.java b/src/UNO/RuleController.java index 06d923b0965462749119f2f2d6124ea44858d93e..2c2a08e1b2ff61a101949a48cafbf9ae02bb274b 100644 --- a/src/UNO/RuleController.java +++ b/src/UNO/RuleController.java @@ -1,4 +1,5 @@ import java.util.ArrayList; +import java.util.Random; public class RuleController { public static CardParser parser = new CardParser(); @@ -6,13 +7,32 @@ public class RuleController { /** 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 String currentMatchableColor; + private String currentMatchableNumber; + private String currentMatchableSymbol = "all"; // all symbols can be played in the first round private int nextPlayerSkipLevel = 0; private int cumulativePenaltyDraw = 0; private boolean isClockWise = true; // if clockwise, next player will be the element in player list + public RuleController() { + // initialize first matched color and number + boolean chosen = false; + while (!chosen) { + Random rand = new Random(); + int cardID = rand.nextInt(100) + 1; // generate a number from 1 - 100. these are colored cards + String desc = parser.parseCardID(cardID); + String[] result = parser.parseCardDescription(desc); + String color = result[0]; + String type = result[1]; + String content = result[2]; + + if (type.equals("num")) { + currentMatchableColor = color; + currentMatchableNumber = content; + chosen = true; + } + } + } /** * judge whether a pending player should be skipped @@ -49,7 +69,7 @@ public class RuleController { * @return whether the play is valid */ public boolean isValidPlay(Player player, int cardID, boolean updateIfValid) { - + assert(player != null); String cardDescription = parser.parseCardID(cardID); String[] result = parser.parseCardDescription(cardDescription); String color = result[0]; @@ -57,31 +77,37 @@ public class RuleController { String content = result[2]; // pending skip - if (nextPlayerSkipLevel == 4) { + if (nextPlayerSkipLevel == 3) { 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"); + if (content.equals("wildDraw4")) { + return checkDraw4IsLegal(player); + } + } else if (nextPlayerSkipLevel == 1) { + if (content.equals("wildDraw4")) { + return checkDraw4IsLegal(player); + } + return content.equals("draw2"); // draw 2 is always allowed in this case } boolean valid = false; - // if not skipped, first consider wild + // if not skipped, first consider wild card 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; + // check wildDraw4 at last + if (content.equals("wildDraw4")) { + valid = checkDraw4IsLegal(player); + } + + if (valid && updateIfValid) { updateRule(color, type, content); } @@ -143,12 +169,17 @@ public class RuleController { * 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; + System.out.println(color); + + if (color.equals(currentMatchableColor)) { + return false; + } } return true; } @@ -246,7 +277,6 @@ public class RuleController { 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."); } - }