From 27c4377453f0ffa62a0818988addea7427a74a6d Mon Sep 17 00:00:00 2001
From: FrederickPi1969 <pixinyudeyouxiang@gmail.com>
Date: Thu, 4 Feb 2021 01:16:13 -0600
Subject: [PATCH] wildDraw4 test passed

---
 .../sp21-cs242-assignment1/CardManager.class  | Bin 3374 -> 3530 bytes
 .../sp21-cs242-assignment1/CmdUI.class        | Bin 4496 -> 4564 bytes
 .../sp21-cs242-assignment1/Player.class       | Bin 4425 -> 4568 bytes
 .../RuleController.class                      | Bin 6727 -> 7111 bytes
 .../CardParserTest.class                      | Bin 0 -> 583 bytes
 .../RuleControllerTest.class                  | Bin 0 -> 4717 bytes
 sp21-cs242-assignment1.iml                    |  37 +++-
 src/Test/CardParserTest.java                  |  18 ++
 src/Test/RuleControllerTest.java              | 190 ++++++++++++++++++
 src/UNO/CardManager.java                      |   5 +
 src/UNO/CmdUI.java                            |   5 +-
 src/UNO/Game.java                             |   2 +-
 src/UNO/Player.java                           |  11 +-
 src/UNO/RuleController.java                   |  62 ++++--
 14 files changed, 307 insertions(+), 23 deletions(-)
 create mode 100644 out/test/sp21-cs242-assignment1/CardParserTest.class
 create mode 100644 out/test/sp21-cs242-assignment1/RuleControllerTest.class
 create mode 100644 src/Test/CardParserTest.java
 create mode 100644 src/Test/RuleControllerTest.java

