From 7065bb62b1eab829a0276682b16777ba2fc88f52 Mon Sep 17 00:00:00 2001
From: FrederickPi1969 <pixinyudeyouxiang@gmail.com>
Date: Thu, 4 Feb 2021 19:47:37 -0600
Subject: [PATCH] gurantee card validation & game state updation performance
 (test done)

---
 .../RuleController.class                      | Bin 7103 -> 7117 bytes
 .../CardParserTest.class                      | Bin 583 -> 0 bytes
 .../RuleControllerTest.class                  | Bin 9198 -> 9858 bytes
 src/Test/CardParserTest.java                  |  18 -
 src/Test/RuleControllerTest.java              | 311 ++++++++----------
 src/UNO/RuleController.java                   |  54 +--
 6 files changed, 172 insertions(+), 211 deletions(-)
 delete mode 100644 out/test/sp21-cs242-assignment1/CardParserTest.class
 delete mode 100644 src/Test/CardParserTest.java

diff --git a/out/production/sp21-cs242-assignment1/RuleController.class b/out/production/sp21-cs242-assignment1/RuleController.class
index 3cbec0e8bc6d73ca34e345d23260d98e2851eae0..4cd51c49ff5cbf5b60e2373a96937b5d4bb4fb78 100644
GIT binary patch
delta 920
zcmZvaduWYu7{@=qb9;AY%+4-n??x<PojJ)|Cgsx7keG?NZd!Im%y8u7x+8aTnSSn<
zESHT)&FoN$KaeF-EceThBKKSJ7ti~W{Gs>l>G^%1?{j}H=H1Av8RT|V&8-SG<Zo~V
zjc_Ou3NHx76VKuVmf%q&PM=v;H6t{vY)+^!TpkL?u}tACtE_DN;riLen}kcq@IkAj
zya~>J-oP0WJ+*j6t!KOSa@W$)jPn)8=(UHoft(l<E!!fS5w;kvS~~rQQ7qV0@j9ua
zN$aD944{a7#xk67jA1;JDW;r>%wh^*rm~C@)-sJPgxF05`^0jL*_@z~E6kypdE8<?
z_gTnuBD`fW_oViU-yI0{LSE7fuXv5JwTJj{SXxx*CQdSG;T`Ry?XFzoF;8%l!)2Zd
z=AjoCXcf&Mk8?Z|El$dA;=TBKc`1Q<kl_1CU*ZkxAF7l=uE7Ueijpg7Gu@d+LZazt
zZ>6qahLmy#Z6p^?F`5`qPbBzfm>mM{81n)~j0FLo*u1tB#<-v_Vvjo`5MxomX|aO;
z*!u*1rM5liub*y6&Wu_y8l*@kcST0%C~~<pSS^i2@w0*stR#z71Xx3F*3qB!3}l1s
zWg{cmB%9g7B(_pYjqGNdOkGQy9db)MCBiON%VsvSS6=(&JHSzDImICwIm|f^M`V6=
zx+tYsE!D?Tx~Sv&ddk79(^5hsiJX!2O>`qhE@v4e6CNUX0q6Cm)C200t}#>f;IwS_
zRpB@5YQJBvO6#d^+IX|zw*_yp@dSTQ^~lDH{MqhS!Ji3Upx)Tn1An%9XY(@BN0}dG
zo1f%FKFcY5A(gK(mTz?CyIzrA;855ukJGz5ltX*WeC5(3%mUTMg4GgTXXdK5`o8(R
kYZ7tFhhHVrO{LIRr7~KjQKI}*s&uNANsY?Ttr<^$0hr9(hX4Qo

delta 900
zcmZ9KTS(MV5XOJU-SzKUxw`JUt{Nnf$%;jGF@o-)M0Bx44NF^cHE6TakV;Xa3*FUD
zgk6;)>Om}1H|Zg%e2Ivnm!x|}XjE486ht-qFCyZ=F!RkfbG|ck?q~O9H_fqH{J~)7
z!UNVx*2>M5fhcw;jH*CQpsG3$O*FNmsUuS1SiiwvSrN$fZwf50DhpI~eY5@?Ww&4z
z4!qPVE<4xPjU%!Ro25PM+1hY>qnrgHrkup?%Lq}_{ySrBBl-V~#A}4vZkb`%jdA-M
zRxp=5=ChK;tdf(@Y6>W(ko6R?Nup|rYAK<fb?nE_Ap*2e&S@&>VFSHv<TgR>vV})%
z<r&);5Uu6E6*I^ii9|cYyhEAZQ=CMEy#_wu#X>Uud?ZGmIe{BI=LI%0=$4`wBAG%5
zuf#LR<}$CvixN40Mx@rxTWK_j(axV@?FP{Xi<C0RGWcX#ak9XpJjQn%%HfTO4PRa^
z@)bHhKCif@hgp}X$^0|J6rU}`7M~HK+UJy<-L%5U&vb4#r<~*qvD0S>voin5Ps?<c
znE8yqb-FJ;xgp%6Q$$*LEVhQRVz<i!cgjO*@yOy+<T<G{;A0n4+09J$Fq^$BU>{3l
z>iHa$O&pRf9HxvTvX@5b(<~j1NtG7%aY0YWY2gefxhS@Sb9B*4ZzOHp<h(9*6(~c8
zT;tV6{lwLp+949I;NYrU-$@$RWK3Pmk*UuUJcsN0l>4me)3*}bdTBzs?T+yKbzGuH
zHz!P1_XMA9dmz|D6HD?;Rs$xt)RS&|Cirs`zaaRKi4S|y)jPq;ydPwfALV<D$T#?e
zo6j<mFO1`>KAM;l!FQAXK5=3MKeWrcK-N^~%@OUQMAhkTZ<ey_A@AUXF+`~t*{p*!
b70YxL$1>$)t%@h8T+}N!O)5c;B)$9vf#BNd

