From 9e843fe646044fe8ab0aa435bd650ecfa49ed51f Mon Sep 17 00:00:00 2001 From: kara-mosr Date: Thu, 5 Feb 2026 16:27:10 +0100 Subject: [PATCH] heuristic + arena + alphabeta --- build/fr/iut_fbleau/HexGame/Arena.class | Bin 0 -> 3022 bytes build/fr/iut_fbleau/HexGame/ArenaMain.class | Bin 0 -> 876 bytes javaAPI/fr/iut_fbleau.tar | Bin 51200 -> 61440 bytes javaAPI/fr/iut_fbleau/HexGame/Arena.java | 62 ++++ javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java | 15 + .../fr/iut_fbleau/HexGame/HeuristicBot.java | 48 +++ javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java | 15 +- javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java | 89 ++++++ .../fr/iut_fbleau/HexGame/MonteCarloBot.java | 59 ++++ javaAPI/fr/iut_fbleau/HexGame/RandomBot.java | 4 - javaAPI/fr/iut_fbleau/HexGame/Simulation.java | 273 ++++++++++++++++++ 11 files changed, 560 insertions(+), 5 deletions(-) create mode 100644 build/fr/iut_fbleau/HexGame/Arena.class create mode 100644 build/fr/iut_fbleau/HexGame/ArenaMain.class create mode 100644 javaAPI/fr/iut_fbleau/HexGame/Arena.java create mode 100644 javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java create mode 100644 javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java create mode 100644 javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java create mode 100644 javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java create mode 100644 javaAPI/fr/iut_fbleau/HexGame/Simulation.java diff --git a/build/fr/iut_fbleau/HexGame/Arena.class b/build/fr/iut_fbleau/HexGame/Arena.class new file mode 100644 index 0000000000000000000000000000000000000000..3f8ddcaf6e96fbf20abc31e261cda5dfc2989c0f GIT binary patch literal 3022 zcmX^0Z`VEs1_pD6*<1{23|8z6)?5rs3|5>Bsth*l47MB$c3cds4E7+t0|$d6h~vc0 z;LOFq&EUe#;L6|zBHTfQ2Rnl&CxaJ*H^?9#E(Tu)KOqJNh9EA6V1^Jb233YoE`~6M za3KaphDeYxQS1!STnthSG3*Sn>S3^EMyoD8xI2_RM?h?T_7kj%xP#E=3aQrQ{O z*cs9p8Ca6?ONtp81bng*%M$fVOEPoxeKLzntQi?NgEQ0f5=%;pQW+U+(4=jA(u(vm zOH1O@l5$cLOZD9ob5k7yJoO!uic5+TlS@GSfSkn2)FNv;kWtCSW#L7cC83?dMt zGV}G_GILTPqSlNIY&MyBnI(3N49psuVT=qc&iN^+j12rfnR%&xrMXF|MInhvIjM{c zyul@j$=SY%1yDX)VoHirehDLkt_JaL0@=V^RGP=mkip2nS&)-h>6=)RoWaOYLV_VA zScgMtP-<~$4k)O(g7Zs@l2bte!pOkoSd^NVs0WG<9tK?oJw^s$5E~LVjzvX@m7oaX zVNhq#U}O*iOQ8fNBZFWf$b|SJu<^xu$;D+LgEDy-v>9|58AP4(OB4)s6hM@bjzV~5 zUS4Vu7Y{=gLpIE+%zS-Mf7goS)Pj=C{5&3p90omhhFl(oJcfK8h608{9)==@VnzlH zViN&7LkSNm@rIWWRS+L7#y{X3<6*m-#5Vr6!l)bPyzoFfxd0 zK(Y;3PjE?5W?s5AC>66M78IoBr7$vRAuDjsNGu9YEi6sVOHQ@cgy_u7*N0~|{(_>+ zyb^Fu3MooVPGw}^1G@+uMxfNi$iSSKl7eO}#HrSrQH%^M#hF#9;JoU|$iSSQS^`q) z3AG9AMyO&GzXn$pm!#%0GBD?tf;7P06##KfQEFnYH6sH@dTNPtPGWH}BZII8vXNjx zYeoisuv@{lfQ1+tc+*o$f-`dqa#H;gb5j`^L{RiWgNut{BEuv`2GO9>yu8f3bcI|{ zmbX%1R46M}U}R(v&rQrubfSWk;=%xUY3|sn(Cj%$e@O7FEl){+QP=D!>Gf^#mL}=tOM!@ zOqXJcU^SIBIVZn3m63r(!xNO!M3Bt}hdVd{vIqD$M!E(WGBU{FDCHn|%9@dZxd0UH zX2|A%@}o79d$9NtD#XYDwaJK)!30++?Fr8IPy@k<8DxQPVgcBx3L2U?oCwKf*6a+E zL8Z%dMg|o`63fgh%g;{LhlD01eYhnim*f{!vNOzNWMK2j&(AI`U}WI&1eK%UfJrT8 zWSD{MO{f`=QqChWFC{0nSOuaH>R9BIgcKeq8bc}zQn4tHMp6$AEadov8sVInlM|d- zl4{M)FpH5v6~lkn+{VZthhH6oB7-V}8Uq6Z69W^dK4M^G&;;|e7??nn4FdxM3j-sl z5@TRw&}U#^Xk=hvU}9ik(AV0|z^JvGfhlr31M^k}7OmY3tdT-&+ZoumGH_^ZVc-zr z+{VBazMO%>M|&Fs&qfAD27U%L22BQT1||jr1_lNt1`Y-W1_1^Z23`h!1|bF+24My@ z1~CRr1`!4=22ln*25|;MunMOC44Mp#>2o(zm<#j=S(PX$M7&0tlFa`&-0RzV$1}4To3<8Y*844KK z*cntA8QB>Gz{nIF_CFZdelf8AVqo=TXW#~<12YD5NRTq5Gq5nQF)%P#?`DvS6yV;@ zAnmuCK_*g2b~}Td6^kUxCI)%!Z43(GAeN#Ih_#zRDN${@+$&%nUI P$H2r8z`)252qq%{2yPVl literal 0 HcmV?d00001 diff --git a/build/fr/iut_fbleau/HexGame/ArenaMain.class b/build/fr/iut_fbleau/HexGame/ArenaMain.class new file mode 100644 index 0000000000000000000000000000000000000000..18210ecc07ec06d438b476638bff325fd5e17128 GIT binary patch literal 876 zcmX^0Z`VEs1_pBm6D|f}1{rn+SuO@95G}{aAkUz{$^ZrimRt-h42mErC3Xg7kdz7+ z0~doTJA)b*122eH=VIVzPz9;bU}w-|XV79~V6(~0%Pg^DWMJ0N3}a+qan4UkWn|#@ z$;?ajE6q(xEec6Y%1LEpV98C)%wuE_(}?!TN-Rs%&q>Tn*AFf!%FIi*25IC9&Mz%W zPIb!!Ddux5O3h33P0Y;G11aWV5MdByWROlP($6d{iBC()Nlh%(_eia9Ps~l#2diad zkjE|?l$e*2pX-!g!p@-0!=S^U3o=ZPhe4mgfRRA~yB^=nyiDK33a9)M9tJ~*4kJbe zCG3hlQcH_6i%T+-o$^Z<8I-Xr^3BgHNp(&v%E<>CY7FuY4hMt11M)f}g8<0$kdX3E z%1TWxVPud&H6IiJjsc$f0Xd14sYQ$o>;XQGk*-08j0|!<_*7amGAL=_SK$SU6}H5b z6tKT^ajAApDlREXOfCVr1mX&CbV40s#K@qCODB?Q=AzO(21N!T24QeYWMW_h#W({a zgBSw?gDe9h10w?i1FP0{2F8sH3=E76;tUK7Y!E>PP6i1ENvP5a1~vvJ1_lO?-3&~T z+ZmXBw0ASGL~deW6=7i8&cLyafm3@o16SlG2JY<)JP;l)NP0U1AB4vbkrM#%wlN4I vY?EVPXJBApXW(GqV31_sWKd_|VlZXkW^iWUWpHQUV_;;EVqjokVvq&^t?AKH literal 0 HcmV?d00001 diff --git a/javaAPI/fr/iut_fbleau.tar b/javaAPI/fr/iut_fbleau.tar index 909471142c2b6abef177aa99fff06249f1c46680..87874e0cb8dca52eed64048f135bab0eda538b1a 100644 GIT binary patch delta 7607 zcmZpez})bFc|*6Ikcokzp@E^PIfH?rxv{Y+gM#T~N5+)RjP}C3oT(MTnYq4+nR$~H zxLhVDRw+ptn3$V07@8WI7#bRyn3)?f7#JBD85uJu7(jHn`!L#P=Ic3?rlqA8rKW@z zWtOBCS#xFP7UUO|C}bs;CF*77>$zp-q~ehB^q*|UBQrV3L#jR~F)t-Q*P4qoI9AthCzAhD<%PcL4PfN;4O)S;(NUd;B%uNN+&_F>_OGv4Lu5Nl_ZfdcDp@D&cfr74Xab{Jj zf;othnwp|uXk?6OSUpaQ;ij1)8D?mxpsSl)T&7TzT3nh_QmmI;Tm~`}gbz0;6oc%^ zOi9fv$t)~Q)ltYvR46XZECB@uG$e~bDhrBJOH=Z7K>=3}3%T^eEAtMoEIPb0RUx&w zL;=Z=l>EG81yHzwLt6=?3#v&;AuGQ$wJ1NY1T3F>cooR^$@!%PU~5tm^NJO6QWe1B z3i(ATMX3rJC5d?@3WcSqAc^Gsl=@UqoaLxPWK&X$6%q>yQgezG4lhuEBn^eURE6Y> z#Ju#>yb^_i#9~c78~p-1XqDlqQ#CCS~SimKYhmlBq?oR7fmMF3p3)WS%-GD$?>xi}Esc6!P;FN=q_xGK*6c zppliB1NDb`MrvtMszP#pX;ETcNve)Qa;idMX{JI^YF-K`@Ic|6T5)(~aw*u0DVb?$ zhgTM*=9Q#MA%|v8eqK7*W@}JLPF@%-S`WQI(g}=a(er_~w_TLZv~* zgD}{N+{`?fluv$Mx}L9NM0}W|PpB)JrMZa}sOmiZVCvx3mE~uqC={h8=NF}D1cCFG zLXnO_W_@0XLN3U9O(g$;9FUo&pi!h?YpVcJtQYR-r>Rh$nWwFdupFc=HK#aL0bQ|6 zkYl)}0w^p9DX$0Ray=ja;9yM!P#~eJFF}Nlwk^b^sP-d;k#Ay2hF)%Fo(4=z2ckg} zO$Q>hd=u+IS`sTXU_pqfr52JfKz;>B3b+K+&{U{aC`v6UEy@E0ih?%SvkKZ^KUgau z=}E~iP0C3HDGEv~Nk!HNHmF`h6BNh>3ib*HdIkzs3K|d%;ILNER{$&0&;%I?asdcK z6o4(nY9vk*LGgiZWIf141qG;?pco*{Opp$AGf|98EK3KcO$?tyoT-Dw=SXpm5&&G2 ztK+058!wPz1*huC{tNW$!7PY{3JS$ZdZ5BJFGWKMRLK=vDJW^eM4$l)=PSkJDWN3{ zB<5YjE0957GjOF+BEwvKO*FphzRl9*_=f)}$f_I XefcB-AW;` zEZr6qKspM!nR&LVDLM+di50f0Dd5mTqzp1+$=83~PDq zSd?Bo`K^Z%sBiGaAU$&A-J-*BsEtrxwHsWVT5Gnrut^)E_N+TEvf_;=HPUvudiUBU<<0a!HqbmBCs3XKnftCqL5r%7LZtypY>q*j32mj#JM(8eIxl-kJ$J

