From a683f5a2b7ef1a706f6ceb39a01ce6ca9b241407 Mon Sep 17 00:00:00 2001 From: xawotihs Date: Tue, 19 Nov 2013 11:09:39 +0100 Subject: [PATCH] Some preliminary work for minmax --- .../lib/libjpeg-static-mt-debug.pdb | Bin 94208 -> 0 bytes JGE/Dependencies/lib/libjpeg-static-mt.pdb | Bin 94208 -> 0 bytes projects/mtg/include/AIHints.h | 4 + projects/mtg/include/AIMomirPlayer.h | 4 + projects/mtg/include/AIPlayer.h | 44 +++- projects/mtg/include/AIPlayerBaka.h | 16 +- projects/mtg/include/AIPlayerBakaB.h | 6 +- projects/mtg/include/AIPlayerMinMax.h | 34 +++ projects/mtg/include/AIStats.h | 4 + projects/mtg/include/GameObserver.h | 1 + projects/mtg/include/Player.h | 2 +- projects/mtg/include/TestSuiteAI.h | 2 +- projects/mtg/src/AIHints.cpp | 4 + projects/mtg/src/AIMomirPlayer.cpp | 3 + projects/mtg/src/AIPlayer.cpp | 206 +++++++++++++++++- projects/mtg/src/AIPlayerBaka.cpp | 78 +------ projects/mtg/src/AIPlayerBakaB.cpp | 5 +- projects/mtg/src/AIPlayerMinMax.cpp | 75 +++++++ projects/mtg/src/AIStats.cpp | 5 + projects/mtg/src/ActionStack.cpp | 8 +- projects/mtg/src/Credits.cpp | 4 +- projects/mtg/src/GameObserver.cpp | 23 +- projects/mtg/src/GameStateDeckViewer.cpp | 4 +- projects/mtg/src/GameStateDuel.cpp | 4 +- projects/mtg/src/GameStateMenu.cpp | 2 +- projects/mtg/src/GuiCombat.cpp | 2 +- projects/mtg/src/Player.cpp | 20 +- projects/mtg/src/Rules.cpp | 12 +- projects/mtg/src/StoryFlow.cpp | 2 +- projects/mtg/src/Tasks.cpp | 2 +- projects/mtg/src/TestSuiteAI.cpp | 11 +- projects/mtg/template.vcxproj | 2 + projects/mtg/template.vcxproj.filters | 6 + 33 files changed, 466 insertions(+), 129 deletions(-) delete mode 100644 JGE/Dependencies/lib/libjpeg-static-mt-debug.pdb delete mode 100644 JGE/Dependencies/lib/libjpeg-static-mt.pdb create mode 100644 projects/mtg/include/AIPlayerMinMax.h create mode 100644 projects/mtg/src/AIPlayerMinMax.cpp diff --git a/JGE/Dependencies/lib/libjpeg-static-mt-debug.pdb b/JGE/Dependencies/lib/libjpeg-static-mt-debug.pdb deleted file mode 100644 index 5e7dbba292e781d4715b0709d871ec142a1f9e73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 94208 zcmeHwdwg6~wf~+;3ZaBDKnev*nL>dI6;fJC3l%a=(k5wM9ep4n^fwf9@{|)A8~2-iSWCablQ1TZ z*CF`pWf;#~`-=XLBoIj;l0YPZNCJ@rA_+tih$Ik6Ad)~Nfk*<81pZDWu+u#IQuIU; zh$Ik6Ad)~Nfk*<81R@DU5{M)aNg$FyB!Nf*uWkvLm5;x=8H~7#BoIj;l0YPZNCJ@r zA_+tih$Ik6Ad)~Nfk*<81YWXc3KQGC;rzsuH<2!uG6nOS7k14y@kI9l#>_^7Q%%i* z@wqklHIdFvdQ<6Q(J&k{)iHNnGOb z&4_aq?>xmj80l&e!}68`wi$2!1!dKm^A#tpI4tV|q+wb0#_S4*k)$kqlZ0aj@*W7w zIKC|JxEa;F$29K+pl2_n?M6D%T=_@37-!60Cabs?D6ZqB5;wr9H95tZP@LmXug2_T zhM{Lllru9~D&(iOdzs1M{0Ov$b(UAWLd@!4r_d7UUlO_0o3V6hJX7$BrF1DBXNCB4(laQf|a>ct!`_85c-8~KQb&z>3U!bOm{*c~AQ^;?vQm4ypog%4+1VP`q z(v-%tMZ&zXLVkKGtOu(DoMI_IWyk~D`xPkLJx%OatMK6Zh5^t`*|;}ajvSsVVim#{)o%Jp#Eyj)r#|W#o;(Hvp)epBl)e9#q`8f zs6O7IIP5>K!S5r%$71xC$7ubup{(%O_D-a!HxrrCcz&dacFeMtJYj6w8RtdmbYyq*PwikNlqszhwBj^@E)Dv#msj8(N}^yoX?C_!M*vF;7;W+oXjlu<1f!^c9O$b2$gmD ziI>My*%mTs4#cWAx4k@`8^S4LVv6qEu6TRUx9sGWOre;;m|3zopMH6q>8XnLeTU-g zp$=_lOX^r)X#V`!m*>smUO1heEM<2%-tJVqch%YX$x+gWWD0K1do;qe=tCx;*BsxE zMYzsP74l+5q70hWGIls?kWFq3`Im;C`l9|jq`J_<(O1CZ< zOHWLsEnP{#F%Ob?8YV8nkQ?V1mokO)RuLZOdKPCDXlgM~DbM!d;q+~0B=3!;Cx^Ft zsz=$$v5?%`89$WE4{!F06Zt#_SVW^8kam{WVVojZhB-bk?ncNT^Q)}PM<>t*LV5O@ z($>6PQ#oWA^C|NI%*l$`0$8(4ATQ6}0+Li6VVN6EW->hlAug5mmgEtpX_Je`j5kx5 zgksnvj^?vn{7EzMsVC<+xm!gMeh5F`!BtV1A88&5)ptl+d_5X>c%ev#o5S37m{rBu zOhvv^m?xK}LH-O;SQ*=Sh)8E5J!R{fbZ&FyaVeS0PCD6>){Bkp3d5^R(n7qQ8kg;A z9+a7t)-&<02L=j@uEPmbg=0viGhYoB2+J0jr>!fk>J z#Ibx~yXMLAZx(niyIK1W!aiXphP>hF0>;^GPR1qljkhB$`fDuIN;%Ib+sc{ocW69& zEkD}TA>!}U_=)`FjQBCoc$ba0Z4v43HpATWSQ;d@Wi3S9eH!QJ%L4x^W=aqojO+P_ z0n1T4l@7wb0oZg1HUZeTP`0fxFplw(V;9$-lZbQAAr zH{DbwJCLpxB@8g zdTUILxkTuTCoYw@F>aj_PVe-GeJrWmsW@*`9IlXmM@JH=iDzXv%e+(4#}n_Wg2%io@vPitnd=nq z-HJD={r6=E84jI@=f4^7dWBmVSoqa|Con!- zIF0QkFH|jB&b5edD34ck+x8Ac{5-^`bC@Svd@0qmRUfaie5T7iI z*X%XEAMuOq-rZC_pOgN8WiB?wOl}mjW$hl>hp;bQg1zuB-IDkz?zAh`Sznx@Woe5jGRMF1}pb`TypepKXczIqwV6I?v6~Cph8-W5tSk zcQ(+`)xQo4_pa9d{?t0zYt;37YhRz2>chewu>WYyWz*Wa6J_W7P2#CL?F z$=kj`TxVvH zw@Xec_e40ZXI&p^%rEDF@;I-*xv#xq>LaV7K!78W-2CRpFyr9i4CETT;zldv{CgMyJ<@2k^5}or6Bl z3l!c4jSQzhLg8mtz>^l<+SNO-$!qUos}hDc|AtqeJNHIYroZ(2J<`C#!EYChE{t$klEVK zo()=Oi$n2U?NGSxb!ZmYn$vP$YvYAWZ=k2gThqSI#T}<{-MxbYo=C&W;rKFm&svv$ ziG`z>uGBztTWgDV<{)(<+}>+5ZB$~_4l|oygGmncJ~kV_V)BqHASwkcX91A^E=(e^+WxT-&*DS3=1C+g>FW( zKog++n!<2tEmogXVR(OQ>w3*+O&D$)fY8*eaA;;{`@jISUsOioT5KG~r|zB&-CkF! zZ+&Z@=GSWDTtBvEaGm9Gt%YxBZ^5`&(N@;kxQ$(*_T3i1%G!6kk#V=Vr*~6d`?|IP zSJxdDUN(lTHys0g&EC=#YudFj(8J$OHyzP8&mJ(_$xhyE`(NVvH#%ix@uVa{R`LQ_0(fV|0zJzsR`xW3`}`aznmLDOBMJn-z# z7;d82IW5!I8gF<~_XZhXXS|`b&IK7?Z@kf5dd%(vAe=y$9Ft$hHvoS;U7+OrJ+67k zV|uih-Jx<}zVnS&nwT1$#A;aNUkBWSL7#h)CXj#ltjF<2IntBkJm4Hzwf<1=p%WU` z_hHI^pYqT1n($c`^Ts~#q%ef~a&?#t)uF(EwaC~tE4CmdIccCc=ZAP$%(@NH;Odln z*}JRL1wvyyafH(9c)tO(R`R|OX>L^)=eS4>iDJ0qND0hRDB(DYuW=T@20Mc7SDt1!x$ z!#$s{HgGe}C@C){qrBQwUM}3CVb(3;SH zoL+3r7dGSIN>zipy9RktE?blb?n`60nR|`)fJAmlt^8TnwT6IV$Vke@k)%2^mZ`K$ zqMWP_dAZDJNpyzoBcV1@q?bIkgm`M8&WRqE)9cz-Gtx3S^X4DhR}ybhw-yoPYn{9U zd%iFZ+k(U6oA!L|ir3-e+5Uz0Tc*N(i}er#v=eFFlVF>W!#LYmPEdK;B-P~tS)y%O zxlj)5qq;+R*75RjZXXfnAE%Fw1E<9 zCK=>qzA^}UR+?>^iH^lc+k`PXJ3L-C*K5J}Y`4&D{jeP}-!?zrqK691y1i4zbs9G< zuw_!t`v87cNDjRmN2=I?I9-*o0AnWeBU*SY*a!f1b(6dqS2H(>G?d*O$gcXCo|Gq+ zE^H#s?%Xu%(Dr3eq8H=wj1I4AP#IC3whau#R60Nuk(;EY#bGy>g}Dh za|7mij>%;v$4cW~E;|8(L?TJOc)&ToJWdcP^ADq(8}0Zk<~kgoU0r-c`xf#=IQi$< z%=yUAJvUoDm6tdr&VmCWCalmA$pSaMnUH zw;I!Y-Tp6ahr+UD8TD1{pOb(m%yf}G@(JTa*_?!1t8 zIAM9@5npenr_>5V>YwvZnO9cKKV@E7G5?%bW&SxoH28OUjMoKXI3bfD!OWciHAoB72KWFJY81a#@xx=nPUup=L#=sA zdDz=e>(+~L^BCkxdGF)L)y(zh47i`*Wa{h$IPYVh9?riWxOXqd?B`Hpo@s*3%&0NL z8;m()K7NnD=4mB%T#hs5CtWyAUVyeW0Q;BIF~>dwx`^NLQ!$gg$e8b=Asmc+F6%bt z1IXw8*TZJ^6l0!v4eUM^8FSt7#(WR&Wt)xJ|72rcT!A(43HaS%%nOsqD+fKq!28Vt zY%9(-<`+kxUqrcWNn`%9!$eUEyup}z8;v;|ICY30c^zO#^ZrYXd3L=q zZ#p0OUjX~6jVR+Zd>aGh9fWc(#Q|R}%KE`}WB%N4%))bxIT`tG055N7G3M)_za0l_ zOOf`Bt+a%q15Z7%+nt&_6p4 z9%_yGE@*tV%b1;6V_uK4JHYRufc+79&xI@sN8>~n=^h3jt5L>(K(3!kL)MVziv2Mk z0zF%i_D7K8`gUVZLFIKGWz3`F#@qtjGr;2}$oHuf;Kx9YsKcGZSTBJ74}iYk;_q(I zvK0J2jB-xhfczo5iy)6bAm76`VGKMUVWfF+9q5LF-t}6HE0-Db*?o;U9c2|z=4bIY z4F$jQ9PkN!c)A;VH{jzZON{x@V&K+8=8(lp$o~lZ{a^6#?@0F)%IJi=-gt>IYfv{0 ztDsvk$h{fs_w%4{Td;P-`yD9jC8QaJ4t+Oo%#RZ&6V2>S)c>4TW1d6VTjv?``PsPJ zg8CUc0`-XcI08J)0k1nwgnleE=3MYo3wgW~vVC9(dW$lb0q+l}m+V=nH_-iI=*cSZ z_Z`r<4su=t*{p^vp9btk)Wh|V&y%3Gz9(*FYdpN+KdJsq+`{Jtpf zeaQF3Gr?a8`qF~Bf_!UMU`}{2^bLQ%NI~DmkXIda1A6)nr1>LwT?swfg*rayTx0Hm zJO?5Bk3(+XMf!foIud?1UdXSWb%g%xW5GY_6L1$11&dCLgx_wYtZv3>iA^H z@GjKVtuf#spR++%9q=}w+&uL9DwMMTbUZlKY_k?U2M#Q(BbQWw;D2D3%=%{ zZa)KhT9Ni{$nc;}W4;I;GN_+Fy#ezS@Vh@~{vl|Jp^S^5R~sSc_*!G$yaM!qj$4r5 zouL2si=l(ivtyt;e>fJh1P^_P?}J_>081gyd!W+~B9D)w?%xL4{B{ZOS|KCIeA^tf zs~Yr0klQJs;m^SRF=%=O^|%qVu0Iqy2Acj8vi#R!$iB;%FN4o7q5dgs}_;*L64i*|y zgq$`&&o7^V-n}MILpxgv|$B~GB*Q$A!v9QX;LWf-w^-nBaOKRd}bP< zdno^BsL%dWp=;2cFAku+Y)9JwpP#IUUO^TfqkI8__=B}WCb}~w%qnD z7Xxo2%DNe4+ymNrk>)a#)d^hhD1`Bw{meuY*5jb_A=JlV;Pv{|s3YVtAFvy?Vcr7X zZkd9dPel9nAT!A3wZQuW^z4`O@f&n}0Cau`>DMEDANcG5tZoDHJ^*79{_aIP``kFn z8A4mz8)F#CJf;zRT3ER@QE^h3qGkMO{YOGcOcIbY#xV$mN!B7m!thm zAg>f?x)|xsY(Tp~+7E%}n~?7y>S#V_c_Z|1Z>2kZ%5&G z3utHqUpFAlIMT1kf-lr@3*>k)7 z;ZD?P3UV0-&Bub4CeX>b#|MG)1k!S&l+O)#H!Xr3QO4pXdoD5me8xmiB!Nf*kp%wt zBv3@3+l4V@^eBvvz39`9##n{9!ZYYUccBma`o8GK7Nh@LfcXm24x`VD0XF}5*rzsO z9KksHKJ?SMV=;G(;e8%#>$@@kK>zpw^s(Vqarw4BtcUi;IJ^>b zY?SGpgmDi2?uS-mK813*4*W-y`EK;9ljuwDLr4Da7i0bnI$sN#a&xiX0gU6|-~h&O z(9_h6@w*-Qg8rYq0b>BhiJzm-Uv>fZ{xDWu4W539a-IdBcQ#_~&2E%-fs8RVD2oM01pUW<7vWXC;)-_7O4uHb4f=TQQ!2<`=;G z!{DJEIY!*^YU|GVnc$aS*g{ZSW}S@yDpAI}ksZKs`bQ8!yHB6Fi;cVZL>QF<;=bRBRfu_Q$u+yK7b0h+cT@45o!4=8Vc&@u?z?Mopi z)P1!6kJkUu`afF#N9+G+{U5FWqxJv)59|L&FfaNT=4=OJf8!J9VScs(`_9-eSciG! zb(m`%jd|n6nAfewd~FBjq@0Iu!+fd(bE_vYU+co$^NnX<4;Aykk6|wRPR!RD_qJ*7 z!(4SK=DAnlefn(R3}Q~U82d;U0S9G1jrrxxOK=`=4EAOFum^%U<^IUyWTa1TFyd|9N21f-3rMx}tnu&TbSbR2x6v?7w;x`*oM-!l^;j70KiNAC zy&A?X1c^UW!)I!^Ps4rfHoo76TXD~!xwEI=FMA+>_uvwjls~92Ty&AV&$3}&qQPwj zdxvF%g>|fLxA*!s24P+@9B3QtTGO3s@01&3%EP8mn%0d2t=)1J&hmG5D9%TQjRJXp zgN?%-qh@#EL*VCFcyFq&s}ncY%HqzoadQ6#7sJGF5%W4P1oxJC-S#%2<4W$8=XI9&bX<&sUfKsVsu@f*^(eyx{b8}9Gw=?PuZwE2u^94^`je;Ge4 zcb?>arPGH|7pHYJ=ED6Jcia!p#LWhAkX@?F-4nXN!u4K{Xo=9VISdyqvGnE?&bszG zdRy0NeNBYny{XRDf#8nZWH_#G-5SSF-oo9zp7s{yW6HvLKc`G@FAU(m-WE*9hQ4l3 z+LpI*ZBHNU+gRsC3t!W-Zm{3$?F(*UYTcG>JXEqT)!o{IM8SQdX&cvuTRdfVjkYM9 zcU7V95OQ$8ZmW%B+W=BWkGr3=P2rG~*NRFeZQCuZYp{O+cLV!3b!+?HVd1y~=k>Rq zIf$FJ?Ws*o7*h4%FD&}E>dTze*kxcg(~sabqRXK68_$g{ihDxRg;Uu zmTIw`VH~DFoNDZHw#9^D7ZPS@Vn0mx4`QoC8(Y{iIsZa1TOG;Jp2*oXHJE@6ME>qsWbZj`nvh;#?Xt*viBpZGbV+Y+CK zJ<(TZ#^I*`#&x6cQ>+=UmRH!YhhW&^1HWRUCN|jg zNaSql8J9HHc}^(O&wr8O)oHMvgQX#^C^;K-;=xYe_dhYBo}mI8xNw|0=YiZ9p9F4W zsP5F<-Y;{x+@lF>>d4bdftBY&_-cFS#)cC8^Yd@9w%Kt0qcI^o_?n^L zP5MF9!Ys}758LVLfHvEzYrLz}i#jOrY73yW+ryOF+!)fI0sk(&b%q5w!_%j|W06E! z>ihxF`LNxVfxXq~#_HHjl9yQd3lFsMKS1sw1inF;&jGO_y?}Yp9I9OesCqiA&hH zKkDpf7fL)GP||vH6xW%=ZL!T0akbW#R<4{(tG_4W)S1!jWVSeNlLz_rG+4FwoXF=*FLzh4-ho~MIX#iOX+KG1x+0ulUbh$&W4gU ze7E5*u-%KBPa;hFzuVysgft}uD{elAFnPLD-jIdT zv_SHnNz>;6uZ52@UdQn!hNme{(N(}Jb%kx6wfP0fOA3UIve4=72RS#yyH0u;S!|GU ztIOf8Q2xFgs@xw4*O&8sdIfMtD~NZ51G&Y`gMhIvACfoYYUYv- zjx9+YTc$MJi|}Du*N-4QP79?!{+j$Q!k_tGQm0=>xYw+np5!g$k=LQqJzdJ!5AfM2 zpXEKu-^gKy`W3Qrgzrl9z?p4X@fS;3(ccc*74LjZz-14+c99{|*E#=pE=Jqj!-%V= zzD=Y9ER7=`{R`oSu4hZL{&ZLt`ED>1#WCCk#icqa=Nm|m`$vFbfKWfyOgC=eFbrO5 zbD1p}caMklniKz(Q1nUMv{3(vk0FmKTQ5p!FxI+qOLqQXoy#htj3e$glO7r26?+R; zWZS=xmWweK6)^cIOQrJ+#_zOwmq$Y$)So$s|0c?C&lTDaNk!Rb}KqxS`IfkJ$wm-$pjA5+z* zx*e}ET*Jd@;1R&q@8c+=9{M#^+U`4Qw->R4{u}L%cU;SG*@xqZ+dL8CPupH7op?pq z&7BO~D!UU$;#n1Rp7m#c)tnZF^$8U)b`kc_%Q#!M!1f?YdLaj zf4d8?dibQ>k}dlFWPc0Te5M=C<5sr6-8-Qh;5U&9#r?O&HEG*oU&*vwLs3?M`(by-hdRl0Y)c|*gmE%r{(`)y zBQMFDc-3^P>~6n5r({`s zD_(Uv%kLLc7NpJ9Kf}~@7rs~Zp%c7^^?~>I>tEshee$^2-UZhS{^G7##$bLoVhQ&v zs@&~o-0I!j?dNwT;18L8W&KY}P|xi5CV00$fKz@rPBZnuaZj%N&b}`{^3VDgUD5kn zQ!WhMIZov~I(uATiq98X!Vxc@ip{GV`cp42<`q=il5VBy^|w8&J_V*TmRnJ0BRDam4H9>S#a zPse@!0op&F8)cbgaU|{mNp%D*Kh!YCgwldg?+F`B3*3 zR`gp|H|i26NjnU_NpwO*S)`q5%4x1Kjat^yDrJE^83Tz&9a^h8WXIO(->bnIo!_zX zzgNS(SNL--`#ufzg>pF=Wx5AG zmnuJ}0~YSXSRbP6f!iMGlL)W(@AC(K0+N8kKf`o!DubP6>$imaCHhS&@ygCVh&~A_ zeOPh-cy&k)@@M zZFq$XKVZYBxbRmH?&!6?9KUMgR=KzbZFp%T#$D3#kcO9P_z?{+)9}|cyj;Uy*YL?2 zz7OFR91t?Ehxr?ahy48w{O;=?@OggJhJBtNvtgg-Z`!cW^S5l+Kb!ov4f`_qPaF1S z@EsfWW$-w{^rA;vp1|)`{r#lC?fK$U_}zlTOs4rRe|LAv@2B}2hnj?ckH6b`<@fjT z8%`JH_cQnn2L?WWKd@n+zaQGL&)<)1*yr!ZHth5F6C3vV`!5^z`Fqxeeg1xG!z*3A z{>+B`dVbD^{d)enhFv}XLc=F%J^oU|uHJv8VOQ_ZYuMHMuMvih`TYN18(!-0|AH`` zKTw7-{2pkvzfBA>?CX>BD(+*RjJCu+<8-tq9`CsmFgHf959IyHpwEt*CWP5Xq~yH^ zzYOKxx}{%iRvfohZmH6b%-1sl>%a)R`B+wRqulL?;Ui$!E_3@VjQgrd!_WITre%O?KFg8Mg9^t? zPR<6H7vWC^@WF8U=M;_^-g*aTfBmBK9TkXPXl2U_v313Y(LBWx*#F6i&n0#0u=Gd_RZ5i(WZ3K^B0Ng#d z$7FlcdbN?Q!oWZH|Mp(i^`H~qY?$G1@oDG)_9~_)uw|pWTr%h6Ue;Y9-dbK}FN;1o z*f$4#a@>3oFgG4Cp7X?T{M~?YJi15T?5hL+9&!$XwnM$)tMU%c)E<<4 zXPz5bddhV#DP{BI9}U0CcPbu|yhtJE=R%{)K=hvUBqcVW=MkkRTo-QdkZTj}9e!QX z#1r4Bl9p{b$T#qZ@=ZZW_8E^N z4(pK3S@w-whRLqH9n-#rIDSK+xP21alzDt=Mr1F29?Qaaa>b{ke1{KZaehL5|2E>> zqu(tFd12-Gap1Rx<3HEOqMJYp^kA0x4)UQMJT7m>)y#bYzuj8Q&7pRu7f&K>E#$$W zI8$++_!Q#Yv&7cFODKKXd0H1X@9??59x2GtypJC@7u5ElDcwArKrgT0d`9Rf+aF%4N(yymlJ;$2i z8c*pX{dIbNZqo>f(qA)#YkgTn1U37kM@adrTF>g<2X8~a<(brd7M zmZ;wbujGyWqq251v+sUB#EY9KxN+py zA-dd|pb*cQLFn6vUnnaBy%f%DwbIlbp>3yZBWw$BMr)(>+?D+!&SMDIE>7cBf0B?3 z_ouhYiawa{@24>OjAwnZUiDkfE}yb9HQ3T+(Lf5=@A1TAskR69nJ~J`QOrEU9w&M0deke=buo;8Q&YNY%eqCE4AiN!ecz~-|`N0YQZccpN4! z&#d+WPB@Kg|IEjwsY4pl8QuSn?*9*>Z;I~!NB;jJ|9|isg~@K@|1a|YcT{8K|1a|Y z7y18-{Qv#+{{LQ$_y4hAlxKH74WoPiRzA`Hkp%u8B(U#;OJi{J!Hpn8U+uM*^Gh}_ zy~KUSnw^Zm-Awq4;~(s)%tpNV(01!Wyq(<}@wvavGX-OI;#c?yzB2DV9n8J2xZ=3I z%7rMD`x7z1Yw+jpwAj7$S*BLf!!}m&xFxZL~L9qz=?FmGh)2C}sg z-^a(Zd%blC(1%sOPYA%a82uIDL;D+?wBMsU3GmFUW02w z*ykgkN4`9ce(>AgEh5gMm&b92F>+~yIL9c?9_nGnT@&IhRy>}w&`t^`tcpScpS>tk6C;6W7qb4Xl6Tdx>SC@nLI2}9;(|mm){j;`V%xg&k|3>Z=NOMC3l%; zmK)qLSZF!6XFgY@w? z^-+HBWyhtX5QZn;g0ta28exn{8D~FsEW)^>si>dzJKEKM!Xm&Sr^Y5xx;SfeW7MT*2ZKo>aIxR`Ac{7U4{V zrVUhH@uH#Ue#ay4#fU=$k3{hireWWnFopaY6>ls(F_8|@xpFtQjBG=z#kg_abEy)y z9C5fKDz=lAHj-}%;AJ+F@2z4ZNj`5DKJ9To`8x%9f5HUa8(~SLyWJFUZYpQ*FiQ*c z;8U7!*oKqoKW*|;C49PVM+UVrLQ}*5=QF0dog(SE!xSg-`O-MQTc$2gL>{X^*PY@& zL%-~$^+A2Pi*^y>HNZEq6&&VwcPLIw0Yh}$XQW@SR-pESgko!v1m68-&o-c6*7)7o zfIa|SuVw@K6*JQY^g%Pz2J~U2t;`BE#Pc`o-6>X7E<0)cWRUJ}`E9t|-j=d>9JUs_ zv-SM0#LcwzqYmCXpZg={oi z5$>$8zg!1cZ-pIYJ75E`VAycBn(G1I7~_RUn|~L==Tz8kb^~^PtTfS^nIA;QSZ4fm9-z%ajqV@c7>lBySQ^>4`8%Y z!8JkLQ?&KVyS(*i^A70l)eWN@_arKOERc>7rQ>|i!SCnIocrczVtwu-M=8YgTGh3ruR9SltE`&cCYqxr!&YBQM%e zxbJ~{9I^@fk9{lXsH@^vjP@<{rrd8n`GC)+66|-}N2J7lhj7qIi#D3U!6?ewh!8$c zeP$EpWOq@Bzxnh_R)uzFmqULNv{NKK&c4Rl($|_xgtvI&Qst5MC70nh?ObS2e4EPu zjtZXX+m_!kq46a+6dSsoz*`Rc($aW-#QFZC9nm>H{rvnwz}NZoty`f>r-8R*7SSfUIb&9hB$oQs%fs$G?eia;FR%K zAY)gC+;OQ=#@iMD8pU__9cbUna;%+i%v_6j=KUz(%=@?MBd| zfHY`(Gw5bH0bhcM|DZFW8y@8CgTS};YBh7o7i9`v!~KJ>Typ6G?3<@5K9wMRDL`W2JAo261gRZm2R2 z*};Fl_1h`tgdS=Yx03K}B5BjCuU_?Oi^d9no5SPNlbgZ2^%wUMW}weNh0L4|kl+8Q z4tz9J&hB*JW0GdiI&d@6BvgLkI;uV<45f!RvpX*0drura3e&XBT%g}GVt)!f$xJw} zks)1%uPXIHDiZf)3ExcO2bGd!Cr=wf${61ZNpDLJ592^b$pK0OOFI*M= z_%ToHZ>?2yIPdxT?VxFRD<74iC9um*!ge_0?Z_1LyOBxczYxBz3Z?Br2g662A+PA= z;qBb^-PE;3(x=<-$05_jl14sWC}R{pb8c=4)!8<4L0CEULP;RYH~m$zwEkRGl)fDP zo@B|k`ATGYYZX~)WAF?6?|E}qsJ+Y%JNE+adtaz@LePqgpcoOdh&Fg{wA%@ z;5TIf6_9xueW|+eW3GgGef8X|3$rkt6vIX&jN;&)-dCfE9aIB86 zLZ&DDv2s4f#mq=iMpPu0F^TnxZ(hxi&!P(XFkkBFV&2)qx=Ng+L(0@Mu4#UOGTpPJ zLT=UX0Sx8&-D~pxE9Lzf<=wV};YN6L7up#P-~XS3`~OQWpLzd(B^nn079;)n(fxn9 z0}|c;uaoZ{u@N4E_DOx4C-0ZJZ(DKyhq1A$SAu1Z|8VPh$_1O zU*>H<=62Ej|3E*Y`~T7X|7zcAithiHe@CeubK~g#e>}nc3A?Vh`!v!0fBQ|3zxVt9 zJlkizR`*3!&F5X@|IclbVy`pu{|DW1d$;Tpk`i`%3~>|r|BKfDk^jHo+<<$x-1Eks zY~=rMOuzr+MgIS+T@cO^^qeO0|A(`S$p2sD{}1Qk;^{B){|DcIk^euQyGH*10(%MC z&2UZ;*jhyX|7dIQYWM%gb$c)V*5PjiXa9T-#UIc4VGe-r!OENyHbr9hYWBgK_PJc^ z(*|gN{4tL?_@lqT=!ql{Ng$FyB!Nf*kpvP?a z;*5#|iUW#>ieBZS*8%+;P*G8F#2LM!pooeB&Vus&{(E@e_ar9;@8@$r^?A?pBFkkFP)Gm|Sbu+T6*%TXHDR-#^QVFCINF|U;AeBHWfm8yi z1X2m45=bSGN+6ZM<0Hk1{^Hcwr1|Y54E-q&RI*FAuJ zNShRlsj1a(-43YbdvBAi-Oqk2&B5Ns-2uS}wo0JDKF!0<^RNd1TTM!U74Cpv-^Ns% z1ALyR`#k%TNBEL?;xektfgb)K55KRen!i8rhdXrek_cxFd}sEbKZ>_OM5JSk=)z)XCn!?G5c2z9qvrPsS|qd5-mYk~H-LtH1Jcs?G7hChI&mjgDp0 z(*LWh%&a-lm$}@RnUw1Quv65-6gX9O_#M-{r&;0ipX~FmFjf2SGr!85MA(?pWbede ze!RC-l8!5WCGpi}yjUW7qS#+3mGT3xUNnkkLom82h#UgMp<))g~`^gj$i|xSWnjYv`=`xipblTG0`|YHa<0( z@7pn1pf8XNRUg$x5hW7yeN%&j2*ssI9yMloY7t zkydL;7Y>j6G+QGMUp;B_%tWEIW2`?vS1pD2v?P89Nk#ff}zYEpG#6+4!DHS!%B zE)A6>d%e$55cPt#PmPZ5a5<0mde@Sm!|UBZp}#mfo*(TkAztabWTLj8)V((HN%wB{ zbhcYOsCH>;wBV{Ts;6qzVIe3}d0Pr~S>xNQQ#i|<)i>3%bjR|RNL0QY<=2{_-co*a zYGiVlww5pT_l`yFUFrLoLU9<)>FpmX3?P5;y$A6f3BKnA`Ae<|^0s*1eigNcxGh(k z(cXy*oqR-#+C_~iP7H4z9_t;+j}*o>PY(HZp|rV1id`O`L+@&Bys>m?Eb5#|mn$e= znw*$IGpG;g*YL6 zx8~9pY(p^0H`+UHBtu9yXWMf4!Kc>AupIeB`50^abYN$WC3DOM%R{zyqy0AKiQ3k= z30|l&n%HF2k3T0tm+!|NUD9j|>gw&IWo^vqs24U0GZ6J_(rxK*?*Qzp(o0Sc)UVaJ zc;7YobYJf|c%5rXz1#9#?ahg}eSF+nAE&le>-yV(XKZTJl_A}kX9k8*?1=#e58LyS zPj#Ta?)(I=3tSzPvtZkAJXcU1#O*n2^5mC3ZL;6)_ZxtbZoQyPK2Dz!P0+SaCmY0S z+r90)qjnqGbh5`%Tj`}N+Jo(8VAzkst}p8&tri_E4CZ?$CVF?2;8o+Z+D1QV;p5w^ zMpHl&jTU~V&s}ED9jZ;i@|&$4wBND5%{F(~3>1>sooJg z772EN>;Hzw1`6AgGFzzealChcHfCYp1#Hf=*?vPhD7_WVrG{x6m&X&}ly2b!2HbN! z-19t~bjX-pHZ}g&*V}*L@YrU?ry?^HKT6rUeZJ2Z=356q>kEieIhv=I&sFD`7y9yE zq`o@>^h)l=8jo$p@LA{gmuj(C*; znPp+=?diVE^DK_O;YmNkqXmy+(8Gp05Ul!-@;az8LmqzE z!zXbIKBg~gF7&V?9u_`obHHSfh3HmaFr7Z7Cvl7e6UN6tlh%9XbTQi@PW7ZZ(%%B= zsd5{e?A_c_pKoq(^*BX7!7l>7)7RrTGBjqS*x&1PME&Mr#L<>~r0-ve(|l^s?M%5o zm$mj55voJ=DM_9r>uN)84c_Cuo5MO; z@ii`fWTcl)rhnY_5tHDYtN5Ybu>s$2Y$a}y;)dOLwS#bz%}d73#fcq>G3zE9KQerw z--{4U?=ho&`Ti+Q=%aB^WBV<@A{R!u$q|-Kp6s}OKH$@tpvKAYZpTOR-|Evx8S0|! zKK&AC{FqC3d;x!(>7U#&URV|bJ6zI_+kIN-e=jBf9cJ8S2yxQgJBb_7(C^Dp+}DVk zO2l1G+&3xP)fkL3&(XY6W6&D${qBE=oNx4LK|e%~$_;enfR1H2$6ROOv$b!okWaX$=W}wKW3Ko4-sSte0d`*D2yt?_!tBIfM{b`uT1RCrW^Ohz$hyvVVJb`g-4gb$w&zC+qYM+;SDh-kxv0{X z+B7H2{-LR{3*k}Y-#(Y#-0w9wByL~zxxSqrY~h$zdifkmnEkTCs1F}TxJGN~ z&076C8Ebwzm$`GiVs*01QDCjzyNtFAiUF_M^gOe9q}a!f8{G<<)Y1Ol+Y_|+)^wt&Lq>b?X{6jwn83);D%`=Ic6}yPHVuZf@@sv2NrLk>Yca!INZX&*UP5$i0&aUS6HpkaKaawDAPu>zKT>HlH z9qsKcb`GidrzYY%8|#}bCSNDD$+Y%$ozz%V&VJ=-`L6oT=8kSh!~StvR~_}(+_uij z?`etn`ntBVJo6H1-F4lOeC8+PJA0^(SndbJX=gWgcK6h^9|q0-Cg=|0`neVlKB zi-!cT^>lSKwz+yg(8t#|cdl5vEYd=&O9%P1NLT9Hd)hp|&j@L4zAB=uPDdBI^oBa7 z?rjZ?8-mUs9LJws*U}U5e29;4LL-y#hx+)lrp4!6d}C`z_r`p4t6G(%`LGb*mG5k< zb37g%$DKo4i^_bak8f|zH*V-?tnY4g@QYmh+UAxp6v(!!^~?(5Cms}Ayzyd{&QJa&>lxmMR%rRq(8FUczdjN#FZTJue%`6m zX#Wv)$o`x?$I`Z->RmM0J>8OKLYs?fhe02fyE7R;?T&2}zmhc~yT#JC%@f6`Xx%Bk zR>gVP5Orr|nxnroA)nQfPX!*5G7??Fr@v%fzs$?%<(^mVhs=~wKa;+R;jJv3Ci_Ng z`@7o1OHQx!ze9P_yPPlURiq`$d$onl*1pE)sk~Mh*QQ)P93&LRw6QT^%YCiSC;d{n z(l3g$dwwzi3gHPqmtQ-r&P3N-v$>voPeT$&v2FWI`wk>8)2`6DK~q_y=V`OR~4 z;@C{V*DYkwmaSofGek(#`9c>T?KwKx^lKx)mZ@_A_FXs|&kywH<9$fg({kqM5_o>h z!HR~Jrm=lZXHQ$uE5)rcYwXw|J=5OUYQr99Fs1Oc2(E!OgIz6G(9Nb<&I_-FxfagW zqT2WK>(>*19=eaoSAKkwQwhpZT}s?fL)ox09`$K!|K z#6!9MvexnN*L!&Bh2W$IrrPPncnGr>e6;U}ZUEkx0(#aTwb8dJu3B-WVezB3kR|Fy z3oCd&bIRL77~e$S%=T4f-a)+hd8hp@=SOuVT4VmJ%)31NyFGm9&jbD#Hf!GFVc$Cq zw{&a?e`*gge3ki64}VL9yJ~)p@+B)k?Owy}^l2I&!`fT8#Mgh3N8dUAVw`i#2RzOX zCU6Rel|S+ptnz6W^iGRATYIaA4YCVm(}3iM6S8|SGupYwS> zpP=sn@L757t#Pta|En{+avK`y)E7ODQ_*S7I}K}XIwyOqt8m7iZRCsN-nvI|S(7|H zm+jJ?Hm7*J6MX$PdtQ5}X3V#h6_(%ljP%RE)fyG!;KKMe&&4rk>Z7-g$35(-ijyp< zm80PM50$sj*d5CVUcBq9>MP(6pPZZLOtT+~=X^_njq5*azD8KG`MUky&D^DYH`40< zT|Q5+6*+wx-)gH1U0)sSD%jR8OsLlm^OkRcBYZ6F)5;O8;$3Gu;r!)0zTC^ayjNBz zmw5)8>(LZ48k6Sg{MgPK?8MZ@=fKVx_Ec%Sz`3&9&zG&Fub)u{Q#yNyA&h_%qT*TW6TIxTKoz6TiT zYEER(liX|R%hulK=?s0h;F9CAgRe3_^YHh3_%MzMUiu&7&YA~2>@PfQ7<&XOn+?Hh zuBX2^-yHKR3!klh(0-SXSs|as!18f$j`@wx_mI!0F+y~!59YkijtMGL;cDg(Yki#n z&ZqJ`4_h7?wnp0Rfj9^VO$73k$&;525D+w*MluinxWx z81v%Q#+-htF|QxvRQFlXo#Fce&JNp*xvasM>!^%3)^WDiV$3iFsD#804=zQQg z#(eTP=-Y10iCc`>#P_|xJ_x@1;pdkq#l?-rylD|U9c;{p;CTl!ymcw_A@XJ6q3(QR zUOvJ-1%zHr{_j)%7HItTnb=+szXm#U@XeX2IRzZI7L8c}E%Po07Y`(Sh(`4MO~yP% z`5zv_etRQtC7i^ZeH5G6x!^=L&pyMLN8l}stbW?c4hyn8{~5--2suBn5**MgT7SO4 zm`@#LOh>;l%g8gBHRg;yW6s(D>_Nty49(XK^`Bt+?e5~amrG|digSAj(sY9b35m;$YCq6--Mox$ChD+;d^@47Ncmr)jt-%Iw!z=CDgVr7>~rAf6{E(y zhI;)E%KuZ=n2qS^4-Y_g(6F6+w?kju3FsZ)r-OeUytMOc_BH0a zl)L<7WBz-X{s6vDBW?(oJJ6Lgkl~@wb|q#07M^pR@OK9CM<(|iV$6Sn`(yC*vLoRG zoUfvs+xIf&s)NW2{^O{d4P?AExgAV;^H)x5Ah|EH$eWt6)KTK4WS<}>i{-NVUS z!~QAse!h*iaVmO%%)g5c+ZoH-S%e_j}+Tg1%nreIxQ8f|iqz<8Pt=smSj) zWAt0d|APl2JLK>!^yNyvZ>3z*&iFvPKM@&Rn?YZo?KkN9Gmzh{$mvmNDx6M!h>RYEuLC=2ljzd->x|iquKpB0?x;5A(^+&5*uS-sRu4Ut z@jAk*!TU~l-1lZS>#B=MmEX1K9of`v|f-9zEYu zM0cJI&a>$o&xB6!-wI!^cnWJ^e=9yADJCl+|=3{S|mWNgXbx zyq7i7zKEL-j~_UiI^T*OQHE%HXpptz<d*GIVnfo9|d2?XCw7^7x7o1U-zC&`<+Af0Ct(7o?J9lho6c{Q{#aO^`|*B;>8#Q3`& zd2VFx*hLxC@Ar_)yi;iJgbzhF!`g%Ic+y*- z;V#N)1;<73{vG7EjO>ok;umBf9jZOOZRYZaRoMByL9! zdb&6I0zDsxPL=(3w~P%l^W_r7_^4!m!nyo2HAe(-%A89aJ0 z{U~^s%mx4H)C2gh0LLpy|MPa*2Xvf_TrOQnztcdwS`Odv^;hU$g51?+2ax?u=+Zjs zYS%Du@bjl_)EDp*M?f2LIRf5#It>a%-$7fKe(VAKjg!o&sLTBhBaQZ=&rN(+(+}v5 zuUXApWq;~n6MD<{!d=9rpHu>=1pehD@VDRm?KgB$Mt|;~XQ8Fzv_1KGyzHq(Y`j{+SuCOxlwkVN;}!5IYiOzs4h#_%4FKw#&i6gC7X6iGOe|)ZDUCEcu#fd zT@={R!?mFL#66#9$7yZt`0xpLcvi*nJ#Ae*9UbkRx|(9?JT0U(&%}3nNbBPI3Hapy zN4%Zk;=ApImU^zma0N#3tCR8ewv2I+wPgHF3YE zDUK_<-_z{w-PN~uZ0u}a*VG;AdcBMH7nfX_XPWiho%Q);E7mmQM?#ljT-~?0__~I6 zXHzY?wfeY@j-^(PZBcmH@=!**i{t*2!{6bs#8p zqPsxdaeOug25U1e$KyENPWxER(I z+@+{zFzuW zxU03jJ#k6X;TiC0G&IX!Aqv|*&)%;Ld<=#(Uq_omxXa}oio!GV4##Q8uC6uo6I)!o zZ`W2!EFBjnN>x^qj8#>rz_vl8Q;S6bvx=)|($qG{w7xy`PuqPwlFBzy$!hyMTwH5U zS2uS9yEeA@_IBz=L%Fx>jilreR5r$m1FM#qa|a;D?F zOU3rs^GsLd(~u)NY=FB8s^?(Gz0lMRCBr#WH=?j?oH>#-@AhYTnv*PY_qMGex4qN? z+yU9?^m#f4+}&)IxjXu5%n*JGlk&_pTPw!bIzK|;s!YTOe(uWd&p2MO z<(+n?W}(?OJTN(AjcK{_f1&Q7NfuL-nKMI$;mt!5V!qEMM)(&QU7d#Y5iUlRaT{VjyqvM|REFCr55225{IR{6XG1%3PYLyD_{~U=kgbx!m42jPZ%5l{T&%kK$L^ zcu#ereplOg=NrS57U6GjHmh7}^C^jZ_=Ax>tL~}!!W_Rxp${#g4y@xQ7q2_`9n?W^ z=Oo)!j)l#868h8a^e3sT+>Cl8^4X-LW306WDGMR8fPtzMSeIHs|*D-J&? z_1Ly|2rl?+x;atS9wzoJ6nqOfA0kPAsP_7V#i=yxdUemiUYp`(UKC#we27|b$8QcA z@Hv3919yocWD?Cv+edx}tO#8h;l9Hy2^u56m+_5D=O@O5ns7NKzIE{G1|q&x42 zruBJ&Ie!Adep~kB)BP@gfH>J7f6#u*?%4UW7Od`P27ieIiQls07Drg}m=D{0+zYbb z*v0t%?L&ks`Ca}9@$#LBdy0zw6F+M6DX(mpt>0y?wN$?2d66$O$)Eg##CGOc^KlE4 zt^I_DUGM4p7~x9veUf-QLfTu4NneW6r|Ej5HX%9OW^oChbjZ>?<1RtCUo>5=u`H|2 zr@^DT_>BF=e&-2fc{^~k`Jw!5f~H8074Cv0>pR>jmY*8+pW!d2aZmGkOIx<~4*MO` z=-Q%fHSQ9V?|Z%g9QPfH6Wj1vI?eC#7fA~rPh$YIeviXlf*>=igJh77$283`cY2z> z{3L1milxcj{gs0&`HNLu>Tvt3GifRVy~Td>zC~V4q0Fni4(F_ufb}E(cio9`?aulU zm+u-JDo+j<$DAy`O&N0yE)*ws1XtJoLX_*%l~?{0@e5v-S9LuH+P;(EFZhs+%dlP$ znZLRFYN|K=iC2AV6#E|e!=1x$mpy!|KGl9=J>A3H4ScwJqc#@WSqxKUen@;MbEcoh ztoae?^7SYD@=d4vFFz*kaKfv(v#Gi>W*4F4$N8_1yyhhQ-l)I1*W#S*zMaim<}+P+ ze(LkAp?>}N)_`jqeAu+HmU3elW1Ej_?`&x7WLw0IVT^4)eyv?QY7VV_R6N}e-t~E( zY=go@E`jp?_KA6QE}Ro2E8p**IG>lTca2S7uY7-e;(TFTjQk5L z-=BRx^=Y%zWtl&G<$3gp^OU`rpge!|d3IBWuCEt$3E$w#`}h;{=JLkwdaX?4quBO? zHEvz?|I;6q#-E?T2lv({`e};_tB=CpvAmf0MME5)^YMPcVBysc7a4hcjHdn3dQ?7K z7Kb%~9K@H#d;1HnuU6U;-CrhOoD`R}RQHLwNhhZ*fv)8~ZlEw$4F1CED1T+R2fev> zbhOvu&k>LB1na|r;nTTg@pBNR)tL!=uUo(S5aIHi2u%%kjpKZnB-e3Gx<5DAJJ!D= z@AU}3#$IN^*Q|8AJj2agv4|58k`v{vcWWv+d9w_Mb?MJ3le?#{4a{5Zb;MBe9yc6t4b=-eL4(~if;@K}(&Xx)jQbln2An<4&Id(gvOJ}tVz z?dw_ju1wt(G}7}<8AJ}31)cx!zIn0JpG#Q<5yMA2_l~+IpFcD>izNIHpVHb zEk|izFunLB9YXg|U61ED@O;t7L!tGHCz=)i{W#vPc|-g!d^}X28SqG6kA-{VkmRrD zM`fFg-cPVwe{TFqw4F@G$8VwE(IYDfK1Fxdrw@-+POr+)%an$hNlr0~cRFMDB{CbB zJld3K|6-5K#{)fu-R zru{siY<-+W84WfMJ|%HoQ5qU9^T9NdDc>(0&y??%PG-vYOSfm-eyQ|SW$w&OjU`_D zux0+5GIt^8Fh3ORL&RN4+^}DpJVN+I$fPjnc>5jUt1_CXx$qwdU!AdiPXDC$q5dc0 zUIUKgTQYwk?%Ir>HhCR*jJP*thV2?zW&e%v^_ei;aros-{l-jxSl(J0W{JBg6Fn$k z-$>a2k5#(Q;Xk)zW?w7l4*bmdnf#lC;mY}Wn&am@TAQd1*|iCK-C^PP#{a(A zuMz*g_Z<9q=w-4OxE2^(aZTb$wAalca+JLgUlWCaQvTw?L^15e%D+Ia!kY|>roG8v zbpY7K#<|6}O3y-Ga^UKX*_$%M2S2K1a;WTQ+`XeG-sgJWU+#IAuCe2`lolF|`RubV zl}qlRi};3EmP}{AU|BMqw^B|>rq5H@=hvEbf!SId;Yp^@-zsh){XZIDUIRU0kLm#Q zNonCPUh`I)*8&&Tu!7SY4uVUrVXq}F&XX}u2QKjG{J(4Pa(XJht4+S2*9WbRD!s<= zWJ1a97b|_P$q$ZjVKvC1mT-78LFx0zKh!(HOd-a(037lKb+OkE;azC*lcVE<_H702 zF{=*W2(96hGv#kYyqYx4-4C%hqIP*O>s8n~*A2^P)#sZ%tk%P#^DP#y_B$L+>hr=| zY53S32z8+N8vlmcNMX?K2L)df*C%M3JI$~C;l7-=T9_fOdbxQ;ou$(HAhdL63Euz= zK4s($xs2{v+p~8EEKk1T?MCuT4rwCi32t$onBr z{~_o`(x>1%fMMQmX|=_*K4)Wc)ScZ!if*$qY@QkCp%ECYR=%Bq!3Ovn_s4 z%`Q{&*3U66U1A?L9nqNy-gqXQ2V}`icVgZRJ>k=>wx5t$&;jx5{J{Lrb>KaTa%R(k z_ga|U>%h&x)K;jYL%gp&&6)9>P#CS?dyW?%{CMmfWJ|xD-nuz~q@zJR#N!jPcYCjO zVI9TcGuz{!46m!gfv;@hBk|fkK=HiI!@R-mCns?)bdXn=8ByIQ2W~M_%uLC&wPuU5@nR z*z+L^m#zJ91$fan9sVO8{-YjVWnGi_{f6kb^R2WmI8>e+$1>&fJR*nLn(Q+S8Voyld)9TvAsTg|u^ z%O_*Lm`D@uWLog?_hnC8;6-m0i64EEynNNdWoy6Y;rC##JisWAKHu;#cX=4`G)}yD zx&xj<8IotEGQREef5+z!@AYUOO|-cEw2b*4>B8MZym0>x`6#`Qcx@Ul;d|EcE=--7 zlc@fG=*tp5^^3x{7+S;p=sx1Kc2=6sq+%IXn|o~DZ0(N|IDTTwb~uDH2R`9Mw;#UQ zkAvEqOpb%{@22x0?UiX_qrUZC;_#;%`P)^T>hL`8PmQzvXm3k>=qA_amie+hNi-|o z`JF4X1-K9VS7E%F4IsG z^}P!sxyEk^{4&A!Lh)UJ_R4xTkaS&V`?Lo=f6}>M``<|&%fYSc_cx>^b?hMv%h`a> zQ(3O%zFWhFyUP0QZu7AnWf#1RM7*bKUs7NWR+*=A|7X%|@gd!&3LXw`MrwaQS#R>2 zL;c`y&Arp0`jPKD-smgyA-Fi~1x@pL`$u50!%kiz*|6>-Ga|oX&oNp)dh=-6*-#D!` zkNbQ=tg<9$l_5DdA?IYc!H1Ps=@e^iP$c85?VXvM+08pQZ>l1WIdboa_kb=q`JcTC zpLpS2#cE)#=FP6+h50eNS?TBG=L2_jwT&2u2U{y5*3)$Z_= zROS!)UEt#_tQmgyvOw;cz-jd);dd|C)XAT$_q*sebFB+#$EV5*>o1vuIcLu5BN>tBkhFx)S(ypq0ylKH7vUOSr}NQxdpY zCsu5ms{e2XOqTL9VZuQ-MZ3l;$wGZ(vQK#`<*0Aa;kI-vyl*vqT-uNHxn^qIpP1Ob zQuB{mQ$LZP(A$5m|5V&OQ{R{Gi{s?Wae?4Am&Yv>Tp}(9T+Xm6!%8U3{d3T?$j1#% z@vM)pBk{6Wd+gS$}X+W)-hJZU0tk9YhUN@)uc zY4+%bv|u0d^aQVVyqZS0bU^K0b0qE4h>rt3ANs8{*XPvO_ZO0I3q4#?za%@!Ud1N$ z>ktc@tv%G|v2q4Ra^81$a`s!`{-oK?&kj%E(7VgRvD)js$`0Z1PK3^-ll7oH_sHvb{$*3rz?Cn z`aK`efTRr zyxfPs>cc1b@a=>fxbz`hcj`A6LiGD<{O;`Pj(GmM3r9SE!-XTB?{eXY=Wn`jbl>D# zE*#0=+b$f*;5#lH$>6($bu&t|e2?Fa{`dE7yt^}UH@_RWpdy$b=yzL-{ryAz=Aw$? z|69MCI_&Qs@tcQq?C*Q{jnYQ^{n&*g{(j=Z5r6l(aKztzE*$aqQx}f-`(s9_MR-ej93u~hxA&CR62Hr1%alxTDQA{b zZuoS#{%(d1)vWTHVZG$+kXCs4g?DRyKTTda3Y^E8zIzn26xn{ROwh7Gv{b-fnU*r& z>0v2h$Es63Evn0~Hd;(w{>0bi|8#9Mn}2n!jkv#1(NDV4R?gx>UH%QRdBImY+pd@w zgtK48%idyoy!HjI6}-RXVCTlf-5^*WCs}Fka9TpH?GiqIq40*t$eHto5IqZ*CoLEnAEUlY{APh_OweJ^1}c`3sYP7$C(gcc9eKq2E_6foopp({W}mj2w95L2uDY!v z$un54S^YcH^U>$!Cp)$zADXAvRG7QZtuS}DbNs|SUGsT6$MtzU0Z+nmSM&LEyc|2doMgwFl+(*mO6&p0>;bD^W4ZNMIiERlu^yZ3wU4K{ z^H|XUn`UEzm&M{GK|@$81lwlSi^|k|Q@^!Je~zUwTYH|THP}OI4jW+IUU$}90L-kq zl(6~^Z`w-+n>=2ejqQ_|O7Wx^j9at6Acjf&xY`<3Fjs$V*I_oxe)pMKMt}(bDL7XmdM|b`RZ>;aDpRnn? z8%(&qb)$brn74v$dOcy?rO;lt+RGx!_mB4G-J`y4ys@(k(&JC4_vq5Gy~ioukMulL z9$(gbXC7;;&8OfOdw7jiNAWw1RkF94S?+r0JcR7ZdrTs)_MJjr;mSd52=7Kmtog3p zoV7ZVsXZP%p-nCI_)m}PL&lKJU3%UXc(=Y=Z{yZ-$ved9z6x->0$XR}lJ`|k04MiV zNN#QHZfb8(9?`4w!9cIVst<)VXW0$?Gxii$c$zEsbFSWITSu;ZGI?jYTY2h}$y;HL zqIz8A^X{gP@wh9{;k z+;0ibUfOO`R zmz%|&{|7d0raiypdyltYoNli#ocCFMkB#U>hwSlxZucz8+k$ZYNo&~(_V|y*YhW6( z*z)Il zo<_JSVTXSP;g)GO_I1Q{WXf&j>xt{m#KT6=>qg=?Wb~k$!@rL3dDHCkn~2+#DfMG( z!WLhX?Dt@ z$F5~q*Xef;FdF|Z_I9TSS{!a_U)Ubshi@ewKMdTEr(-FVdmNUdEDv zf#O&YfX^J$ywlslmO=S`RJeCJ+~vv0L-QogT+KO@p%2#eMI;vK^rJAu{RkHsUJN9k z^i=zMkBMRbvFGO%-Z!i~_lx!^;~5-yr5AcbM0yeSTQ#RcdXWu^_8M!<#OA*Ij((f~ zVP+8Tw;J{=!UucVuoqfZ#=HGqkLIRHyz2XE;;X*r{vYGQ8pewG>HB}_`+w>Cf9d;w>HB}_`+w>Cf9d;w|Je8cScKTmO?+2+OV6dI z|5FL15=bTRzaxRy9Jeg@BfWFi~XVuICy*X z7TzhntbsRH*@$=}n_IWFu-~wpJR3${q-i`aIAdI670u#*Y$S(&g6~OPw-CaM|;`Z0Pk~-;LT#*aQ)H2>m#;ED~pSvsQa=QLc*Z=AIKVAQ) z>;H89pRWJ^G3)FN<~>`RWgk>{Qv3hZ{$F}fS$C{?Ol-E} zZAMc2fA`KpYX6VoI6JG<{y%YtHMReb^ddIoOzrcWiSy)pIQM?|Qr<*lkzczQpJkj`XMyeK(tn1yg~u54;?>5SeyTCA=Zrf&|4+~V zx2`qjo7f}VG-1q}&&Hl(voUXD;M|5i#D->Lt{BDU0Gox&uyJ?@-T6BD^#*JyE*j?_dt)Y!G>Z9>0f38>k;(xW^5}STfu%l<=ub|--n$<2|d1xjjQzhAN!i- z^!)$L*vl+BIX(YR&;Qf&|MdJnJ^xS7|Nk-P|GSu5-^aev8C$T0WuB9{5ZhAz-p2vk zRei?1g?Y%q*p1%LJm%}nV;(&i`%%WCB^2<<5=bSGN+6X$DuGl2sRU99q!LIakV+txz`wNwlK%hYFYd}KX8QlHWgk+1m;soT zeCw0?|Cba}|Nrb}M{ftF{{QW>0jd9geBP)2|LswVeGfo3w3wo#{{Ou{T=%MF>i_>> zd)CI)ChxcRP{O<)QyBH(!wA-JZTOL4UK_{{^iKAA*c|#*YlcfaG#>m$ z7Xx1D-#oKxKlJt{GAeBHWfm8yi1X2m45=bSGN+6X$DuGl2sRU99q!LIakV+txKq`S$0;vR2 b38WH8C6G!Wl|U+iR0634QVINvOW^+kmu`Fa diff --git a/projects/mtg/include/AIHints.h b/projects/mtg/include/AIHints.h index b5014725e..99d9975b3 100644 --- a/projects/mtg/include/AIHints.h +++ b/projects/mtg/include/AIHints.h @@ -12,6 +12,8 @@ using std::vector; class ManaCost; class MTGAbility; +namespace AI { + class AIHint { public: @@ -66,4 +68,6 @@ public: ~AIHints(); }; +}; + #endif diff --git a/projects/mtg/include/AIMomirPlayer.h b/projects/mtg/include/AIMomirPlayer.h index 07526778f..c32a0200e 100644 --- a/projects/mtg/include/AIMomirPlayer.h +++ b/projects/mtg/include/AIMomirPlayer.h @@ -3,6 +3,8 @@ #include "AIPlayerBaka.h" +namespace AI { + class AIMomirPlayer: public AIPlayerBaka { public: @@ -14,4 +16,6 @@ public: MTGAbility * getMomirAbility(); }; +}; + #endif diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 01a029fae..563620a01 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -18,20 +18,44 @@ #include "Player.h" #include "config.h" +#include #include using std::queue; +using std::vector; + + +namespace AI { class AIStats; class AIPlayer; + +class Action +{ +protected: + GameObserver* m_pObserver; + bool parseLine(const string& s); + +public: + Action(GameObserver* g, const string& s) : m_pObserver(g) + { + parseLine(s); + }; + + friend ostream& operator<<(ostream&, const Action&); + friend istream& operator>>(istream&, Action&); +}; + class AIAction { +protected: + int clickMultiAct(vector&actionTargets); + public: AIPlayer * owner; MTGAbility * ability; - NestedAbility * nability; Player * player; - int id; +// int id; MTGCardInstance * click; MTGCardInstance * target; // TODO Improve vectormAbilityTargets; @@ -60,7 +84,6 @@ public: { }; int Act(); - int clickMultiAct(vector&actionTargets); }; @@ -77,8 +100,20 @@ protected: int clickMultiTarget(TargetChooser * tc,vector&potentialTargets); int clickSingleTarget(TargetChooser * tc,vector&potentialTargets, MTGCardInstance * Choosencard = NULL); RandomGenerator randomGenerator; + virtual bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy); + virtual bool canPlay(MTGCardInstance * card); + virtual int getCreaturesInfo(Player * player, int neededInfo = INFO_NBCREATURES , int untapMode = 0, int canAttack = 0); + + virtual int createAbilityPotentialsActions(MTGAbility * a, MTGCardInstance * c, vector& actions); public: + enum { + INFO_NBCREATURES, + INFO_CREATURESPOWER, + INFO_CREATURESRANK, + INFO_CREATURESTOUGHNESS, + INFO_CREATURESATTACKINGPOWER + }; //These variables are used by TestSuite and Rules.cpp... TODO change that? int agressivity; @@ -89,7 +124,7 @@ public: virtual int receiveEvent(WEvent * event); virtual void Render(); - AIPlayer(GameObserver *observer, string deckFile, string deckFileSmall, MTGDeck * deck = NULL); + AIPlayer(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck = NULL); virtual ~AIPlayer(); virtual int chooseTarget(TargetChooser * tc = NULL, Player * forceTarget = NULL, MTGCardInstance * Chosencard = NULL, bool checkonly = false) = 0; @@ -116,5 +151,6 @@ class AIPlayerFactory{ #endif }; +} #endif diff --git a/projects/mtg/include/AIPlayerBaka.h b/projects/mtg/include/AIPlayerBaka.h index 65816c015..ccdae3e17 100644 --- a/projects/mtg/include/AIPlayerBaka.h +++ b/projects/mtg/include/AIPlayerBaka.h @@ -4,6 +4,8 @@ #include "AIPlayer.h" #include "AllAbilities.h" +namespace AI { + class AIStats; class AIHints; class AIHint; @@ -57,7 +59,7 @@ public: OrderedAIAction* a2Ptr = const_cast(&a2); int e1 = a1Ptr->getEfficiency(); int e2 = a2Ptr->getEfficiency(); - if (e1 == e2) return a1Ptr->id < a2Ptr->id; +// if (e1 == e2) return a1Ptr->id < a2Ptr->id; return (e1 > e2); } }; @@ -72,7 +74,7 @@ class AIPlayerBaka: public AIPlayer{ virtual int interruptIfICan(); virtual int chooseAttackers(); virtual int chooseBlockers(); - virtual int canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy); + virtual bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy); virtual int effectBadOrGood(MTGCardInstance * card, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); @@ -105,19 +107,10 @@ class AIPlayerBaka: public AIPlayer{ virtual int getEfficiency(OrderedAIAction * action); virtual int getEfficiency(MTGAbility * ability); virtual bool payTheManaCost(ManaCost * cost, MTGCardInstance * card = NULL,vector gotPayment = vector()); - virtual int getCreaturesInfo(Player * player, int neededInfo = INFO_NBCREATURES , int untapMode = 0, int canAttack = 0); virtual ManaCost * getPotentialMana(MTGCardInstance * card = NULL); virtual int selectAbility(); public: - enum { - INFO_NBCREATURES, - INFO_CREATURESPOWER, - INFO_CREATURESRANK, - INFO_CREATURESTOUGHNESS, - INFO_CREATURESATTACKINGPOWER - }; - vectorgotPayments; AIPlayerBaka(GameObserver *observer, string deckFile, string deckfileSmall, string avatarFile, MTGDeck * deck = NULL); @@ -137,4 +130,5 @@ class AIPlayerBaka: public AIPlayer{ virtual int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingContainer& ranking); }; +} #endif diff --git a/projects/mtg/include/AIPlayerBakaB.h b/projects/mtg/include/AIPlayerBakaB.h index 8943578d0..ee04b9ebf 100644 --- a/projects/mtg/include/AIPlayerBakaB.h +++ b/projects/mtg/include/AIPlayerBakaB.h @@ -11,6 +11,8 @@ class AIStats; class AIHints; +namespace AI { + class AIPlayerBakaB: public AIPlayerBaka{ protected: int orderBlockers(); @@ -18,7 +20,7 @@ protected: int interruptIfICan(); int chooseAttackers(); int chooseBlockers(); - int canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy); + bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy); int effectBadOrGood(MTGCardInstance * card, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL); @@ -61,6 +63,8 @@ protected: int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingContainer& ranking); }; +}; + #endif #endif diff --git a/projects/mtg/include/AIPlayerMinMax.h b/projects/mtg/include/AIPlayerMinMax.h new file mode 100644 index 000000000..8c10814bb --- /dev/null +++ b/projects/mtg/include/AIPlayerMinMax.h @@ -0,0 +1,34 @@ +/* + * Wagic, The Homebrew ?! is licensed under the BSD license + * See LICENSE in the Folder's root + * http://wololo.net/wagic/ + + AIPlayerMinMax is the MinMax implementation of the AIPlayer interface + */ + +#ifndef _IAPLAYER_MINMAX_H +#define _IAPLAYER_MINMAX_H + +#include "AIPlayer.h" +#include "config.h" + +namespace AI { + +class AIPlayerMinMax: public AIPlayer{ + +protected: + void LookAround(); + +public: + AIPlayerMinMax(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck = NULL); + virtual ~AIPlayerMinMax(); + + virtual int chooseTarget(TargetChooser * tc = NULL, Player * forceTarget = NULL, MTGCardInstance * Chosencard = NULL, bool checkonly = false) = 0; + virtual int affectCombatDamages(CombatStep) = 0; + virtual int Act(float dt) = 0; + +}; + +}; + +#endif diff --git a/projects/mtg/include/AIStats.h b/projects/mtg/include/AIStats.h index 744988dce..cf6cd1692 100644 --- a/projects/mtg/include/AIStats.h +++ b/projects/mtg/include/AIStats.h @@ -18,6 +18,8 @@ class MTGCard; class Damage; class WEvent; +namespace AI { + class AIStat { public: @@ -49,4 +51,6 @@ public: void Render(); }; +}; + #endif diff --git a/projects/mtg/include/GameObserver.h b/projects/mtg/include/GameObserver.h index 281e742ba..01f51eea0 100644 --- a/projects/mtg/include/GameObserver.h +++ b/projects/mtg/include/GameObserver.h @@ -111,6 +111,7 @@ class GameObserver{ void loadPlayer(int playerId, PlayerType playerType = PLAYER_TYPE_HUMAN, int decknb=0, bool premadeDeck=false); virtual void loadPlayer(int playerId, Player* player); + int getPlayerId(Player* player) {if(player == players[0]) return 1; else if(player == players[1]) return 2; else return 0;}; Player * currentPlayer; Player * currentActionPlayer; Player * isInterrupting; diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index 099b24604..eb52fd516 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -105,7 +105,7 @@ public: std::string GetCurrentDeckStatsFile(); virtual bool parseLine(const string& s); friend ostream& operator<<(ostream&, const Player&); - friend istream& operator>>(istream&, Player&); + friend istream& operator>>(istream&, Player&); bool operator<(Player& aPlayer); bool isDead(); }; diff --git a/projects/mtg/include/TestSuiteAI.h b/projects/mtg/include/TestSuiteAI.h index 6e33055ed..c90a966e4 100644 --- a/projects/mtg/include/TestSuiteAI.h +++ b/projects/mtg/include/TestSuiteAI.h @@ -114,7 +114,7 @@ public: int run(); }; -class TestSuiteAI:public AIPlayerBaka +class TestSuiteAI:public AI::AIPlayerBaka { private: MTGCardInstance * getCard(string action); diff --git a/projects/mtg/src/AIHints.cpp b/projects/mtg/src/AIHints.cpp index f61686598..434415d2b 100644 --- a/projects/mtg/src/AIHints.cpp +++ b/projects/mtg/src/AIHints.cpp @@ -7,6 +7,8 @@ #include +namespace AI { + AIHint::AIHint(string _line) { string line = _line; @@ -584,3 +586,5 @@ AIAction * AIHints::suggestAbility(ManaCost * potentialMana) } return NULL; } + +}; \ No newline at end of file diff --git a/projects/mtg/src/AIMomirPlayer.cpp b/projects/mtg/src/AIMomirPlayer.cpp index 0d7e741a5..05b0435b3 100644 --- a/projects/mtg/src/AIMomirPlayer.cpp +++ b/projects/mtg/src/AIMomirPlayer.cpp @@ -6,6 +6,8 @@ #include "AIStats.h" #include "AllAbilities.h" +namespace AI { + AIMomirPlayer::AIMomirPlayer(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) : AIPlayerBaka(observer, file, fileSmall, avatarFile, deck) { @@ -132,3 +134,4 @@ int AIMomirPlayer::computeActions() return AIPlayerBaka::computeActions(); } +}; diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 1c911af9d..29978ff85 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -13,11 +13,36 @@ #include "AIPlayerBakaB.h" #endif +namespace AI { + + +bool Action::parseLine(const string& s) +{ + return true; +} + +ostream& operator<<(ostream& out, const Action&) +{ + return out; +} + +istream& operator>>(istream& in, Action& a) +{ + string s; + + while(std::getline(in, s)) + { + if(!a.parseLine(s)) + { + break; + } + } + + return in; +} int AIPlayer::totalAIDecks = -1; -const char * const MTG_LAND_TEXTS[] = { "artifact", "forest", "island", "mountain", "swamp", "plains", "other lands" }; - AIAction::AIAction(AIPlayer * owner, MTGCardInstance * c, MTGCardInstance * t) : owner(owner), ability(NULL), player(NULL), click(c), target(t) { @@ -116,7 +141,7 @@ int AIAction::clickMultiAct(vector& actionTargets) return 1; } -AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, MTGDeck * deck) : +AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) : Player(observer, file, fileSmall, deck) { agressivity = 50; @@ -124,6 +149,33 @@ AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, MTGDec playMode = Player::MODE_AI; mFastTimerMode = false; + if(avatarFile != "") + { + if(!loadAvatar(avatarFile, "bakaAvatar")) + { + avatarFile = "baka.jpg"; + loadAvatar(avatarFile, "bakaAvatar"); + } + mAvatarName = avatarFile; + } + else //load a random avatar. + { + avatarFile = "avatar"; + char buffer[3]; + sprintf(buffer, "%i", int(observer->getRandomGenerator()->random()%100)); + avatarFile.append(buffer); + avatarFile.append(".jpg"); + if(!loadAvatar(avatarFile, "bakaAvatar")) + { + avatarFile = "baka.jpg"; + loadAvatar(avatarFile, "bakaAvatar"); + } + mAvatarName = avatarFile; + } + + if (fileSmall == "ai_baka_eviltwin") + mAvatar->SetHFlip(true); + } AIPlayer::~AIPlayer() @@ -372,3 +424,151 @@ void AIPlayer::invalidateTotalAIDecks() totalAIDecks = -1; } +bool AIPlayer::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy) +{ + if (ennemy->has(Constants::FIRSTSTRIKE) || ennemy->has(Constants::DOUBLESTRIKE)) + return false; + if (!(card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE))) + return false; + if (!(card->power >= ennemy->toughness)) + return false; + if (!(card->power >= ennemy->toughness + 1) && ennemy->has(Constants::FLANKING)) + return false; + return true; +} + +bool AIPlayer::canPlay(MTGCardInstance * card) +{ + if (card->hasType(Subtypes::TYPE_LAND)) + { + if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY) + return false; + } + else + { + if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY) + return false; + } + if (!manaPool->canAfford(card->getManaCost())) + return false; + + return true; +} + +int AIPlayer::getCreaturesInfo(Player * player, int neededInfo, int untapMode, int canAttack) +{ + int result = 0; + CardDescriptor cd; + cd.init(); + cd.setType("Creature"); + cd.unsecureSetTapped(untapMode); + MTGCardInstance * card = NULL; + while ((card = cd.nextmatch(player->game->inPlay, card))) + { + if (!canAttack || card->canAttack()) + { + if (neededInfo == INFO_NBCREATURES) + { + result++; + } + else + { + result += card->power; + } + } + } + return result; +} + +int AIPlayer::createAbilityPotentialsActions(MTGAbility * a, MTGCardInstance * c, vector& actions) +{ + if (!a->getActionTc()) + { + AIAction aiAction(this, a, c, NULL); + actions.push_back(aiAction); + return 1; + } + + vectorpotentialTargets; + for (int i = 0; i < 2; i++) + { + Player * p = observer->players[i]; + MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay,p->game->stack }; + // try player first + if(a->getActionTc()->canTarget((Targetable*)p)) + { + if(a->getActionTc()->maxtargets == 1) + { + AIAction aiAction(this, a, p, c); + actions.push_back(aiAction); + } + else + potentialTargets.push_back(p); + } + for (int j = 0; j < 5; j++) + { + MTGGameZone * zone = playerZones[j]; + for (int k = 0; k < zone->nb_cards; k++) + { + MTGCardInstance * t = zone->cards[k]; + if (a->getActionTc()->canTarget(t)) + { + if(a->getActionTc()->maxtargets == 1) + { + AIAction aiAction(this, a, c, t); + actions.push_back(aiAction); + } + else + { + potentialTargets.push_back(t); + } + } + } + } + } + vectorrealTargets; + if(a->getActionTc()->maxtargets != 1) + { + if(a->getActionTc()->getNbTargets() && a->getActionTc()->attemptsToFill > 4) + { + a->getActionTc()->done = true; + return 0; + } + while(potentialTargets.size()) + { + AIAction * check = NULL; + + Player * pTargeting = 0; + MTGCardInstance * cTargeting = dynamic_cast(potentialTargets[0]); + if(cTargeting) + { + check = NEW AIAction(this, a,c,cTargeting); + } + else + { + pTargeting = dynamic_cast(potentialTargets[0]); + if(pTargeting) + check = NEW AIAction(this, a,pTargeting,c); + } + + if(check && pTargeting) + { + AIAction aiAction(this, a,pTargeting,c); + actions.push_back(aiAction); + } + if(check) + realTargets.push_back(potentialTargets[0]); + potentialTargets.erase(potentialTargets.begin()); + SAFE_DELETE(check); + } + if(!realTargets.size() || (int(realTargets.size()) < a->getActionTc()->maxtargets && a->getActionTc()->targetMin)) + return 0; + AIAction aiAction(this, a, c,realTargets); + aiAction.target = dynamic_cast(realTargets[0]); + aiAction.playerAbilityTarget = dynamic_cast(realTargets[0]); + actions.push_back(aiAction); + } + return 1; +} + +} \ No newline at end of file diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 2c1039562..634c7e555 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -14,6 +14,7 @@ // AIAction // +namespace AI { Player * OrderedAIAction::getPlayerTarget() { @@ -2204,36 +2205,6 @@ int AIPlayerBaka::computeActions() return 1; }; - -// -// Combat // -// - -int AIPlayerBaka::getCreaturesInfo(Player * player, int neededInfo, int untapMode, int canAttack) -{ - int result = 0; - CardDescriptor cd; - cd.init(); - cd.setType("Creature"); - cd.unsecureSetTapped(untapMode); - MTGCardInstance * card = NULL; - while ((card = cd.nextmatch(player->game->inPlay, card))) - { - if (!canAttack || card->canAttack()) - { - if (neededInfo == INFO_NBCREATURES) - { - result++; - } - else - { - result += card->power; - } - } - } - return result; -} - int AIPlayerBaka::chooseAttackers() { //Attack with all creatures @@ -2280,19 +2251,12 @@ int AIPlayerBaka::chooseAttackers() } /* Can I first strike my oponent and get away with murder ? */ -int AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy) +bool AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy) { if(hints && hints->HintSaysAlwaysBlock(observer,ennemy)) - return 1; - if (ennemy->has(Constants::FIRSTSTRIKE) || ennemy->has(Constants::DOUBLESTRIKE)) - return 0; - if (!(card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE))) - return 0; - if (!(card->power >= ennemy->toughness)) - return 0; - if (!(card->power >= ennemy->toughness + 1) && ennemy->has(Constants::FLANKING)) - return 0; - return 1; + return true; + + return AIPlayer::canFirstStrikeKill(card, ennemy); } int AIPlayerBaka::chooseBlockers() @@ -2476,7 +2440,7 @@ int AIPlayerBaka::receiveEvent(WEvent * event) AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) : - AIPlayer(observer, file, fileSmall, deck) + AIPlayer(observer, file, fileSmall, avatarFile, deck) { nextCardToPlay = NULL; @@ -2491,34 +2455,6 @@ AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall for (size_t i = 0; i < mDeck->meta_AIHints.size(); ++i) hints->add(mDeck->meta_AIHints[i]); } - - - if(avatarFile != "") - { - if(!loadAvatar(avatarFile, "bakaAvatar")) - { - avatarFile = "baka.jpg"; - loadAvatar(avatarFile, "bakaAvatar"); - } - mAvatarName = avatarFile; - } - else //load a random avatar. - { - avatarFile = "avatar"; - char buffer[3]; - sprintf(buffer, "%i", int(observer->getRandomGenerator()->random()%100)); - avatarFile.append(buffer); - avatarFile.append(".jpg"); - if(!loadAvatar(avatarFile, "bakaAvatar")) - { - avatarFile = "baka.jpg"; - loadAvatar(avatarFile, "bakaAvatar"); - } - mAvatarName = avatarFile; - } - - if (fileSmall == "ai_baka_eviltwin") - mAvatar->SetHFlip(true); initTimer(); } @@ -2610,3 +2546,5 @@ AIPlayerBaka::~AIPlayerBaka() { } SAFE_DELETE(hints); } + +} diff --git a/projects/mtg/src/AIPlayerBakaB.cpp b/projects/mtg/src/AIPlayerBakaB.cpp index 67675ab21..281b39c7a 100644 --- a/projects/mtg/src/AIPlayerBakaB.cpp +++ b/projects/mtg/src/AIPlayerBakaB.cpp @@ -14,6 +14,7 @@ // Abilities/Target Selection // +namespace AI { MTGCardInstance * AIPlayerBakaB::chooseCard(TargetChooser * tc, MTGCardInstance * source, int random) { @@ -117,7 +118,7 @@ int AIPlayerBakaB::chooseAttackers() } /* Can I first strike my oponent and get away with murder ? */ -int AIPlayerBakaB::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy) +bool AIPlayerBakaB::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy) { return AIPlayerBaka::canFirstStrikeKill(card, ennemy); } @@ -180,7 +181,7 @@ AIPlayerBakaB::~AIPlayerBakaB() { } - +} #endif diff --git a/projects/mtg/src/AIPlayerMinMax.cpp b/projects/mtg/src/AIPlayerMinMax.cpp new file mode 100644 index 000000000..d087e3dc7 --- /dev/null +++ b/projects/mtg/src/AIPlayerMinMax.cpp @@ -0,0 +1,75 @@ +#include "PrecompiledHeader.h" + +#include "AIPlayerMinMax.h" +#include "CardDescriptor.h" +#include "AIStats.h" +#include "AllAbilities.h" +#include "ExtraCost.h" +#include "GuiCombat.h" +#include "AIHints.h" +#include "ManaCostHybrid.h" +#include "MTGRules.h" + +namespace AI { + +// +// Abilities/Target Selection +// + +AIPlayerMinMax::AIPlayerMinMax(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck) : + AIPlayer(observer, deckFile, deckFileSmall, avatarFile, deck) +{ +} + +int AIPlayerMinMax::Act(float dt) +{ + return 0; +}; + +AIPlayerMinMax::~AIPlayerMinMax() +{ +} + + +void AIPlayerMinMax::LookAround() +{ + vector::iterator ite; + vector potentialActions; + + // look for something useable (including mana) + for (size_t i = 1; i < observer->mLayers->actionLayer()->mObjects.size(); i++) + { + MTGAbility * a = ((MTGAbility *) observer->mLayers->actionLayer()->mObjects[i]); + //Make sure we can use the ability + for (int j = 0; j < game->inPlay->nb_cards; j++) + { + MTGCardInstance * card = game->inPlay->cards[j]; + if (a->isReactingToClick(card, 0)) + { + createAbilityPotentialsActions(a, card, potentialActions); + } + } + } + + // look for something playable + for(ite = game->hand->cards.begin(); ite != game->hand->cards.end(); ite++) + { + if(canPlay(*ite)) + { + AIAction a(this, (*ite)); + potentialActions.push_back(a); + } + } + + stringstream stream; + stream << *observer; + vector::iterator it; + for(it = potentialActions.begin(); it != potentialActions.end(); it++) + { + GameObserver g; + g.load(stream.str()); +// g.processAction((*it)); + } +} + +} diff --git a/projects/mtg/src/AIStats.cpp b/projects/mtg/src/AIStats.cpp index 3e9a59664..0da1f1122 100644 --- a/projects/mtg/src/AIStats.cpp +++ b/projects/mtg/src/AIStats.cpp @@ -6,6 +6,9 @@ #include "MTGCardInstance.h" #include "WEvent.h" #include "AllAbilities.h" + +namespace AI { + //TODO:better comments this is too cryptic to work on by anyone but original coder. bool compare_aistats(AIStat * first, AIStat * second) { @@ -217,3 +220,5 @@ void AIStats::Render() } } } + +} \ No newline at end of file diff --git a/projects/mtg/src/ActionStack.cpp b/projects/mtg/src/ActionStack.cpp index 69a32202e..cf2dc5f20 100644 --- a/projects/mtg/src/ActionStack.cpp +++ b/projects/mtg/src/ActionStack.cpp @@ -994,15 +994,15 @@ void ActionStack::Update(float dt) void ActionStack::cancelInterruptOffer(InterruptDecision cancelMode, bool log) { int playerId = (observer->isInterrupting == observer->players[1]) ? 1 : 0; - interruptDecision[playerId] = cancelMode; - askIfWishesToInterrupt = NULL; - observer->isInterrupting = NULL; - timer = -1; if(log) { stringstream stream; stream << "no " << cancelMode; observer->logAction(playerId, stream.str()); } + interruptDecision[playerId] = cancelMode; + askIfWishesToInterrupt = NULL; + observer->isInterrupting = NULL; + timer = -1; } void ActionStack::endOfInterruption(bool log) diff --git a/projects/mtg/src/Credits.cpp b/projects/mtg/src/Credits.cpp index 1e07fcb49..d5c8062b1 100644 --- a/projects/mtg/src/Credits.cpp +++ b/projects/mtg/src/Credits.cpp @@ -625,7 +625,7 @@ int Credits::isDifficultyUnlocked(DeckStats * stats) { if (options[Options::DIFFICULTY_MODE_UNLOCKED].number) return 0; - int nbAIDecks = AIPlayer::getTotalAIDecks(); + int nbAIDecks = AI::AIPlayer::getTotalAIDecks(); int wins = 0; @@ -743,7 +743,7 @@ int Credits::IsMoreAIDecksUnlocked(DeckStats * stats) { // the number of currently unlocked decks in order to go through. if (stats->nbGames() < currentlyUnlocked * 1.2) return 0; - if (AIPlayer::getTotalAIDecks() > currentlyUnlocked) + if (AI::AIPlayer::getTotalAIDecks() > currentlyUnlocked) return 1; return 0; diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index b7dcda29c..17ace2d4f 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -1668,7 +1668,7 @@ bool GameObserver::load(const string& ss, bool undo, int controlledPlayerIndex } else { - logAction(s); + actionsList.push_back(s); } break; } @@ -1772,11 +1772,11 @@ bool GameObserver::processActions(bool undo for(loadingite = loadingList.begin(); loadingite != loadingList.end(); loadingite++, cmdIndex++) { - processAction(*loadingite); - + string s = *loadingite; + processAction(s); size_t nb = actionsList.size(); - for (int i = 0; i<6; i++) + for (int i = 0; i < 6; i++) { // let's fake an update GameObserver::Update(counter); @@ -1815,12 +1815,15 @@ void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone, size_t in void GameObserver::logAction(const string& s) { + stringstream stream; + stream << s << " cp " << getPlayerId(currentPlayer) << ", ii " << getPlayerId(isInterrupting) << ", cap " << getPlayerId(currentActionPlayer); + if(mLoading) { string toCheck = *loadingite; - dumpAssert(toCheck == s); + dumpAssert(toCheck == stream.str()); } - actionsList.push_back(s); + actionsList.push_back(stream.str()); }; bool GameObserver::undo() @@ -1850,7 +1853,7 @@ Player* GameObserver::createPlayer(const string& playerMode switch(aMode) { case Player::MODE_AI: - AIPlayerFactory playerCreator; + AI::AIPlayerFactory playerCreator; if(players.size()) pPlayer = playerCreator.createAIPlayer(this, MTGCollection(), players[0]); else @@ -1923,7 +1926,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b } else { //AI Player, chooses deck - AIPlayerFactory playerCreator; + AI::AIPlayerFactory playerCreator; Player * opponent = NULL; if (playerId == 1) opponent = players[0]; @@ -1933,7 +1936,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b else { //Random deck - AIPlayerFactory playerCreator; + AI::AIPlayerFactory playerCreator; Player * opponent = NULL; // Reset the random logging. @@ -1950,7 +1953,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b } if (playerType == PLAYER_TYPE_CPU_TEST) - ((AIPlayer *) players[playerId])->setFastTimerMode(); + ((AI::AIPlayer *) players[playerId])->setFastTimerMode(); } } diff --git a/projects/mtg/src/GameStateDeckViewer.cpp b/projects/mtg/src/GameStateDeckViewer.cpp index 41a8a2f37..c657714a5 100644 --- a/projects/mtg/src/GameStateDeckViewer.cpp +++ b/projects/mtg/src/GameStateDeckViewer.cpp @@ -332,7 +332,7 @@ void GameStateDeckViewer::saveDeck() void GameStateDeckViewer::saveAsAIDeck(string deckName) { - int deckId = AIPlayer::getTotalAIDecks() + 1; + int deckId = AI::AIPlayer::getTotalAIDecks() + 1; std::ostringstream oss; oss << "deck" <save(filepath, true, deckName, deckDesc); - AIPlayer::invalidateTotalAIDecks(); //We added one AI deck, so we need to invalidate the count cache + AI::AIPlayer::invalidateTotalAIDecks(); //We added one AI deck, so we need to invalidate the count cache } void GameStateDeckViewer::sellCard() diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index c2b4174da..35f57d66c 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -1733,8 +1733,8 @@ void GameStateDuel::setAISpeed() { if (mParent->players[i] == PLAYER_TYPE_CPU) { - if(dynamic_cast(game->players[i])) - ((AIPlayer *)game->players[i])->setFastTimerMode(tournament->getFastTimerMode()); + if(dynamic_cast(game->players[i])) + ((AI::AIPlayer *)game->players[i])->setFastTimerMode(tournament->getFastTimerMode()); } } } diff --git a/projects/mtg/src/GameStateMenu.cpp b/projects/mtg/src/GameStateMenu.cpp index 11327ce83..a423c2875 100644 --- a/projects/mtg/src/GameStateMenu.cpp +++ b/projects/mtg/src/GameStateMenu.cpp @@ -253,7 +253,7 @@ int GameStateMenu::gamePercentComplete() { //unlocked AI decks int currentlyUnlocked = options[Options::AIDECKS_UNLOCKED].number; - int totalAIDecks = AIPlayer::getTotalAIDecks(); + int totalAIDecks = AI::AIPlayer::getTotalAIDecks(); int reallyUnlocked = MIN(currentlyUnlocked, totalAIDecks); total+= totalAIDecks / 10; done+= reallyUnlocked / 10; diff --git a/projects/mtg/src/GuiCombat.cpp b/projects/mtg/src/GuiCombat.cpp index 46265e209..8fd3a8af3 100644 --- a/projects/mtg/src/GuiCombat.cpp +++ b/projects/mtg/src/GuiCombat.cpp @@ -727,7 +727,7 @@ int GuiCombat::receiveEventMinus(WEvent* e) DAMAGE: step = event->step; if (!observer->currentPlayer->displayStack()) { - ((AIPlayer *) observer->currentPlayer)->affectCombatDamages(step); + ((AI::AIPlayer *) observer->currentPlayer)->affectCombatDamages(step); observer->userRequestNextGamePhase(false, false); return 1; } diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index 2792c5a11..db9cb85cf 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -385,18 +385,30 @@ bool Player::operator<(Player& aPlayer) if(isDead() && !aPlayer.isDead()) return true; + // if this opponent is not dead and aPlayer opponent is dead then this < aPlayer + if(!opponent()->isDead() && aPlayer.opponent()->isDead()) + return true; + // heuristics for min-max // if this is more poisoined than aPlayer then this < aPlayer - if(poisonCount > aPlayer.poisonCount) + if((poisonCount - opponent()->poisonCount) > (aPlayer.poisonCount - aPlayer.opponent()->poisonCount)) return true; // if this has less life than aPlayer then this < aPlayer - if(life < aPlayer.life) + if((life - opponent()->life) < (aPlayer.life - aPlayer.opponent()->life)) return true; - // if this has less parmanents in game that aPlayer then this < aPlayer - if(game->battlefield->cards.size() < aPlayer.game->battlefield->cards.size()) + // if this has less permanents in game that aPlayer then this < aPlayer + if(((int)game->battlefield->cards.size() - (int)opponent()->game->battlefield->cards.size()) < ((int)aPlayer.game->battlefield->cards.size() - (int)aPlayer.opponent()->game->battlefield->cards.size())) + return true; + + // if this has less cards in hand that aPlayer then this < aPlayer + if(((int)game->hand->cards.size() - (int)opponent()->game->hand->cards.size()) < ((int)aPlayer.game->hand->cards.size() - (int)aPlayer.opponent()->game->hand->cards.size())) + return true; + + // if this has less mana than aPlayer then this < aPlayer + if(aPlayer.manaPool->canAfford(manaPool)) return true; return false; diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index 9d7ed8ae9..54f051b46 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -183,8 +183,8 @@ void Rules::addExtraRules(GameObserver* g) else if (p->isAI() && (p->playMode == Player::MODE_AI && p->opponent()->playMode== Player::MODE_AI)) { handsize = ((AADrawer *)a)->getNumCards(); - ((AIPlayer *) p)->forceBestAbilityUse = true; - ((AIPlayer *) p)->agressivity += 100; + ((AI::AIPlayer *) p)->forceBestAbilityUse = true; + ((AI::AIPlayer *) p)->agressivity += 100; hand->OptimizedHand(p,handsize, 3, 1, 3); } else if (!p->isAI() && !Optimizedhandcheat) @@ -202,8 +202,8 @@ void Rules::addExtraRules(GameObserver* g) handsize = ((AADrawer *)a)->getNumCards(); if(difficultyRating == EASY) { - ((AIPlayer *) p)->forceBestAbilityUse = true; - ((AIPlayer *) p)->agressivity += 100; + ((AI::AIPlayer *) p)->forceBestAbilityUse = true; + ((AI::AIPlayer *) p)->agressivity += 100; hand->OptimizedHand(p,handsize, 3, 1, 3);//easy decks get a major boost, open hand is 2lands,1 creature under 3 mana,3spells under 3 mana. } else if (difficultyRating == NORMAL) @@ -265,7 +265,7 @@ Player * Rules::loadPlayerMomir(GameObserver* observer, int isAI) if (!isAI) // Human Player player = NEW HumanPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, false, tempDeck); else - player = NEW AIMomirPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, empty, tempDeck); + player = NEW AI::AIMomirPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, empty, tempDeck); return player; } @@ -299,7 +299,7 @@ Player * Rules::loadPlayerRandom(GameObserver* observer, int isAI, int mode) if (!isAI) // Human Player player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck); else - player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck); + player = NEW AI::AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck); return player; } diff --git a/projects/mtg/src/StoryFlow.cpp b/projects/mtg/src/StoryFlow.cpp index b77c40efd..2b56e2502 100644 --- a/projects/mtg/src/StoryFlow.cpp +++ b/projects/mtg/src/StoryFlow.cpp @@ -310,7 +310,7 @@ void StoryDuel::init() sprintf(deckFile, "%s/opponent_deck.txt", folder); sprintf(deckFileSmall, "campaign_ennemy_%s_%s", mParent->folder.c_str(), pageId.c_str()); - game->loadPlayer(1, NEW AIPlayerBaka(game, deckFile, deckFileSmall, "baka.jpg")); + game->loadPlayer(1, NEW AI::AIPlayerBaka(game, deckFile, deckFileSmall, "baka.jpg")); string rulesFile = folder; rulesFile.append("/rules.txt"); diff --git a/projects/mtg/src/Tasks.cpp b/projects/mtg/src/Tasks.cpp index d1e32a63b..eca12e80e 100644 --- a/projects/mtg/src/Tasks.cpp +++ b/projects/mtg/src/Tasks.cpp @@ -616,7 +616,7 @@ string TaskWinAgainst::getShortDesc() bool TaskWinAgainst::isDone(GameObserver* observer, GameApp *) { - AIPlayerBaka * baka = (AIPlayerBaka*) observer->players[1]; + AI::AIPlayerBaka * baka = (AI::AIPlayerBaka*) observer->players[1]; return ((baka) && (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins && (baka->deckId == opponent)); } diff --git a/projects/mtg/src/TestSuiteAI.cpp b/projects/mtg/src/TestSuiteAI.cpp index 80c930ef8..dad308b6a 100644 --- a/projects/mtg/src/TestSuiteAI.cpp +++ b/projects/mtg/src/TestSuiteAI.cpp @@ -567,7 +567,7 @@ int TestSuite::loadNext() #elif defined(IOS) thread_count = 6; #else - thread_count = 4; + thread_count = 2; #endif for(size_t i = 0; i < (thread_count-1); i++) mWorkerThread.push_back(new boost::thread(ThreadProc, this)); @@ -600,6 +600,13 @@ void TestSuite::ThreadProc(void* inParam) theGame.observer->startGame(theGame.gameType, /*instance->mRules*/Rules::getRulesByFilename("testsuite.txt")); theGame.initGame(); + while(!theGame.observer->didWin()) + theGame.observer->Update(counter++); + + stringstream stream; + stream << (*theGame.observer); + theGame.observer->load(stream.str(), false, 0, &theGame); + while(!theGame.observer->didWin()) theGame.observer->Update(counter++); } @@ -834,7 +841,7 @@ void TestSuiteGame::initGame() for (int i = 0; i < 2; i++) { - AIPlayerBaka * p = (AIPlayerBaka *) (observer->players[i]); + AI::AIPlayerBaka * p = (AI::AIPlayerBaka *) (observer->players[i]); p->forceBestAbilityUse = forceAbility; p->life = initState.players[i]->life; p->poisonCount = initState.players[i]->poisonCount; diff --git a/projects/mtg/template.vcxproj b/projects/mtg/template.vcxproj index 7109ebd09..6086a7965 100644 --- a/projects/mtg/template.vcxproj +++ b/projects/mtg/template.vcxproj @@ -310,6 +310,7 @@ + @@ -450,6 +451,7 @@ + diff --git a/projects/mtg/template.vcxproj.filters b/projects/mtg/template.vcxproj.filters index 32cd0ca8b..db101b330 100644 --- a/projects/mtg/template.vcxproj.filters +++ b/projects/mtg/template.vcxproj.filters @@ -331,6 +331,9 @@ src + + src + @@ -687,6 +690,9 @@ inc + + inc +