diff --git a/out/production/sp21-cs242-assignment1/CardManager.class b/out/production/sp21-cs242-assignment1/CardManager.class
index 688ff8e76e90e2dbfd7b94fe66d7016924b47aa2..bc11383ba8d1990ad64464166c8f003d107eeb1e 100644
GIT binary patch
delta 229
zcmZ1{bxNA+)W2Q(7#J9=8Rl=~y3Z!aot|3aoLH0+keQRp$RMtvIk}Nlgw2|fK?ls#
znf#K?gl`fT!(@gj><m+R7(5uJP1a^V!Z>5{2X-@-*^CUTlP9o?PtIfKV`FES%g7)H
z<+4vM=CEPp+`NG!oF#{mp_`$Hfr+64Xt6#6BM>q$uxf2#klg@eFfmjDNj9)xBtsRD
x%>z-uP|Z*S<duQd^fE9r^nqFZ3=@Df2T;vKAfC=J6UgHNvSu;NVVK7t2>@(1E@1!w

delta 114
zcmX>ly-tej)W2Q(7#J9=8Rl%{y3aP*f!&0!pNnAv!$fw5NjwZ543j4}u^(ZaI@z7W
zjB&=~Iu0$CS&R&FlN&kYCZFQ4VdU7%&Kb_4&dksaG=zzvhoKipb1*P6^a1e{hG{?^
O7mzibVJ5?D21x){#T#z`

diff --git a/out/production/sp21-cs242-assignment1/CmdUI.class b/out/production/sp21-cs242-assignment1/CmdUI.class
index 77e01a48c317fef0d98274cbf4e604c9d13d93e3..ed707a5d848bfe36341a91d099a66097d3c0b42e 100644
GIT binary patch
delta 1235
zcma)+>u(fQ7{-4yUAx<Mx7*s=wmaJbtzx!li9v)&m#T;stszB$YK$$lat#OwwB@pp
zs_{dKtwYcf5(21eVimiz10O_zY6ZCnrv4u!d^5!Lnb~#YM>E+u=e+04d4KP7p0gv3
zvyCHzqo=Ksa1o_c1*xWHwBMSz6bO`t*1X@-yS1a|z1Mm+Z0ob917r5KqTNZY0E#H*
zVXuu*@3jUG@dE~zG;%QYKq7ttBi=lPKR$_nU7FB&D&lD>W+=@?DpUB3DI#7lsP`nb
zZs<G>6WXDGAN8)IQ12pq6nCpBp^_kR5w}vtPnxRErsFbKG*V)&YK6hH`f+?29>w$%
z@aq#pZARgL$JutEyv@(M`r>DMu=r52Rypbj5haX81u+#}ulF1pnaktMqlNiA$s;V%
z*2{Q|<;t^yX4+Z6t6H&!g&tW#=!*#O3o{ZZMgzZcO@+YgIoc9dah>0kJH$)e;3j5J
zX;*TK+p2%s9xB<9bY*cBi~1b;#<1MJ!+OhMt7O&e_Nc?I6oskGDa}PR>K|i)*=n66
z9CtXR-;vf-=6;$6cYcx&tc!f?FnpO&2Rk}0qO91RQWu_*qEAcfB~o}P$q3I#kXDJ1
zP{C~qKP&9#S*xPEd4XPDlzi>%<YjjA3j63_zl0cKH7Drwq|iZVk9K}nU8Yve=Z@Ed
zlQ}6&t-Y&Q_TXv=Jis&_P-EElf*V`hDpl|<mtU>wYcUhC|J0Z8>dQ$Lu|EnmC)Y~)
zbyTrl*}7QF8$8FG;(JTcw-xDDuSd@YHuH{XH}au+N7<4m?$W`!oCOl)lN@IigI*Pe
zcHQI=o1WD|`2MELFpThD)EX+%URb(4xkqN&D;Wo5qJvUkP?p+H69=SZQc50{o=3zr
zls8O`vi;%Zv?%th;~Hi~-Y`pZhAES^TsxWCsXq6GYx$^a`8o<b%iFQ=;pDIwj*4MK
zS{x(7sHQ)ajcsup7sF>__*@Jp@(fjZhPwaB(4^~;rFI#VKBGiA2CHmK>@<GP$k}Ig
zUB3|Tm!kfPCrIgBzP8Vn?TCG&72{g*t(5vsSLK}a{a#mXLRL)ME#+-<r*xi<$LL;f
P_dg+f<qc*vGWqi#Mq=uC

delta 1014
zcma))Sx-|@6ouEl72497S}CQaA_(zTB{4+QAP^<S2@OF&j6*AtAQ(}Gh+wEf^hFKD
z6D1fD2h=9cg@Qf^&Z4L|M1O-nz#o9?UYb6elb+jivd>=M-fRELjpRnU2S+_7n-NyH
z%vgeq!LIDsl))f=8-}cw>W)xDOJ(T9p3vcAM?|x+GweV*0HYeIeSOH*z7kUK0urWp
zYJ<!LD9STPN?-zdMTtXSHwMcDoQc1S$Kf7>JC4k8_+yNgG0okBRPIJBLK=4yTrguP
zELZ^>UIJbUYpL-HuNg=YykUsM^;S%$^@iPil*tDkKFETTFEZfc@*|gtRv`z4$j2Jw
zp$N-Sf&!E=c?I)qLjb$564i{L6iSIw1OwjTJ&RK_e83FBfSQE53ES}zpQvrYR?Olv
zWHV*A;0wM&hdD80YL(og*z8MZp~tf((H2E&bp8f=;1rE!zvL7b%sJsA*0h%87Q>Ep
zaAO1MOW4?rBrW4jIdwLn7Ml?yX#)issdtjM?bv}+*ohugq940)9aS2;LQW4W`bH3$
zQ3d#}{R2i4>@s72@J=FTzNNEJ1@sb7MdP<?7=?1FZv-}F621JLD~-<oT`)|epS1*9
zYS^7xT2#mUds#s}Dff|PKW`55bO;AX9O61k>V8t6M-wjdr1n>3tmm$h>3AMUkh##U
zaZAkFq%q253|jkIJLqjEd(=fw&#=DJbhn#M_OMUA>{FPXI!lJWq<<OA{8P*C<v^)U
zs{U0Z{ac^#&rYXRcV%YEPP~{CRkld@A~i2jbAWXUdK01UAeP|@b+1zM8a0QgIh@pV
zB{e<&shP*gT%epIod>#k3hDGP=plmkX5UTdaf^d>o6|o^zB{D6i`BTto=3$Wd#mq0
jBOWl~Av^Pkllqvwd%`(>%9)CZc1L+ioL)_c=ML{bw6(zo

diff --git a/out/production/sp21-cs242-assignment1/Player.class b/out/production/sp21-cs242-assignment1/Player.class
index c564c36c856a9e56c1b305b4a260500413ed55c3..d3baf62c663311ac8df80a26813c027b722e1778 100644
GIT binary patch
delta 656
zcmY+BS!)wf6vuxzNy%iANh7U|B^ZOHDq}?uT(H!ws81qdK;nx`n}F2XN^AuY^}z>U
zmHVs+K3KIb*_v9Vpmuj(5x;=nLOnAR1HSz4oOA!nnRA!d|L8JTe$UJTy?iT~I}FP@
zKIRObIpUnlIs?PH&p4)U8+Rj4K7Vwi(C-v=Uu0W+!DNx8Fv~G=tmw;;x%68_vVXi-
zER2i|U(B3#E)+5<Gw2la=XV>t*Qd;s`lK(JcIb;Dt*@AAy=Z0!q9|IpFZe)4m?K0`
z&ck*cWv6O{?dMpzI;}MlJr&w}7BG9Nthq->RY*%2DYMS6x`n$TC(2DVHruMk){<BS
z_1Wm!lN)JcJMHXeHHYY6Qf@MS6pwf;m4xzym<-~6Jtav1SKmqK4_ySq7%D6k`@&>m
z0tKP=Qr7d)Y<I;IMlq>MP=J~UT`RvcAsxk(-V<9|Rc}O$H)2aVX-&5M;~VVa856=T
z>F?I9EiOACUV9sCgPpvP1l3INJHFyh{8v*k+EBDG#O!|xzvC;atuv~mM-xMWKNAP8
zDqd0+clRw0O0t`6*;Nv$X|7Fi{e-)b01pLC@A8HWc+=Zvhfi*9(_-5D)gZ4t&1-pF
Xhzo080^WG|jt`!b;v=8=it79e9j%Cn

delta 443
zcmYk2yGuf07{;IPsOdTCDYLv#X%vnIEgCok?OC8pG+DHmP(dyI39c_~H3n8z-cphf
zDl4_@J~Xs9)?X04dMsMr@4Y<F?}6`av_7IVzRMK=A-ralfQe>JVbvO0k1Z}w$5x}U
zg~SqVn|3KIkEvG*u5n{T&4M}9DJfUQEzKx{qDcv5K>sM<qfgmMO<C<S;#t6>jmo@J
zfq(&fT=zL^3wr4I4br69Ef?U*!;^!T*32#;QPMmV^1==u`Y?`u1mHlL)x(+xhdAPi
z-8hDYiLEXJCnWAe$da%`eYy6<3<Y^6Nhq+YrPVg6w)TWJEj3%Q!`SF>OBAuD?Z0{x
zCUA-rJ4@87?ua5~RQJDY5NDn1yZYg8u_R7f>97olqr&;(FJIeeysz%EDA7SroU6k&
g<=5eyy%H{P$*;;?m7R@ty~924TEF;!C%nM@16hn=AOHXW

diff --git a/out/production/sp21-cs242-assignment1/RuleController.class b/out/production/sp21-cs242-assignment1/RuleController.class
index de13870341e7e8e6003d598eeecd6ecb1d335618..cfcfdeff757385315eabfaa03c3974a8e2ef7467 100644
GIT binary patch
literal 7111
zcma)A33yyrb^fnrc{8KuNE*qKCz9=P635m^@)l!*WE<O3f(5dp*p3}LEP0-JmM0p`
zo6NkCQ3xr81PE&kfkb9AtD!Z<II#i>;FLnsQrd>@&^p~)O3T`XHVfuI_dU&w76+`)
zZ{EH4+;h)4|Jm;Q)-R^N2B1$}6GI&W27)F+2rD$+X^mTJb5?$2?afE-w9~FacuO{)
zb+;)5R(BqVAi@_bmy05XdIP43MYu}gs=ei$-S6bxqLa(nMTORMxmdLG?rzIXAG40+
z?0pksN1U8Oe5iWFKDU_7kMs$zMiWhlD>Ucr*So_xYr-z>8_gDm>~Wi$)S%F`m{=?{
zi__(?a?WzI<Myzfw{q^pK+!rb6m2G!AfZs7E%oP|^ytBC$>#3E2HIzIE4$g;+Pzjj
z<BY{{H3ITjW@0&#3Wgj$n5WXg)q|b-%xg?si|c5Gf>kVuV&+i4Rm=?QQC|$#W0ir`
zCOVN)h*g&;gWe%Cg9DVA4CiYu`@2o_V2wgcg?_*;rHk2un|1OEt5(n7)_ME<xd_$~
znS7aAZNLo%Hkx<|HqB_Q5%tVXUF^HcR<0yM)$sA~?&~}(hJ2}smkF*k^=ugXot#q?
ziarxtgkm1hd&*-+Y)!Jw#LESZ=Ig8yZ72Ga2C^mbhKzw73QMZ4uw#be#$wSa#;{$G
z8NiJOZZh!-?4s13S12qxn$2g1>=7#`##Lxr?ORquyQ5gNCWf*lm*Bh-uQD)XVmJ0s
z3VqmY8bY2e-8fcoC$xJXmLrEv+=9Id(F~nRPN1R09S+2(^?nn#>NqLdCEKm>z6cI-
z%kgY3Bfhv%Xb+h<jN28O(#P!dsGc4yX}ReftA{*wXzvJKqY#qQHbn3`9wuCbfo0+d
z(hM%mV2A4#bu=yP6E#TsVbw`>Vz5md#Rz?~T*z3iEpe*wvRdGM^7#zr&v-`2nz$3A
z3Jo;Dj$F<;ZfCSx%yFn0F_<*QXx`GqSOgB$D>1G`vb#(a#n7!}vu9S+=OpTd#5GYC
zlICnaU9_!IZM;WtoXql0-j0Cq8AWblh|0D%n7Bt!YGCTgkJzuU#_XGm8Jli3fO}2+
zhPYE?#CA2h;wHao;>~!A$d<jE=3%BAl<E6TycKWbn6&dk*pC+j4-n$2BQt&2BXZQ+
z@lF%(60VVK-aTOD%61g*!FvrnXyOFkH#;;ZO0F$da>{a&XZftNc9;p$r6Fx=jQ;!(
zK49R3CLR{+d;UC|hGr<pj$A&1Nrj{~WO7|^vY<0+^0<><<tC5VNx`6CXCnBJ!Xb}p
zeT6D*<@JW7m9(?&F}uhY?Viar!y)O&xZ>&Foa-d5aVMK0Z)Wr=`;RCb^w@9cO-gp1
zkwngL^0|qbgXZvajwY@AL^3VeCV3_1A5-Y|nBOp)Iho~Zv=mmyt%)@ed|ctStuOX3
zDZY~2=Q2`~d+mZ#bdxVeUb`la_em3<!l&sEbeKxz^JAlBCQa4k;=VYm@M#mD#pjsE
z#;j4Pp=rxKNVD^;<XU-`>F4?d!gaxF62otsI3p6oYbEefavTb$ManOjI43j-34uBJ
z-7C{^b_JUBQ4^0z^1O<qgI-NN&YBv*6ti7gN~|~h61_R?l<E7$%4{3~kI54zz9Q!>
z%CN9is(KtjoQ|{YxG4IkqWB$r-N5gf_y)eoNE9F#!6W>UFx_qCEru1D==$pq852+8
z_YM4kiEl}y)z2cWXEl_GA^WHsM;I4{`j1Tfv5>~OC<`n|;?=|1oE?WDG~dCW8u&94
ze=dqQR3p1T$1=urMJ=*LJ5#Ax;w|Npl=1-O?sqaG_fR%(duHD+^-lU8CvD{pSjDW2
z{mEdFjYy>@s%cI7SnotEX&r)MHjY_x$~k6-<^`%h3y6*tQo9oc9%rQ4jIsZciDOxc
z-8eM6Mbjr7b4qreHl+&b(cM<T7pqYh96^fWN#Ww@t|Mc<c&UV0ZKi_hc8mEfn~{=6
zZk5_Lc)0U0$6n<bJgR9VD^sz7+8!q~F1rmpLsmH{>|W{M){^cJ_dESkLfOe>MNw|2
zLVl8*UC^mj>uf{Q8opL3FRRYpu%)KR`Kh~a8@V*n;r-g?!AQ;NG3;!(R;_z8U2T}v
zO}DqL+_yk6qu4rAd687pHxT@y&NF@>>eR#7T0JBI(ARlDc1R4=^wwc-jSy@}=lte8
zw$CXS)AmhtV|vJJsMm-OQ|z6N<I<p3VYlrbb26odurgvQqN0Y1F$Pt=!gW=5&*sOS
zQG2Zig_o9YveK?oB*c%@CM2KGW4enTle0_L4LQzexzJZ*U5`=i%&d+32~_Q(PhRWm
zGaYITQ=dgYZzTJ&uH6?=ixk>@TPDY5E+2j&=}l7G28E9K1Rl0YP0_R<g@>?5(M(hX
zAJ#&)72e!px}Mk5>GJ2--fT(EY*Se2ug%n^qYmN9NNtnSnpC=umLe*laP5B9Tsvv;
zzdxPK=94VMUac(<<24c0&bn|#!i<;IlA4y6mce}9F6snkmkhOx;>-hjA^Z%*ThX!?
zCuLxGU}FVqOC1YZ1W~y(@vTm75&D;R1^vt0qW<M&Pyg~lr+;~E0~(jeg@NN%mMY#y
z`TR>j?IE8iXYWcq4wZ@<7Z6PaE}%gk&8gr8v<9}cpTzzraP^_b(XqR`F*Ajw!8^8{
z$BLy>SlJuoOCmUhuHH~06xh%kPK1IRdW}RVw4pbWFyd>cuznIvsYLh!Hm4Gi3+N4Q
zIeq!;7kOv^f5qoswD8%)l4VlKMQETJEqo@>jCQ^)=go8##a@Rts=NgKTp8re@@B5y
zilulRmcb^~D3Tb%H7L+Pe@%|Bredpb6TXY@QOR}Kioc;j0_3p~f6Ft1<dniCJVSE~
z(xiXKxiGmd!{77GAnzsk2fjtf>n{8w{)xZsIDmiV`3nD{We!~CRGopC0-nPybzJyY
z-0s<yn|c2ynU#7B4dGK*ejZy-BF<6sB<i2Qc3O1jMQ!fj8KIfP<|@g_DoNKA`lm3s
zwPO-3n!HPROyTC#6!vwT37p{}o-H@gen1yx?xtmWXpJ@0Y#nEA;I(ffcJbmmgiXAK
zZN?$I6t6)q?YBkSD@h)^XybpQe&TADud}>;AOG&_%#RsM!|l(bLp{qGg&*KQJayIc
zv}la`E@>Sax}K^kKp*M&mCoW@DHPDQ2%SQ6A`(BK-KR4}6WpffgQw75nU8w&9KV`$
zX9Am|e0{Cn5)N*PrpR)0B0PmVCNVSaO++|8iAGr@d1bM}G^UwuiY|`QZ<kJD5q&pd
z2xVq*v})=+kp~SHr}MP)LbDRDqM3)Vo>$_{7-qEHf*o}3LG0t-ewzCL4SA4mdK=y8
zFeYgHduaT(;to833_eIFp5%S<QH)@Um%t}U`4k*HO^5y-N*bc0o`EaCT=IdT(Y136
zY?tt#1Ta9EzK$PiH(!Y_`EK5Y^S+y}XDjQu`DT2^ck_EF-;cOAs-3$B_3_JepD4ZC
zz<^Q)HW>J^e)44l(AH)=j|SC{sB4s_mRDil6Yl3INKr0|C69H-a~hG8J@LGLX-yH5
zLaIk{jbxnO(|EW?BRGln6mv--6`DfHCm7cR<2}ABbjVsEWe7o>Yh>Y3?C*)cUbDZN
zQE_)FP(8w1-`L}OM%V0p9o>G8YOd_*^(d+-YMj9Y9A~1KphfRykh}pMcq6m&y-Xo*
zVsO2g!SoiUnES93_tO*JM#LW==I_93@lK-qE~cjUU>px}_X*yT-$%;#<K6fGaeo*e
z#v}MNK7_OQ2=V_Y@%k9PhL3B^Q=Ta+m|xTgGT-2$Mo=)ns1Xck+#(34k}H<CEg=Z}
z#Nk0gWW8L}))lAe)-$<u6B}k`BX}CYnhYX^0laBR29=CJm@7xO;U`pI*4yw?zSZdi
z!w575wkwYKNrtOZR%B$5ZkUI>ad%+b((Wm|=@Z!5&6;uFlX&|S-myt4$6}(rvNt3(
zgs^r={Wy<zOUS-I5tiuHGBPFH7f`1!e<dj47t~cFr9<KKgybx1#TN+2Io=Y!NWVPK
zoc0*={sji-S?r-PMVDQar-NBhYCw>b*XZB~k-pUr34`Clex@q~<*w*F?1xy0p#B&B
zn|l2ZxtY|{oB3bg3JnOn0K>p5)pOYXEEknr7Cm!dEq}#qA*$4SjxTju2hY||YWajC
z3H);ve$MdMw!}743&`PA7fYx(rMKH_2)@BR-_#shJ*tZ2Vf>#)OUaGY=T$36bgm%Z
zUQm$#Us#anTtS|uAm6!yAkX@O@B+VpAe-k3^4$dm;dilL%OaO3$TL?EL~6W1@f?HI
zvu>?L8pOS7q51xTg7C|lzqyyx9yIOjnW`4?fc?Q0gm|7#CIP~WyU(Y<k-&c~{iER|
z!tqDW$y@LgUr*}Ski35QT>PVAE3eis(-HPVK7Yhm_%WIOgq_k)nW}z<Bt2EU-t($!
zeV>q?CB%=vu^3N#VL=l#&o#jdHGHbx$q%)@=<9sZ!@6UfYl6!)yTcXV=66Sb_sgk^
z7_dHdh#gJ)9c()1uwW_?MLg%OuQ+!-n;NmAl5Z$Jvsx%y=jfp#HIh{I;HOM~^R2!f
zP0|;%p7iq~`(KgAGq^@o#cCKe)flz~bwezbmr2f2$>7vop~5e4**k(eJ#RU|6c<0m
z6vtoIs!vR_0<+}%!{3_5=WB*%r<v5MtLG;XiC;L4Q2g;}e7R;}dKzD?3~9@077%}1
z*JF5c3cojvKb$@D?P>gpo~f#+v#Mgxs~XUz8gad9!b?<~Uu&BACA|eXwHWuRRy?TM
zYG650f#n1d5m3a_*cksD0|p)v{lyvvKSS~NA!!~Ws;*-Co!SMsAs(B;(=(=j6kDIb
z^=y&<;!&)qI)#_@f@q{Cd;VN!Zs*5cC8erP1yzW@!k=g<Q$MfL&qn=>>t~C8w(92+
T{k&Rra3AABEmh0a3M76BQWkz#

literal 6727
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
T*Ut_$kNX%GDx(&tg~<L7Hw`gc

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
GIT binary patch
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%

literal 0
HcmV?d00001

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
GIT binary patch
literal 4717
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
KZ?d}zZ~Ygq$zAmT

literal 0
HcmV?d00001

diff --git a/sp21-cs242-assignment1.iml b/sp21-cs242-assignment1.iml
index 1be8078..98ef083 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 0000000..9c4a698
--- /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 0000000..9c28204
--- /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 5d14b79..4dcf7bf 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 9840bb7..75d95c8 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 59c4676..9009612 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 a8756a2..cbf50ab 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 06d923b..2c2a08e 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.");
     }
-    
 
 
 }
-- 
GitLab