Heo{yTTU1n>3gT%b zg1qVAEw7>kYBGXalli&2WyQJ>mI9*62PO2$6GLU{6%;@%Hd`eHZG}WV5K}>0K?zdE zKm{Se1rh~?4pb1tRL}-H2JCJqp^*q`oq>I&2=SJJy@C?Rr%-(`KU*m%DS_e=kz1iH zpWMXalGGwln533NTSFR&dLZkN!X8rlf?K*!m0%H2^uV2j+ENEO0i+ZZ&~POsMU^U8 z>NpHPOl}MnF$A?hZ58rT%N0;f)&L11+Wm+Sn4FL%qM2M=rdJN?D`+Tz!ZAKGFD11? z2hx&_FEG>rSG1sVXYz+2*~!}TI49rE;)W23hW>udm>jUs{%$SgKHv zlUS0PSgNCt2N9@%^awOSK~xNC#1&CBkl!Xx z_fkM8U+5(U?bl#Y4vJ~G?a-v60LnmM?JBK_Du9*irKgqzrLuw=4jPja(}b%|paf<^oUN?@sxd(JO+FtgYzb=dB2^h2T(MU&o+G1z0OCvsfXmC_h&rz{fGtHONq*AhoEtAT_xpvn&lB4&qvc-qD8C5e z)KpO5`=%BbC#I)rfFc?eO&~fIqAj>2F*!S=C^0z|ltn=jAY7}EmYJ8BlT(Q$4?<#u z6W9{Tb0r7gL{Lz|0nZ=;a2B;%b0_vv~gE|%k#hQAM^rEHU zpQn(Qs*szXl9`qX>X;WRfOIwOjuZwUJ&+WJ(wP7Wr}sTE)|H6ey-f?^{$vkKI#)Kb7FZ4Ihx z5;aQ|3NnjQ6|6uR4pgov6oEzzG7-71JR>tFRY5~>@__^qHb{qO@J}xS}HuZf;i6k1(nE&0i+rhF9A7~3I#c!3bjBl zJ+mw|GQYG4RQqdyG$JQgkS0*JQ-qWPnZ-V->4`a$SA`NDSQU zha^~#$)MUSsdDng0NKe2VUm+Wl9cqpAr9(mKw=zZw1NV-8v~Y6uv0(@{K<~RqLY97 z3xF&!3dzq`$W6?v1lv-qpaIIV5M9~|N}4(f#re6ZB^jA{=?a;}3gt!ldFhkm!W1We z$YL>r_87pbA)ONJ%@|Pi4r&5GD*DOyJfsA{LZJE|R0DxHlaFO8c!HV$dO4|i=_MK9 z`UAZe2W@xVNpveN=?j0wG(7F z%qDQn2eFDBR_rN+i0ERs3#=NqWvT=$167(3yIdfR3-fE-r)EYMuyhs)3p{6(yJv2`Yp!<1sN! zc=CY|76$?rWu)d5C|RLoCZuT2fH*=BT&sAb<`g*QrMOmPmVm2okn!?3j892TODxSP z0Vl<9eSHN`iJe$t1!^_ts6)nz5=&A+!&&)xd8rCnrNt%Sp;gGBDtJUGM?EvIB(+a$Z~KuDiqZGv4V`R6C5*-&MyFsNfyV(mF6a;7AfSXK~ld0+z8N^ zK0Z^xg&^1vP?ZnqEI>LPa3i2yXoZr@+*Euz0t2i6n62MpVm(=&O=fZmk6=BRhcZzGRta$$ViL+JzXUw)04f`Eax%eF4_4sG z4$#aLXo5o_=kUt(#2kghQiY7f;>4m9&@7WaS3zk~4t!i3Vk*e2)QXbSyp&>v$xr+= z%pt-GMS1Da{v1>jMBFLAL?a*v)DBk2P1S++`ay~(`}nIQ6y>FZdWaycpm=~~Pl&Bp z4FC_yf)XbvdxG?UFev6UK*obwOAu|~KC|Xzu6X6ido8#pFY#CN*VhNN^ou~#1_g&# z6s0PZ3Wm*%#~JxG(bU> zY`FqFer)WJTECO?=StSYo5!G;#l({QA|wS0&{lY6iC#uxv0rLMiH0V!=Rh6=Tcekl zlA@7W0`9A$^v0mU22uzrtRadsi(PXIN-Du-O};o&4%C><^2=8M%YqxT3W;TjnK_`T z9tB9x9MpnME6vHV*97$ bots = new ArrayList<>(); + private FileWriter csvWriter; + + public Arena() { + try { + csvWriter = new FileWriter("arena_results.csv"); + csvWriter.append("Bot 1, Bot 2, Winner\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void addBot(AbstractGamePlayer bot) { + bots.add(bot); + } + + public void run() { + for (int i = 0; i < bots.size(); i++) { + for (int j = i + 1; j < bots.size(); j++) { + AbstractGamePlayer bot1 = bots.get(i); + AbstractGamePlayer bot2 = bots.get(j); + + System.out.println("Running match: " + bot1.getClass().getSimpleName() + " vs " + bot2.getClass().getSimpleName()); + Result result = playMatch(bot1, bot2); + + try { + csvWriter.append(bot1.getClass().getSimpleName() + "," + bot2.getClass().getSimpleName() + "," + result + "\n"); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + try { + csvWriter.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private Result playMatch(AbstractGamePlayer bot1, AbstractGamePlayer bot2) { + IBoard board = new HexBoard(11); // Standard 11x11 Hex board + EnumMap players = new EnumMap<>(Player.class); + players.put(Player.PLAYER1, bot1); + players.put(Player.PLAYER2, bot2); + + Simulation simulation = new Simulation(board, players); // Ensure Simulation is correctly imported + return simulation.run(); + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java new file mode 100644 index 0000000..5dac4fe --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java @@ -0,0 +1,15 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.Player; + +public class ArenaMain { + public static void main(String[] args) { + Arena arena = new Arena(); + arena.addBot(new RandomBot(Player.PLAYER1, 12345L)); // Correct constructor usage + arena.addBot(new MiniMaxBot(Player.PLAYER2)); + arena.addBot(new HeuristicBot(Player.PLAYER1)); + arena.addBot(new MonteCarloBot(Player.PLAYER2)); // Correct constructor usage + + arena.run(); + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java b/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java new file mode 100644 index 0000000..1666346 --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java @@ -0,0 +1,48 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; + +public class HeuristicBot extends AbstractGamePlayer { + + public HeuristicBot(Player me) { + super(me); // Correct constructor usage + } + + @Override + public AbstractPly giveYourMove(IBoard board) { + HexBoard hb = (HexBoard) board; + float bestScore = -Float.MAX_VALUE; + HexPly bestMove = null; + + for (int i = 0; i < hb.getSize(); i++) { + for (int j = 0; j < hb.getSize(); j++) { + HexPly move = new HexPly(hb.getCurrentPlayer(), i, j); + if (hb.isLegal(move)) { + hb.doPly(move); + float score = evaluateBoard(hb); + if (score > bestScore) { + bestScore = score; + bestMove = move; + } + hb.undoPly(); + } + } + } + return bestMove; + } + + private float evaluateBoard(HexBoard board) { + int size = board.getSize(); + int center = size / 2; + float score = 0; + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (board.getPlayerAt(i, j) == Player.PLAYER1) { + score += Math.abs(i - center) + Math.abs(j - center); // Distance from center + } + } + } + return score; + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java index c90002d..d314aa7 100644 --- a/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java +++ b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java @@ -15,6 +15,19 @@ import java.util.Random; * java fr.iut_fbleau.HexGame.HexSimMain * java fr.iut_fbleau.HexGame.HexSimMain --games 10000 --size 7 --seed 123 * java fr.iut_fbleau.HexGame.HexSimMain --games 5000 --size 11 --csv results.csv + * + * À seed identique, la suite de nombres + * pseudo-aléatoires générée est identique, donc les bots "aléatoires" joueront les mêmes coups + * dans le même ordre (tant que le code et l'ordre des appels à Random ne changent pas).

+ * + * Intérêt : + * + * Reproductibilité : relancer exactement la même simulation pour déboguer / analyser. + * Comparaison équitable : comparer 2 bots sur les mêmes tirages aléatoires. + * Si aucun seed n'est fourni, on utilise généralement l'heure courante, ce qui rend chaque exécution différente.

+ * + * long seed; + * */ public class HexSimMain { @@ -170,7 +183,7 @@ public class HexSimMain { // ex: "7 10000" if (isInt(s)) { int v = Integer.parseInt(s); - if (a.size == 7) a.size = v; + if (a.size == 11) a.size = v; else a.games = v; } else { System.err.println("Unknown arg: " + s); diff --git a/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java new file mode 100644 index 0000000..bb71baf --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java @@ -0,0 +1,89 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; + +public class MiniMaxBot extends AbstractGamePlayer { + + private int MAXDEPTH = 5; + + public MiniMaxBot(Player me) { + super(me); // Correct constructor usage + } + + @Override + public AbstractPly giveYourMove(IBoard board) { + HexBoard hb = (HexBoard) board; + float bestScore = -Float.MAX_VALUE; + HexPly bestMove = null; + + for (int i = 0; i < hb.getSize(); i++) { + for (int j = 0; j < hb.getSize(); j++) { + HexPly move = new HexPly(hb.getCurrentPlayer(), i, j); + if (hb.isLegal(move)) { + hb.doPly(move); + float score = minimax(hb, MAXDEPTH, -Float.MAX_VALUE, Float.MAX_VALUE, true); + if (score > bestScore) { + bestScore = score; + bestMove = move; + } + hb.undoPly(); + } + } + } + return bestMove; + } + + private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing) { + if (depth == 0 || board.isGameOver()) { + return evaluateBoard(board); + } + + if (isMaximizing) { + float bestScore = -Float.MAX_VALUE; + for (int i = 0; i < board.getSize(); i++) { + for (int j = 0; j < board.getSize(); j++) { + HexPly move = new HexPly(board.getCurrentPlayer(), i, j); + if (board.isLegal(move)) { + board.doPly(move); + float score = minimax(board, depth - 1, alpha, beta, false); + bestScore = Math.max(bestScore, score); + alpha = Math.max(alpha, bestScore); + if (beta <= alpha) break; // Pruning + board.undoPly(); + } + } + } + return bestScore; + } else { + float bestScore = Float.MAX_VALUE; + for (int i = 0; i < board.getSize(); i++) { + for (int j = 0; j < board.getSize(); j++) { + HexPly move = new HexPly(board.getCurrentPlayer(), i, j); + if (board.isLegal(move)) { + board.doPly(move); + float score = minimax(board, depth - 1, alpha, beta, true); + bestScore = Math.min(bestScore, score); + beta = Math.min(beta, bestScore); + if (beta <= alpha) break; // Pruning + board.undoPly(); + } + } + } + return bestScore; + } + } + + private float evaluateBoard(HexBoard board) { + int size = board.getSize(); + int center = size / 2; + int score = 0; + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + if (board.getPlayerAt(i, j) == Player.PLAYER1) { + score += Math.abs(i - center) + Math.abs(j - center); // Distance from center + } + } + } + return score; + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java b/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java new file mode 100644 index 0000000..55a4e43 --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java @@ -0,0 +1,59 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; + +import java.util.Random; + +public class MonteCarloBot extends AbstractGamePlayer { + + private static final int SIMULATION_COUNT = 1000; + + public MonteCarloBot(Player me) { + super(me); // Correct constructor usage + } + + @Override + public AbstractPly giveYourMove(IBoard board) { + HexBoard hb = (HexBoard) board; + float bestScore = -Float.MAX_VALUE; + HexPly bestMove = null; + + for (int i = 0; i < hb.getSize(); i++) { + for (int j = 0; j < hb.getSize(); j++) { + HexPly move = new HexPly(hb.getCurrentPlayer(), i, j); + if (hb.isLegal(move)) { + hb.doPly(move); + float score = monteCarloSimulation(hb); + if (score > bestScore) { + bestScore = score; + bestMove = move; + } + hb.undoPly(); + } + } + } + return bestMove; + } + + private float monteCarloSimulation(HexBoard board) { + RandomBot simBot = new RandomBot(Player.PLAYER1, new Random().nextLong()); + HexBoard simBoard = board.safeCopy(); + int wins = 0; + int simulations = 0; + + for (int i = 0; i < SIMULATION_COUNT; i++) { + while (!simBoard.isGameOver()) { + AbstractPly move = simBot.giveYourMove(simBoard); + simBoard.doPly(move); + } + + if (simBoard.getResult() == Result.WIN) { + wins++; + } + simulations++; + simBoard = board.safeCopy(); // Reset the board for the next simulation + } + + return (float) wins / simulations; + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java b/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java index 0bc5843..b670ada 100644 --- a/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java +++ b/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java @@ -10,9 +10,6 @@ import java.util.Iterator; import java.util.List; import java.util.Random; -/** - * Bot non intelligent : joue un coup légal au hasard. - */ public class RandomBot extends AbstractGamePlayer { private final Random rng; @@ -28,7 +25,6 @@ public class RandomBot extends AbstractGamePlayer { @Override public AbstractPly giveYourMove(IBoard board) { - // On récupère tous les coups légaux via l'itérateur fourni par le plateau. List legal = new ArrayList<>(); Iterator it = board.iterator(); while (it.hasNext()) { diff --git a/javaAPI/fr/iut_fbleau/HexGame/Simulation.java b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java new file mode 100644 index 0000000..c18f87b --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java @@ -0,0 +1,273 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; +import java.util.EnumMap; +import java.util.LinkedList; + + +public class Simulation extends AbstractGame { + + //ATTRIBUTS + private HexPly bestmove; + private float bestoutcome; + private int MAXDEPTH = 9; + private int EVALDEPTH = 10; + private LinkedList taken = new LinkedList(); + + //ATTRIBUTS QUE JE NE VOUDRAIS PAS CRÉER IDÉALEMENT + private IBoard simCurrentBoard; + private EnumMap simmapPlayers; + + //CONSTRUCTEUR + public Simulation(IBoard b, EnumMap m){ + super(b, m); + simCurrentBoard = b; + simmapPlayers = m; + } + + //METHODES + /*Le jeu de Hex ne peut jamais finir avec le résultat null. En utilisant cette propriété, on peut avoir cet algorithme simplifié du monte-carlo*/ + private float MonteCarlo(HexBoard position){ + RandomBot simplay = new RandomBot(); + HexBoard simpos = position.safeCopy(); + LinkedList ctaken = taken; + HexPly testmove; + float wins = 0; + float losses = 0; + + for(int i=0; i=losses){ + return losses/wins; + } else { + return -(wins/losses); + } + + } + + private float explMAX(HexBoard position, int depth){ + if (position.getResult()==Result.LOSS) { + return -1.0f; + } else if (position.getResult()==Result.WIN){ + return 1.0f; + } else if (depth==MAXDEPTH) { + return MonteCarlo(position); + } else { + float bestcase = -1.0f; + HexPly bestcasemove; + HexPly testmove; + for (int i=0; i= bestcase) { + //System.out.println(" MAX new best case"); + bestcase = val; + bestcasemove = testmove; + if (depth==0) { + this.bestoutcome = bestcase; + this.bestmove = bestcasemove; + } + } + position.undoPly(); + taken.remove(t); + } + } + } + return bestcase; + } + } + + + private float explMIN(HexBoard position, int depth){ + if (position.getResult()==Result.LOSS) { + return -1.0f; + } else if (position.getResult()==Result.WIN){ + return 1.0f; + } else if (depth==MAXDEPTH) { + return MonteCarlo(position); + } else { + float bestcase = 1.0f; + HexPly bestcasemove; + HexPly testmove; + for (int i=0; i= bestcase) { + //System.out.println(" MAX new best case"); + bestcase = val; + bestcasemove = testmove; + if (depth == 0) { + this.bestoutcome = bestcase; + this.bestmove = bestcasemove; + } + if (bestcase >= B) { + return bestcase; + } + } + position.undoPly(); + taken.remove(t); + } + } + } + return bestcase; + } + } + + +private float explMINAB(HexBoard position, int depth, float A, float B){ + if (position.getResult() == Result.LOSS) { + return -1.0f; + } else if (position.getResult() == Result.WIN) { + return 1.0f; + } else if (depth == MAXDEPTH) { + return MonteCarlo(position); + } else { + float bestcase = B; + HexPly bestcasemove; + HexPly testmove; + for (int i = 0; i < position.getSize(); i++) { + for (int j = 0; j < position.getSize(); j++) { + if (depth == 0) { + //System.out.println("MIN New Line :"); + } + Integer[] t = new Integer[]{i, j}; + testmove = new HexPly(Player.PLAYER2, i, j); + if (!taken.contains(t) && position.isLegal(testmove)) { + //System.out.println(" MIN test move : "+Integer.toString(i)+","+Integer.toString(j)); + taken.add(t); + position.doPly(testmove); + float val = explMAXAB(position, depth + 1, A, bestcase); + if (val <= bestcase) { + //System.out.println(" MIN new best case"); + bestcase = val; + bestcasemove = testmove; + if (depth == 0) { + this.bestoutcome = bestcase; + this.bestmove = bestcasemove; + } + if (bestcase <= A) { + return bestcase; + } + } + + position.undoPly(); + taken.remove(t); + } + } + } + return bestcase; + } +} + + + + + private AbstractPly GiveBestMove(IBoard board) { + if (!(board instanceof HexBoard)) { + throw new IllegalArgumentException("Ce joueur attend un HexBoard."); + } + HexBoard hb = (HexBoard) board; + float bestcase; + if(hb.getCurrentPlayer()==Player.PLAYER1){ + bestcase = explMAXAB(hb, 0, -1.0f, 1.0f); + } else { + bestcase = explMINAB(hb, 0, -1.0f, 1.0f); + } + return this.bestmove; + } + + @Override + public Result run(){ + while(!simCurrentBoard.isGameOver()) { + AbstractGamePlayer player = simmapPlayers.get(simCurrentBoard.getCurrentPlayer()); + IBoard board = simCurrentBoard.safeCopy(); + AbstractPly ply = GiveBestMove(board); + HexPly concretePly = (HexPly) ply; + + if (simCurrentBoard.isLegal(ply)) { + simCurrentBoard.doPly(ply); + taken.add(new Integer[]{concretePly.getRow(), concretePly.getCol()}); + System.out.println("Player "+player+" goes ("+concretePly.getRow()+","+concretePly.getCol()+")"); + } + else throw new IllegalStateException("Player "+ player + " is a bloody cheat. He tried playing : "+concretePly.getRow()+","+concretePly.getCol()+" I give up."); + } + return simCurrentBoard.getResult(); + } + + +}