diff --git a/out/test/sp21-cs242-assignment1/CardParserTest.class b/out/test/sp21-cs242-assignment1/CardParserTest.class
deleted file mode 100644
index 794bde9fda9c4cea84edd5984070d1824519a500..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 583
zcmah_%Sr=55Ukm(iFrSa2f>3!AK-pKM34kQSX5Nvd9oQ|ht2LXJDdOVBzW)x{3x+o
zf*3sP!1Q!ichwaA`Stz*V1)e!JXC7%Ti8KWp|{j)9mG0W1b4HgiJU_9f)a9<3YFuN
zX$`vyow2s_du?-LpG?jp{y3c*g>FcRxh+;R#`P>_WDus2j;Gp^)Em%uFO>6F;kL|3
zp_z%Oz&8_xgNGt<v@%o5iP=|4k~-}uO_+Qbrgjl53wFjaBWG-&GYZ5H3k`pTCMJ(8
zWkOSEQ2tEuTt499F)eIlu1SpUZF@PFHQ)#}-i{ofFYgBmb<P5Ql^~~YP_LYLXmFIa
zj5Rs7HhBweu7D1Ay&v)!S2FjHSpP#*EFi=#Uy<$c3-rrH+NC0ca*=+C2(OFXco=>I
D?f+m%

diff --git a/out/test/sp21-cs242-assignment1/RuleControllerTest.class b/out/test/sp21-cs242-assignment1/RuleControllerTest.class
index 193d470dab21a84f25e59a09e3affae987875f93..d81b897b07ecfc6a709c8c887add8725d235eada 100644
GIT binary patch
literal 9858
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

literal 9198
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
R35ZiB$=3>h<&QXg^gn8Sd9DBe

diff --git a/src/Test/CardParserTest.java b/src/Test/CardParserTest.java
deleted file mode 100644
index 9c4a698..0000000
--- a/src/Test/CardParserTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-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
index dfc8040..be37cfc 100644
--- a/src/Test/RuleControllerTest.java
+++ b/src/Test/RuleControllerTest.java
@@ -23,61 +23,19 @@ class RuleControllerTest {
         System.out.println(player.getCards());
         RuleController ruler = new RuleController();
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
+        setCurrentState(ruler, "red", "none", "6", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
+        setCurrentState(ruler, "yellow", "reverse", "none", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "blue", "draw2", "none", 1); // previous blue draw2
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
+        setCurrentState(ruler, "yellow", "none", "none", 2); // previous wildDraw4, declared yellow
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("none");
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("none");
-        assert(!ruler.isValidPlay(player, 105, false));
-
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "red", "skip", "none", 3);
         assert(!ruler.isValidPlay(player, 105, false));
     }
 
@@ -90,63 +48,30 @@ class RuleControllerTest {
         Player player = player_Red_N_WildDraw4_Y();
         RuleController ruler = new RuleController();
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(ruler.isValidPlay(player, 105, false));  // playable!
-
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("none");
-        assert(ruler.isValidPlay(player, 105, false)); // playable!
-
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
+        setCurrentState(ruler, "red", "none", "6", 0);
+        assert(ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("none");
-        assert(!ruler.isValidPlay(player, 105, false));
+        setCurrentState(ruler, "red", "reverse", "none", 0);
+        assert(ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
+        setCurrentState(ruler, "red", "draw2", "none", 1); // previous red draw2
+        assert(ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
+        setCurrentState(ruler, "red", "none", "none", 2); // previous wildDraw4
+        assert(ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "red", "skip", "none", 3);
         assert(!ruler.isValidPlay(player, 105, false));
 
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
+        setCurrentState(ruler, "blue", "reverse", "none", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
+        setCurrentState(ruler, "green", "draw2", "none", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "yellow", "skip", "none", 3);
         assert(!ruler.isValidPlay(player, 105, false));
-    }
+     }
 
     @Test
     /**
@@ -157,61 +82,19 @@ class RuleControllerTest {
         Player player = player_AllColor_Y_WildDraw4_Y();
         RuleController ruler = new RuleController();
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("none");
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
+        setCurrentState(ruler, "red", "none", "6", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
+        setCurrentState(ruler, "yellow", "reverse", "none", 0);
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "blue", "draw2", "none", 1); // previous blue draw2
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
+        setCurrentState(ruler, "yellow", "none", "none", 2); // previous wildDraw4, declared yellow
         assert(!ruler.isValidPlay(player, 105, false));
 
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("none");
-        assert(!ruler.isValidPlay(player, 105, false));
-
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("skip");
-        ruler.setNextPlayerSkiplevel(3);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setNextPlayerSkiplevel(0);
-        assert(!ruler.isValidPlay(player, 105, false));
-
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("none");
+        setCurrentState(ruler, "red", "skip", "none", 3);
         assert(!ruler.isValidPlay(player, 105, false));
     }
 
@@ -222,12 +105,11 @@ class RuleControllerTest {
      * 3. test previous card: red 8
      */
     void testSituation_Red8() throws Exception {
+
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
 
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("none");
-        ruler.setMatchableNumber("8");
+        setCurrentState(ruler,"red", "none", "8", 0);
 
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
@@ -236,6 +118,7 @@ class RuleControllerTest {
         System.out.println(validByRuler);
         System.out.println(groundTruth);
         assert(validByRuler.equals(groundTruth));
+
     }
 
 
@@ -246,9 +129,9 @@ class RuleControllerTest {
     void testSituation_Yellow0() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("yellow");
-        ruler.setMatchableSymbol("none");
-        ruler.setMatchableNumber("0");
+
+        setCurrentState(ruler, "yellow", "none", "0", 0);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -265,10 +148,9 @@ class RuleControllerTest {
     void testSituation_blueSkip() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("blue");
-        ruler.setMatchableSymbol("skip");
-        ruler.setMatchableNumber("none");
-        ruler.setNextPlayerSkiplevel(3);
+
+        setCurrentState(ruler, "blue", "skip", "none", 3);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -285,10 +167,9 @@ class RuleControllerTest {
     void testSituation_greenReverse() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("green");
-        ruler.setMatchableSymbol("reverse");
-        ruler.setMatchableNumber("none");
-        ruler.setNextPlayerSkiplevel(0);
+
+        setCurrentState(ruler, "green", "reverse", "none", 0);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -305,10 +186,9 @@ class RuleControllerTest {
     void testSituation_RedDraw2() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("red");
-        ruler.setMatchableSymbol("draw2");
-        ruler.setMatchableNumber("none");
-        ruler.setNextPlayerSkiplevel(1);
+
+        setCurrentState(ruler, "red", "draw2", "none", 1);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -319,8 +199,6 @@ class RuleControllerTest {
     }
 
 
-
-
     @Test
     /**
      * 8. test previous card: wild with red as declared color
@@ -328,10 +206,9 @@ class RuleControllerTest {
     void testSituation_wildDeclaredRed() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("red");  // only color should be matchable in this case
-        ruler.setMatchableSymbol("none");
-        ruler.setMatchableNumber("none");
-        ruler.setNextPlayerSkiplevel(0);
+
+        setCurrentState(ruler, "red", "none", "none", 0);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -349,10 +226,9 @@ class RuleControllerTest {
     void testSituation_wildDraw4DeclaredRed() throws Exception {
         RuleController ruler = new RuleController();
         Player player = playerAllCards(); // a player who owns all cards
-        ruler.setMatchableColor("red");  // only color should be matchable in this case
-        ruler.setMatchableSymbol("none");
-        ruler.setMatchableNumber("none");
-        ruler.setNextPlayerSkiplevel(2);
+
+        setCurrentState(ruler, "red", "none", "none", 2);
+
         ArrayList<Integer> validByRuler = getAllLegalCardsByRuler(ruler, player);
         ArrayList<Integer> groundTruth = groundTruthGenerator(ruler, player,
                 ruler.getMatchableColor(), ruler.getMatchableNumber(), ruler.getMatchableSymbol());
@@ -363,12 +239,84 @@ class RuleControllerTest {
     }
 
 
+    @Test
+    /**
+     * 10. Test if ruler is updated correctly if last round was red 8
+     * suppose the user cannot play wildDraw4
+     * All possible plays are tested - no need to do more other than wildDraw4
+     */
+    void testUpdate_Red8() {
 
+        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());
 
+        ruler.isValidPlay(player, 25, true); // check behavior of red 0
+        assert(checkStateUpdatedCorrectly(ruler, "red", "none", "0", 0));
 
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 20,true); // red skip card
+        assert(checkStateUpdatedCorrectly(ruler, "red", "skip", "none", 3));
 
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 22,true); // red reverse card
+        assert(checkStateUpdatedCorrectly(ruler, "red", "reverse", "none", 0));
+        assert(!ruler.getIsClockwise());
+
+        ruler.changeGameOrder();
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 24,true); // red draw2 card
+        assert(checkStateUpdatedCorrectly(ruler, "red", "draw2", "none", 1));
+        assert(ruler.getPenaltyDraw() == 2); // next player should draw 2
+
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 58, true); // blue 8
+        assert(checkStateUpdatedCorrectly(ruler, "blue", "none", "8", 0));
+
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 102, true); // wild card, color should be NA, a special state
+        assert(checkStateUpdatedCorrectly(ruler, "NA", "none", "none", 0));
+    }
+
+    /**
+     * 11. test behavior of wildDraw4
+     * given player has no red card, but has a wildDraw4 card
+     */
+    @Test
+    void testUpdate_WildDraw4() {
+        RuleController ruler = new RuleController();
+        Player player = player_Red_N_WildDraw4_Y();
+
+        // previous play red 8
+        ruler.resetPenaltyDraw();
+        setCurrentState(ruler, "red", "none", "8", 0);
+        ruler.isValidPlay(player, 108, true);
+        assert(checkStateUpdatedCorrectly(ruler, "NA", "none", "none", 2));
+        assert(ruler.getPenaltyDraw() == 4);
+
+        // previous play red draw 2  -- penalty draw should be 6 after wildDraw4 played
+        ruler.resetPenaltyDraw();
+        setCurrentState(ruler, "red", "draw2", "none", 1);
+        ruler.increasePenaltyDraw(2);
+        ruler.isValidPlay(player, 108, true);
+        assert(checkStateUpdatedCorrectly(ruler, "NA", "none", "none", 2));
+        assert(ruler.getPenaltyDraw() == 6);
+
+
+        // previous play wildDraw 4, and declared red as matchable color
+        ruler.resetPenaltyDraw();
+        setCurrentState(ruler, "red", "none", "none", 2);
+        ruler.increasePenaltyDraw(4);
+        System.out.println(ruler.isValidPlay(player, 108, true));
+        assert(checkStateUpdatedCorrectly(ruler, "NA", "none", "none", 2));
+        assert(ruler.getPenaltyDraw() == 8);
+
+    }
 
 
 
@@ -481,4 +429,31 @@ class RuleControllerTest {
         }
         return player;
     }
+
+
+    /**
+     * helper function to set current game state based on previous player's move
+     */
+    private void setCurrentState(RuleController ruler,
+                                 String matchableColor,
+                                 String machableSymbol,
+                                 String matchabeNumber,
+                                 int level) {
+        ruler.setMatchableColor(matchableColor);
+        ruler.setMatchableSymbol(machableSymbol);
+        ruler.setMatchableNumber(matchabeNumber);
+        ruler.setNextPlayerSkiplevel(level);
+    }
+
+    private boolean checkStateUpdatedCorrectly(RuleController ruler,
+                                               String truthColor,
+                                               String truthSymbol,
+                                               String truthNumber,
+                                               int truthLevel) {
+        return truthColor.equals(ruler.getMatchableColor()) &&
+                truthSymbol.equals(ruler.getMatchableSymbol()) &&
+                truthNumber.equals(ruler.getMatchableNumber()) &&
+                truthLevel == ruler.getNextPlayerSkiplevel();
+    }
+
 }
\ No newline at end of file
diff --git a/src/UNO/RuleController.java b/src/UNO/RuleController.java
index 67639ad..c84d310 100644
--- a/src/UNO/RuleController.java
+++ b/src/UNO/RuleController.java
@@ -9,7 +9,7 @@ public class RuleController {
      */
     private String currentMatchableColor;
     private String currentMatchableNumber;
-    private String currentMatchableSymbol = "all"; // all symbols can be played in the first round
+    private String currentMatchableSymbol = "none"; // Card played in the first round can only match by either color or number
     private int nextPlayerSkipLevel = 0;
     private int cumulativePenaltyDraw = 0;
     private boolean isClockWise = true;   // if clockwise, next player will be the element in player list
@@ -76,38 +76,43 @@ public class RuleController {
         String type = result[1];
         String content = result[2];
 
-        // pending skip
+        boolean valid = false;
+
         if (nextPlayerSkipLevel == 3) {
-            return false;
+            valid = false;
+
         } else if (nextPlayerSkipLevel == 2) {
             if (content.equals("wildDraw4")) {
-                return checkDraw4IsLegal(player);
+                valid = checkDraw4IsLegal(player);
+            } else {
+                valid = false;
             }
-            return false; // any cards other than wildDraw4 are not playable
+
         } else if (nextPlayerSkipLevel == 1) {
             if (content.equals("wildDraw4")) {
-                return checkDraw4IsLegal(player);
+                valid = checkDraw4IsLegal(player);
+            } else {
+                return content.equals("draw2"); // draw 2 is always allowed in this case
             }
-            return content.equals("draw2"); // draw 2 is always allowed in this case
-        }
 
-        boolean valid = false;
-        // if not skipped, first consider wild card
-        if (content.equals("wild")) {
-            valid = true;  // can be used unconditionally
-        }
 
-        // then other colored cards
-        if (checkAttrMatch(currentMatchableColor, color))
-            valid = true;
-        if (checkAttrMatch(currentMatchableNumber, content) || checkAttrMatch(currentMatchableSymbol, content))
-            valid = true;
+        } else {
+            // if not skipped, first consider wild card
+            if (content.equals("wild")) {
+                valid = true;  // can be used unconditionally
+            }
 
-        // check wildDraw4 at last
-        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);
@@ -118,13 +123,12 @@ public class RuleController {
 
     private void updateRule(String color, String type, String content) {
         setMatchableColor(color); // wildcard "NA" - this will be updated later by player declaring the color
-
         if (type.equals("sym")) {
             if (content.equals("skip")) {
                 setNextPlayerSkiplevel(3);
                 // the states after the skip.. (supposed skipping is done)
                 currentMatchableSymbol = content; //??
-                currentMatchableNumber = "all"; //??
+                currentMatchableNumber = "none"; //??
 
             } else if (content.equals("draw2")) {
                 // next round match by either COL or SYM
@@ -138,7 +142,7 @@ public class RuleController {
                 setNextPlayerSkiplevel(2);
                 increasePenaltyDraw(4);
                 currentMatchableSymbol = "none";
-                currentMatchableNumber = "none"; // color will be declared by player later
+                currentMatchableNumber = "none";
 
             } else if (content.equals("reverse")) {
                 // next round match by either COL or SYM
-- 
GitLab