pax_global_header00006660000000000000000000000064132445773120014522gustar00rootroot0000000000000052 comment=b4e08db7d5307730f4e10d701cc0776b6f79a77b mrboom-libretro-4.4/000077500000000000000000000000001324457731200145045ustar00rootroot00000000000000mrboom-libretro-4.4/.travis.yml000066400000000000000000000012571324457731200166220ustar00rootroot00000000000000language: generic os: linux dist: trusty sudo: required addons: apt: packages: - g++-7 sources: - ubuntu-toolchain-r-test env: global: - CORE=mrboom - COMPILER_NAME=gcc CXX=g++-7 CC=gcc-7 matrix: - PLATFORM=linux_x64 - PLATFORM=ngc - PLATFORM=wii - PLATFORM=wiiu before_script: - pwd - mkdir -p ~/bin - ln -s /usr/bin/gcc-7 ~/bin/gcc - ln -s /usr/bin/g++-7 ~/bin/g++ - ln -s /usr/bin/cpp-7 ~/bin/cpp - export PATH=~/bin:$PATH - ls -l ~/bin - echo $PATH - g++-7 --version - g++ --version script: - cd ~/ - git clone --depth=50 https://github.com/libretro/libretro-super - cd libretro-super/travis - ./build.sh mrboom-libretro-4.4/Assets/000077500000000000000000000000001324457731200157465ustar00rootroot00000000000000mrboom-libretro-4.4/Assets/mrboom.6000066400000000000000000000024551324457731200173360ustar00rootroot00000000000000.TH mrboom 6 "March 27 2017" .SH NAME mrboom \- 8 player Bomberman .SH SYNOPSIS .B mrboom .RI [ options ] .SH DESCRIPTION This manual page documents briefly the .B mrboom command. .PP \fBmrboom\fP is an SDL2 version of the original 1999 version of Mr.Boom .SH USAGE The goal of the game is to bomb away your enemies and other players. Bomberman is controlled with the keyboard or with (up to 8) joypads. .IP "Keys" Space - Add a bomberman bot .br Return - Start a game .br P or Pause/Break - Pause .br ESC - Quit .IP "1st player" Left - Left .br Right - Right .br Up - Up .br Down - Down .br Right Ctrl, End, Keypad 0, Right GUI (Windows) or Right Command (Apple) - Lay bomb .br Right Alt, Keypad Dot or PageDown - Ignite bomb .br Right Shift, Keypad Enter or Home - Jump .IP "2nd player" A - Left .br D - Right .br W - Up .br S - Down .br Left Ctrl, Left GUI (Windows) or Left Command (Apple) - Lay bomb .br Left Alt - Ignite bomb .br Left Shift - Jump .SH OPTIONS .TP .B \-f, \-\-fx FX volume: with from 0 to 10. .TP .B \-h, \-\-help Show summary of options. .TP .B \-m, \-\-nomonster No monster mode. .TP .B \-s, \-\-sex Sex team mode. .TP .B \-c, \-\-color Color team mode. .TP .B \-n, \-\-noautofire No autofire for bomb drop. .TP .B \-z, \-\-nomusic No music. .TP .B \-v, \-\-version Show version of program. mrboom-libretro-4.4/Assets/mrboom.appdata.xml000066400000000000000000000017111324457731200213740ustar00rootroot00000000000000 mrboom MIT MIT Mr.Boom Mr.Boom is an 8 player Bomberman clone

The goal of the game is to bomb away your enemies and other players. Bomberman is controlled with the keyboard or with (up to 8) joypads.

mrboom.desktop http://mrboom.mumblecore.org/mrb5.png http://mrboom.mumblecore.org GNOME mrboom

Added new sound effects

mrboom-libretro-4.4/Assets/mrboom.desktop000066400000000000000000000002721324457731200206350ustar00rootroot00000000000000[Desktop Entry] Name=Mr.Boom Comment=8 player Bomberman clone Exec=mrboom Icon=mrboom.png Terminal=false Categories=Game;ArcadeGame;ActionGame; Keywords=bomberman;bomb; Type=Application mrboom-libretro-4.4/Assets/mrboom.ico000077500000000000000000000565561324457731200177610ustar00rootroot00000000000000 hV   F00 % D(    %K G J FO G7aU<Ex6dX 6h5]&GhOUeNMamu4G^i&_Y=(|t;zYt>5FHD t56P|a*MuG F9G}(PrCdtZ'yRZVX9l YIUrxru0I` $b<&Sv"f<KWM[?HE];~QC~4+e@n L g-5fj]:sHp*0N!%ekCkjhBjHl4D"'FXXwCFd4Y QxdP50zE QlwFf2ay_ORf@!iPjQ jN k: D$"PWdEg0Etx`t~(1GB`<})#@#>!,9UM]mm*o,yr,tL_h"C_+o/|y/mN[b#Ib0s  ; 8 E$Ga6}"%B$` YOl~>Pf BajQyf7999999999996LwCl )$((%!'(((" -Q{&KE}||XEr{zz,\ 3Y!9;n:o*X 25h9n:n:n;FnW~DAU@[g ;dX} ,Bli-T@?XyXoy6^=pRiOIX. H+eKuVTaXVf,2cw +_X}%>TZiMM\ e[8n Mg-/E,=Q,>R,=/Un9656 .! .656664 =e|!?@V\`r@Ve&5J">?Vabt`asS\m;Sbz z t P J J < /Z|~[~,Ajy#mÿ6^)t|rr])7þ5@jxmþ4^)s|rr])64@jIqEwc??Sc=2J g x x x x x r O I I ;31AjCk.h]g"""""""""""!/~-Bku3P9;O9N_:9J}qy.$6|7y/QP{)MilakixtmtGViAi:9?q3{#"1-EmHp7?r5zzq7?T?@R=Rb=Rb=Rb=Sb>IY?7J?6I7?Upk7z@s?sK)N45555665)N.O35PNG  IHDR\rfIDATxyg}jW+ieK,| َ  )*!"*T!M*U@Q@b!0زd[ֹ:Vk7ly{Z;3>-S{o#]ţ@$b )D"@$b )D"@$b )D"@$b )D"@$b )D"@$b )D"@$b )D"@$b )D"S^.ߖ^jTZ@.ZV@_{>k j臤O zpA .{$ }foW\\Ki<<w*j,۶v\?n0%8<ў]L $^iog1ͷ_'/ɣ@$b ՚=2//׶-F_yPHZw1uuߑ{ i@$b )D":jTR{HדoӭA'`J]Yb'ֹA nFPq?~Zq#,_9D)纪IrZXˊ.SͅϨpӯ` +~mS% >ǧp>{pc83YW7;a֟{ﺖˍInm%@AS_xs&?2 1NJS>P9fadt)CC jbۨ偬fֳdoٷLh@Ae '^+]zIJ{?ןn_{@$b )D"N+v-Uwu_7YԶȹ>MixQEQcv?`Ņ`U @[x(Zh:υ9[\V.?;wZٰp׍5}:FVy?sc?w74M&>?^VT=tl@h)@/NT6=%M$I~P rLyFTjN1mP* uo@u>?ӊJX{FD"@$b  IY;!>nf iTDu͏rfT$|zx{}ix[nt74$>$)d` d{Y1z*ʼOڰV*!6#(-wPgxk 'H8$_~:fOՑw fڗb,`Zs-`]GZu" GuGNϛl>i0/{٘%4X%z4ާ4gu𭺚1NsfmH ѯTaNU=^J )D"@$b\O|ɆGG/~a Ó)Ά% <$3;Iu1]Ov!@ ]|pz]Ȃk_N&x-~ ߿30A$I抗ߞ͆"]Eb1lyyouV*c+9 -_sןʒʹq|&-[#;pY W?bjpVc&]?-_//x<犋q9{&ޓ r3}I'Z~ySSW4ưg>߾=48,8B7^x)**@W֞kW弧vJ7m< `&Jz;,ܱQ=9]ȇ5X/nW>񑜓goaߕi: X⟥ 0 C^;Ywb*-=\bmhбrl=O[{AaIvFmɦ:{~5/@A{6CJX}gnZ<(嗿c;ݩ+3sʹejS / rleM3c ZXFis ͯs[${5vjcc ք&%" |=I,Ə6\N2yC'e3CϿdO<<LU A&/P:H6Oڹ8:=!f&~^ec8ph 6#UBQPpo۞ə;ݟd_w>p&a՜mSw7TkpN(5bJ0tS9|9u+ =kH<@3ǦTJQPPaviFf`,TDNU|ĩNW>ܖٿ8@M48Mq "" @ 1H"S' όgm]׺CHJ$AX 9n.+=t8o` ͛d&ϤpldO!#`fw`Ff=kYCT}\ԃ7!k\UWqV*i '8vT9p*o'% j.@mH Mm? .<ϱhmn=%aШ?BX(uebP' 2lI'I 1H"SD,Q[pM{CHMjO5ɖ*g &ecIb706 &P30z ߿^`# m,y3X=А@ o7 OOd}܍]`5PX5{ fBYJk?068ߨd$`Z9g`K>inM~q: xmsT9lĂaY}J_{k=ҧ9㛪8*)`-${ S'm-H"SDL (“5; P%XUu)RS]h6g j2b44unsvtcx _Ȇg<M'wS#t襩㮛CpU.[0cH1}ޛ 8 p3i{37(\.bLW67߽*|w<UW0331[%##Ň>ĦM},`j> JJ ?%bϳfJC \q͓d'}Yvr?#ٶ4i IzN@)D"@$b iL>nk-I)HRWd1-Z]CRJB^w}:t"@;ySxsLόZI9xp$_w֬yC:p 'Y<`,mSRtF"V´b#o9UMo Gڬ3[>"1H"SDL 1@jIמ-Ҏ'9#jE=ٕ pNc|E>Ȑy=u*,a K8Q"vzn =^z l{>~-M0x?ǼMlf{)SJ^FֲÄr_X-ٴjcpo,&w YDs:)`OX)Ai2>W<;ص`1Q[DL 1H"St)M-(iZˎ&C{n-V<Uu(5,he|ZرC=|6c'/ 0k±fV7owscW…Eە |gGWBdorg 纤Tk۷Vqoc׿mw޾xF#ZQQBSt*"m@$b )D"ƀtθcVZł <)g= ?wn$~By>\h3φ5+"<^ZV3UBh@/QőWN@)D"@$b X{y= W!-Zt.*pxtWDZ{xjX5. P@k*CE>'+A:DL 1H"SDL 1H"SDL 1H"SDL 1H"SDL 1H"SDL 1H"SDL 1H"SD(0XIENDB`mrboom-libretro-4.4/Assets/mrboom.png000066400000000000000000000305221324457731200177510ustar00rootroot00000000000000PNG  IHDR+gAMA a cHRMz&u0`:pQ<bKGDC pHYs  tIME #7J0AIDATx}l}93(Q, -;-R[5q!$ F*$)ԺNQT&NSİc m8 iypIDIy\#-C(Oawg1KR$e /99|״'@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @,Zkж^!Zc"'M!pX.oqԻ!916Y WXoo{W|#$_o=c/a%\՘|?zH&zf^s.dz\Lq:;[JPPPPPPPPPPPPPPPP,[8U${Gtj/&O%o%6GMr]:g*{IkNl^vdu@-O2qbk yKې\:L> g[&y7xn;޹ W wŢL|_]|est;o/&O>i-ޕ仲κ^}Ptpѽ^'-~oDJKzH>/7e`w4%}ݜ;fÅ _8$CE>00?8|>i?Wcr߿HguDuܚ~u|Д8 W)'}K>}Ozι9笏[OɴMgv(]qWɷG,n) :^)Llv4 f3f썛ܒ*  @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @N9vznǴ2͛LS54oZOyf7ߓ C2MmYb!O2qb1">^E3]Q34'Y$w߳Q+i;o=|k >P\J$?䰓@ғyJ?oUЁgo;MǓo$y8ɣ m^ה黳خ[Ov{;3jjzH)r,$g[׈^D[>35nXdȐdh$:<u0=s.]                X7 CE% 0 |*ɢ-{Kڛ̭pUcGgIz&Od9?XͦLKo$מ \L[ZO}s/}\-%H!ϛߗ$_77|<ߟ+kNxCdjZ$RB%I.&ɧlZ/I2'{oυ'<_|27yJy3fךr}'~5{s2e?34%wܒd\ 'ZGNtw< {O>iv:=7͙CNL&|f8h=%"//$8YMBl0U @S_\ Mq#\>_KIdh&xk_@z8=$o;zȉɏ'ym)$wcwݒG&Ӵɹ7ܑS7eL9:~:/>ahq X%{kO${ҾT^@O:YS2λMbݝ4eض9LS Cal2g'^M֔̐~?{zn68`{妛kif9sks[&\m9pMX\p).&Caran͹x۹և&Ao=dg O !& asr,Z$_%=>lєK)ܱ6c2$7>0Io˯N"                XXpb,>zK' Øban!q̐dh}`WzvznǴ2͛LmOǻ=ȴ w`%{$Ü\{W~/~CqL_Ͽ{724Ꭓr?ædJr[7+H/SY̿zI${9>j=mͷ֛oAX.yoYAcr S\~2I{ȟE*[ZI2}{.~rI6Iޞ$[ *I[2{ ]wO9{7[skzsrDzNi} HŔgA :r{I'Y S6mWb0,s&}\gVo{~v7CiJOm;96{(                e&lLX&Z&oKom#I#tH8': ln=B(@(@(@(@(@(@(@(@(@(@(@(@(@(@(@!bȸhq0 g0fÎ,1<6qxe8|l֛s[K\lrK͸9l$%tdjT^<m7Inӑe'ZϴptIvz|[4osgMg2S9ezltq#?Z0gn=ʻ.)Im=2%k=2\O>?ߗ#l>]ph#xYnH#N<8ޙ݋;+ѣ^.IWٝ{k/+@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @l=$k=2&o=2C'IVS_fJM4'{3pmZe9HrK/A^by5l[^_gX`~yTQ)9\'8LퟪdmEƆ!Iޒ<\i$C3/r/}|)&ycޔ3"GٴUp?,]8L1;[I7d>ϓwCM~7;$7q!mwhSIZ2Wnɢ~ ߘw$9n=Pa/A}>Vfvgݧ&ttO[z3qo$s?fΜ~xS(@(@(@(@(@(@(@(@(@(@(@(@(@(@(@(`zZ82~E)IVYutejĘt]Nev^^=Y&90~ЇS!{K{zzУa?s@vq=X$$C$g[[SO['y^O@KN'ٶՠKX$/wҩ.k*ŧ$g6ٽ^/                Xgw~-k=2I.qU$G\bvWZ<ҘLӋ׭W4x9y?do I7}D$y|g6m9E¿4:㐧8v.&y?uWzNW t^gŘͺO=W=9::x/a:&Y<8u@(@(@(@(@(@(@(@(@(@(@(@(@(@(@(@(@k=l2 C8fj}8^'?XezZ!Ͷ !)$ۏHѽf;Md<'3/Xar[!'nMy-dɐ!Ggrg3 nL0$O8VI>v}}E?oHwI|6tjÕzzp AO                Xtjh=CC2u-!}mLGp:Q#^ztbMeg 'o]umHrMgx&ycޔ39 W`ΜP$Gdi3s2u NSe}M{Z=3j#y$C$$hl1xLEH$| Gt껾5Lrz WCP:\g/7 wc;4Gdٶ©1Rg7Щ In8zKG                XXpb,1CE>{7fE/̘fh=ScuWxG|jĶ-@7|dO<I\Q֭pc|.O\!'^ #define IFTRACES ((debugTracesPlayer(_playerIndex)) && (traceMask & DEBUG_MASK_GRIDS)) Bot::Bot(int playerIndex) { _playerIndex = playerIndex; initBot(); } void Bot::initBot() { calculatedBestCellToDropABomb = 0; calculatedBestCellToPickUpBonus = 0; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { bestExplosionsGrid[i][j] = 0; noDangerGrid[i][j] = false; } } _direction1FrameAgo = button_error; _direction2FramesAgo = button_error; _shiveringCounter = 0; } bool Bot::cellSafe(int cell) { int cellX = CELLX(cell); int cellY = CELLY(cell); return(!dangerGrid[cellX][cellY] && !flameGrid[cellX][cellY] && !monsterIsComingGrid[cell]); } int Bot::bestBonusCell() { if (!isAboutToWin() && travelGrid.cost(calculatedBestCellToPickUpBonus) != TRAVELCOST_CANTGO && cellSafe(calculatedBestCellToPickUpBonus)) { return(calculatedBestCellToPickUpBonus); } else { return(-1); } } int Bot::scoreForBonus(Bonus bonus, int x, int y) { if (!bonusPlayerWouldLike(_playerIndex, bonus)) { if (((bonus == bonus_remote) || (bonus == bonus_egg) || (bonus == bonus_roller)) && (isPlayerFastestToCell(_playerIndex, x, y))) { if (isSameTeamTwoFastestToCell(x, y)) { if (tracesDecisions(_playerIndex)) { log_debug("%d we are the fastest 2 to bonus %d (%d/%d) ignoring\n", _playerIndex, bonus, x, y); } return(0); } else { if (tracesDecisions(_playerIndex)) { log_debug("%d should pick bonus %d (%d/%d) for safety reason\n", _playerIndex, bonus, x, y); } } } } int distance = travelSafeGrid.cost(x, y); if (distance == TRAVELCOST_CANTGO) { return(0); } switch (bonus) { case bonus_push: case bonus_remote: case bonus_bulletproofjacket: distance /= 4; break; case bonus_egg: case bonus_heart: case bonus_roller: distance /= 8; break; default: break; } if (distance < 100) { return(TRAVELCOST_CANTGO - distance); } else { if (isPlayerFastestToCell(_playerIndex, x, y)) { return(TRAVELCOST_CANTGO - distance); } } return(0); } uint8_t Bot::calculateBestCellToPickUpBonus() { int bestCell = -1; int bestScore = 0; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { Bonus bonus = bonusInCell(i, j); if (bonus != no_bonus) { int score = scoreForBonus(bonus, i, j); if (score > bestScore) { int cellIndex = CELLINDEX(i, j); bestCell = cellIndex; bestScore = score; } } } } if (tracesDecisions(_playerIndex)) { log_debug("BOTTREEDECISIONS/calculateBestCellToPickUpBonus: %d/%d:bestCell=%d bestScore=%d\n", frameNumber(), _playerIndex, bestCell, bestScore); } return(bestCell); } int noise(int player, int x, int y) { return((x + player + y) % nb_dyna); } int Bot::bestCellToDropABomb() { int bestCell = -1; int bestScore = 0; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { int score = bestExplosionsGrid[i][j] * 128; if (score < 0) { score = 0; } if (score) { score += noise(_playerIndex, i, j); } int travelCost = 1 + travelGrid.cost(i, j) / 16; if (score > travelCost) { score = score / travelCost; } if (score > bestScore) { bestCell = CELLINDEX(i, j); bestScore = score; } } } return(bestCell); } int Bot::bestSafeCell() { int bestCell = cellPlayer(_playerIndex); int bestScore = 0; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (!somethingThatIsNoTABombAndThatWouldStopPlayer(i, j)) { int score = TRAVELCOST_CANTGO - travelGrid.cost(i, j); if (bestExplosionsGrid[i][j]) { score += TRAVELCOST_CANTGO; } if ((score > bestScore) && cellSafe(CELLINDEX(i, j))) { int cellIndex = CELLINDEX(i, j); bestCell = cellIndex; bestScore = score; } } } } return(bestCell); } #define MAX_PIXELS_PER_FRAME 8 bool Bot::isSomewhatInTheMiddleOfCell() { int x = GETXPIXELSTOCENTEROFCELL(_playerIndex); int y = GETYPIXELSTOCENTEROFCELL(_playerIndex); return((x > -MAX_PIXELS_PER_FRAME / 2) && (x < MAX_PIXELS_PER_FRAME / 2) && (y < MAX_PIXELS_PER_FRAME / 2) && (y > -MAX_PIXELS_PER_FRAME / 2)); } bool Bot::amISafe() { return(cellSafe(cellPlayer(_playerIndex))); } bool Bot::isThereABombUnderMe() { int x = xPlayer(_playerIndex); int y = yPlayer(_playerIndex); return(bombInCell(x, y)); } int Bot::howManyBombsLeft() { return(nbBombsLeft(_playerIndex)); } void Bot::printGrid() { if (IFTRACES) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { db brickKind = m.truc[i + j * grid_size_x_with_padding]; if (monsterInCell(i, j) || playerInCell(i, j)) { if (monsterInCell(i, j)) { log_debug(" 8( "); } else { log_debug(" 8) "); } } else { switch (brickKind) { case 1: log_debug("[======]"); break; case 2: log_debug("(******)"); break; default: if (no_bonus != bonusInCell(i, j)) { log_debug(" (%d) ", bonusInCell(i, j)); } else { log_debug(" "); } break; } } } log_debug("\n"); } log_debug("bestExplosionsGrid player %d\n", _playerIndex); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { log_debug("%04d", bestExplosionsGrid[i][j]); if (dangerGrid[i][j]) { log_debug("x"); } else { log_debug("_"); } } log_debug("\n"); } log_debug("travelCostGrid %d/%d cell:%d x:%d y:%d adderX=%d adderY=%d\n", frameNumber(), _playerIndex, cellPlayer(_playerIndex), xPlayer(_playerIndex), yPlayer(_playerIndex), GETXPIXELSTOCENTEROFCELL(_playerIndex) * framesToCrossACell(_playerIndex) / CELLPIXELSSIZE, GETYPIXELSTOCENTEROFCELL(_playerIndex) * framesToCrossACell(_playerIndex) / CELLPIXELSSIZE); travelGrid.print(); log_debug("travelCostSafeGrid\n"); travelSafeGrid.print(); log_debug("%d flameGrid player %d\n", m.changement, _playerIndex); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { log_debug("%04d ", flameGrid[i][j]); } log_debug("\n"); } log_debug("hasRemote=%d isCellCulDeSac=%d flamesize:%d lapipipino:%d lapipipino5:%d amISafe=%d\n", hasRemote(_playerIndex), isCellCulDeSac(xPlayer(_playerIndex), yPlayer(_playerIndex)), flameSize(_playerIndex), m.lapipipino[_playerIndex], m.lapipipino5[_playerIndex], amISafe()); } } void Bot::stopWalking() { mrboom_update_input(button_up, _playerIndex, 0, true); mrboom_update_input(button_down, _playerIndex, 0, true); mrboom_update_input(button_left, _playerIndex, 0, true); mrboom_update_input(button_right, _playerIndex, 0, true); } void Bot::startPushingRemoteButton() { mrboom_update_input(button_a, _playerIndex, 1, true); } void Bot::stopPushingRemoteButton() { mrboom_update_input(button_a, _playerIndex, 0, true); } void Bot::startPushingJumpButton() { mrboom_update_input(button_x, _playerIndex, 1, true); } void Bot::stopPushingJumpButton() { mrboom_update_input(button_x, _playerIndex, 0, true); } void Bot::startPushingBombDropButton() { pushingDropBombButton = true; mrboom_update_input(button_b, _playerIndex, 1, true); } void Bot::stopPushingBombDropButton() { pushingDropBombButton = false; mrboom_update_input(button_b, _playerIndex, 0, true); } #ifdef DEBUG extern int howToGoDebug; #endif bool Bot::walkToCell(int cell) { #ifdef DEBUG howToGoDebug = 0; #endif bool shouldJump = false; enum Button direction = howToGo(_playerIndex, CELLX(cell), CELLY(cell), travelSafeGrid, shouldJump); if (direction == button_error) { direction = howToGo(_playerIndex, CELLX(cell), CELLY(cell), travelGrid, shouldJump); } #ifdef DEBUG walkingToCell[_playerIndex] = cell; if (tracesDecisions(_playerIndex)) { char *directionText = (char *)"?"; switch (direction) { case button_up: directionText = (char *)"button_up"; break; case button_down: directionText = (char *)"button_down"; break; case button_left: directionText = (char *)"button_left"; break; case button_right: directionText = (char *)"button_right"; break; default: break; } log_debug("\nBOTTREEDECISIONS: %d/%d:howToGo to %d:%s %d/%d shouldjump=%d\n", frameNumber(), _playerIndex, cell, directionText, GETXPIXELSTOCENTEROFCELL(_playerIndex), GETYPIXELSTOCENTEROFCELL(_playerIndex), shouldJump); } #endif stopWalking(); if (shouldJump) { startPushingJumpButton(); } if (hasInvertedDisease(_playerIndex)) { switch (direction) { case button_up: direction = button_down; break; case button_down: direction = button_up; break; case button_left: direction = button_right; break; case button_right: direction = button_left; break; default: break; } } mrboom_update_input(direction, _playerIndex, 1, true); #define MAX_SHIVERING 3 if ((_direction2FramesAgo == direction) && (_direction1FrameAgo != direction)) { _shiveringCounter++; if (_shiveringCounter >= MAX_SHIVERING) { if (tracesDecisions(_playerIndex)) { log_debug("BOTTREEDECISIONS/shivering on bot: %d/%d ->startPushingBombDropButton\n", frameNumber(), _playerIndex); } startPushingBombDropButton(); } if (_shiveringCounter >= MAX_SHIVERING * 2) { if (tracesDecisions(_playerIndex)) { log_debug("BOTTREEDECISIONS/shivering on bot: %d/%d ->startPushingJumpButton\n", frameNumber(), _playerIndex); } startPushingJumpButton(); } if (_shiveringCounter >= MAX_SHIVERING * 3) { if (tracesDecisions(_playerIndex)) { log_debug("BOTTREEDECISIONS/shivering on bot: %d/%d ->startPushingRemoteButton\n", frameNumber(), _playerIndex); } startPushingRemoteButton(); } } else { _shiveringCounter = 0; } _direction2FramesAgo = _direction1FrameAgo; _direction1FrameAgo = direction; return(direction != button_error); } int Bot::getCurrentCell() { int x = xPlayer(_playerIndex); int y = yPlayer(_playerIndex); return(CELLINDEX(x, y)); } void Bot::printCellInfo(int cell) { log_debug("printCellInfoBot Cell:%d Bot:%d: travelCostGrid=%d bestExplosionsGrid=%d flameGrid=%d dangerGrid=%d\n", cell, _playerIndex, travelGrid.cost(cell), bestExplosionsGrid[CELLX(cell)][CELLY(cell)], flameGrid[CELLX(cell)][CELLY(cell)], dangerGrid[CELLX(cell)][CELLY(cell)]); } mrboom-libretro-4.4/ai/Bot.hpp000066400000000000000000000037001324457731200163320ustar00rootroot00000000000000#pragma once #include "MrboomHelper.hpp" #include "GridFunctions.hpp" class Bot { public: Bot(int playerIndex); void initBot(); int bestBonusCell(); int bestCellToDropABomb(); int bestSafeCell(); bool isSomewhatInTheMiddleOfCell(); bool isThereABombUnderMe(); void stopWalking(); void startPushingBombDropButton(); void startPushingRemoteButton(); void startPushingJumpButton(); void stopPushingBombDropButton(); void stopPushingRemoteButton(); void stopPushingJumpButton(); bool walkToCell(int cell); bool amISafe(); int getCurrentCell(); void printGrid(); void printCellInfo(int cell); int howManyBombsLeft(); uint8_t calculateBestCellToPickUpBonus(); bool cellSafe(int cell); int scoreForBonus(Bonus bonus, int x, int y); int _playerIndex; uint8_t _direction1FrameAgo; uint8_t _direction2FramesAgo; uint8_t _shiveringCounter; public: travelCostGrid travelGrid; // travelGrid that can cross cells set to true in DangerGrid travelCostGrid travelSafeGrid; // travelGrid avoiding cells set to true in DangerGrid uint32_t bestExplosionsGrid[grid_size_x][grid_size_y]; // score based on the nb of bricks one of my bomb there would break or of the proximity from a monster uint8_t calculatedBestCellToPickUpBonus; uint8_t calculatedBestCellToDropABomb; uint32_t flameGrid[grid_size_x][grid_size_y]; // 0: no flame, 1..FLAME_DURATION+1: time with a flame, FLAME_DURATION+2: time before end of flame bool dangerGrid[grid_size_x][grid_size_y]; // used to track all dangers, including the ones we don't know the timing: true means a flame is coming (possibily under a remote controled bomb...), or that a monster is there bool noDangerGrid[grid_size_x][grid_size_y]; // set to false for each cells at init, used as an empty dangerGrid instance. bool monsterIsComingGrid[NUMBER_OF_CELLS]; bool pushingDropBombButton; }; mrboom-libretro-4.4/ai/BotTree.cpp000066400000000000000000000206631324457731200171540ustar00rootroot00000000000000#include "MrboomHelper.hpp" #include "Bot.hpp" #include "BotTree.hpp" #ifdef IOS void std::__throw_out_of_range(char const *) { } #endif class ConditionNode : public bt::Node { public: ConditionNode(Bot *bot) : Node(), bot(bot) { } void Initialize() { } virtual bool Condition() = 0; bt::Status Update() { if (Condition()) { return(bt::Success); } return(bt::Failure); } protected: Bot *bot; }; class MoveToNode : public bt::Node { public: MoveToNode(Bot *bot) : Node(), bot(bot) { } void Initialize() { } virtual int Cell() = 0; bt::Status Update() { int cell = Cell(); if (cell == -1) { if (isInMiddleOfCell(bot->_playerIndex)) { bot->stopWalking(); } return(bt::Failure); } if (((!(isInMiddleOfCell(bot->_playerIndex) && bot->getCurrentCell() == cell))) || (bot->getCurrentCell() != cell)) { if (bot->walkToCell(cell)) { return(bt::Running); } if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:Failed to go to %d (%d/%d)\n", frameNumber(), bot->_playerIndex, cell, CELLX(cell), CELLY(cell)); } return(bt::Failure); } bot->stopWalking(); if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:stopWalking arrived in %d (%d/%d)\n", frameNumber(), bot->_playerIndex, cell, CELLX(cell), CELLY(cell)); } return(bt::Success); } protected: Bot *bot; }; class MoveToBonus : public MoveToNode { public: MoveToBonus(Bot *bot) : MoveToNode(bot) { } int Cell() { int bestCell = bot->bestBonusCell(); #ifdef DEBUG botStates[bot->_playerIndex] = goingBonus; #endif if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:gotoBonus:%d (%d/%d) current=%d (%d/%d)\n", frameNumber(), bot->_playerIndex, bestCell, CELLX(bestCell), CELLY(bestCell), bot->getCurrentCell(), CELLX(bot->getCurrentCell()), CELLY(bot->getCurrentCell())); } return(bestCell); } }; class MoveToBombBestBombCell : public MoveToNode { public: MoveToBombBestBombCell(Bot *bot) : MoveToNode(bot) { } int Cell() { int bestCell = bot->bestCellToDropABomb(); #ifdef DEBUG botStates[bot->_playerIndex] = goingBomb; #endif if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:gotoBestBombCell:%d (%d/%d) current=%d (%d/%d)\n", frameNumber(), bot->_playerIndex, bestCell, CELLX(bestCell), CELLY(bestCell), bot->getCurrentCell(), CELLX(bot->getCurrentCell()), CELLY(bot->getCurrentCell())); } return(bestCell); } }; class MoveToSafeCell : public MoveToNode { public: MoveToSafeCell(Bot *bot) : MoveToNode(bot) { } int Cell() { int bestCell = bot->bestSafeCell(); #ifdef DEBUG botStates[bot->_playerIndex] = goingSafe; #endif if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:gotoBestSafeCell:%d (%d/%d) current=%d (%d/%d)\n", frameNumber(), bot->_playerIndex, bestCell, CELLX(bestCell), CELLY(bestCell), bot->getCurrentCell(), CELLX(bot->getCurrentCell()), CELLY(bot->getCurrentCell())); } return(bestCell); } }; class ConditionBombsLeft : public ConditionNode { public: ConditionBombsLeft(Bot *bot) : ConditionNode(bot) { } bool Condition() { // condition "i have more bombs" int howManyBombs = bot->howManyBombsLeft(); if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:bombLeft:%d\n", frameNumber(), bot->_playerIndex, howManyBombs); } return(howManyBombs); } }; class ConditionDropBomb : public ConditionNode { public: ConditionDropBomb(Bot *bot) : ConditionNode(bot) { } bool Condition() { bot->startPushingBombDropButton(); //TOFIX ? return false or running ? if (tracesDecisions(bot->_playerIndex)) { log_debug("BOTTREEDECISIONS: %d/%d:dropBomb\n", frameNumber(), bot->_playerIndex); } return(true); } }; BotTree::BotTree(int playerIndex) : Bot(playerIndex) { tree = new bt::BehaviorTree(); MoveToBonus * gotoBonus = new MoveToBonus(this); bt::Sequence *bombSeq = new bt::Sequence(); ConditionBombsLeft *bombLeft = new ConditionBombsLeft(this); bombSeq->AddChild(bombLeft); MoveToBombBestBombCell *gotoBestBombCell = new MoveToBombBestBombCell(this); bombSeq->AddChild(gotoBestBombCell); ConditionDropBomb *dropBomb = new ConditionDropBomb(this); bombSeq->AddChild(dropBomb); MoveToSafeCell *gotoSafePlace = new MoveToSafeCell(this); bt::Selector * rootNode = new bt::Selector(); rootNode->AddChild(gotoBonus); rootNode->AddChild(bombSeq); rootNode->AddChild(gotoSafePlace); tree->SetRoot(rootNode); } void BotTree::updateGrids() { updateFlameAndDangerGridsWithBombs(_playerIndex, flameGrid, dangerGrid); updateDangerGridWithMonstersSickPlayersAndCulDeSacs(_playerIndex, dangerGrid); updateDangerGridWithMonster4CellsTerritories(dangerGrid); updateMonsterIsComingGrid(monsterIsComingGrid); updateTravelGrid(_playerIndex, false, travelGrid, flameGrid, noDangerGrid); updateTravelGrid(_playerIndex, false, travelSafeGrid, flameGrid, dangerGrid); #ifdef DEBUG printGrid(); #endif if (!((frameNumber() + _playerIndex) % nb_dyna)) { calculatedBestCellToPickUpBonus = calculateBestCellToPickUpBonus(); } updateBestExplosionGrid(_playerIndex, bestExplosionsGrid, travelGrid, flameGrid, dangerGrid); } void BotTree::tick() { stopPushingRemoteButton(); stopPushingBombDropButton(); stopPushingJumpButton(); tree->Update(); if (monsterIsComingGrid[cellPlayer(_playerIndex)]) { startPushingBombDropButton(); } if (isSomewhatInTheMiddleOfCell() && frameNumber() % 2 && pushingDropBombButton == false) { bool cantPlaceMoreBombsAndSafe = (((bestCellToDropABomb() == -1) || (howManyBombsLeft() == 0)) && amISafe()); if (cantPlaceMoreBombsAndSafe || shouldActivateRemote(_playerIndex)) { startPushingRemoteButton(); } } } // filled by serialize... static size_t serializeSize = 0; size_t BotTree::serialize_size(void) { if (serializeSize == 0) { uint8_t tmpBuffer[MEM_STREAM_BUFFER_SIZE]; serialize(tmpBuffer); log_error("HARDCODED_RETRO_SERIALIZE_SIZE=SIZE_SER+%d*8\n", serializeSize); } assert(serializeSize != 0); return(serializeSize); } bool BotTree::serialize(void *data_) { memstream_set_buffer(buffer, MEM_STREAM_BUFFER_SIZE); static memstream_t *stream = memstream_open(1); assert(stream != NULL); memstream_rewind(stream); assert(tree != NULL); tree->serialize(stream); // write to the stream memstream_write(stream, &calculatedBestCellToPickUpBonus, sizeof(calculatedBestCellToPickUpBonus)); // write to the stream memstream_write(stream, &_direction1FrameAgo, sizeof(_direction1FrameAgo)); // write to the stream memstream_write(stream, &_direction2FramesAgo, sizeof(_direction2FramesAgo)); // write to the stream memstream_write(stream, &_shiveringCounter, sizeof(_shiveringCounter)); // write to the stream serializeSize = memstream_pos(stream); memstream_rewind(stream); memstream_read(stream, data_, serializeSize); // read from the stream return(true); } bool BotTree::unserialize(const void *data_) { memstream_set_buffer(buffer, MEM_STREAM_BUFFER_SIZE); static memstream_t *stream = memstream_open(1); assert(stream != NULL); memstream_rewind(stream); memstream_write(stream, data_, serialize_size()); // write to the stream memstream_rewind(stream); assert(tree != NULL); tree->unserialize(stream); memstream_read(stream, &calculatedBestCellToPickUpBonus, sizeof(calculatedBestCellToPickUpBonus)); // read from the stream memstream_read(stream, &_direction1FrameAgo, sizeof(_direction1FrameAgo)); // write to the stream memstream_read(stream, &_direction2FramesAgo, sizeof(_direction2FramesAgo)); // write to the stream memstream_read(stream, &_shiveringCounter, sizeof(_shiveringCounter)); // write to the stream return(true); } mrboom-libretro-4.4/ai/BotTree.hpp000066400000000000000000000005611324457731200171540ustar00rootroot00000000000000#include "bt.hpp" #include "Bot.hpp" #define MEM_STREAM_BUFFER_SIZE 64000 class BotTree : public Bot { public: BotTree(int playerIndex); size_t serialize_size(void); bool serialize(void *data_); bool unserialize(const void *data_); void updateGrids(); void tick(); private: bt::BehaviorTree *tree; uint8_t buffer[MEM_STREAM_BUFFER_SIZE]; }; mrboom-libretro-4.4/ai/GridFunctions.cpp000066400000000000000000001075401324457731200203660ustar00rootroot00000000000000#include #include #include #include "GridFunctions.hpp" #include "common.hpp" #include "MrboomHelper.hpp" #ifndef __LIBRETRO__ #include #endif #define liste_bombe_size (247) #define INFINITE_SHIELD 1000000 #define COUNTDOWN_APOCALYPSE 64 int playerGrid[NUMBER_OF_CELLS]; int killablePlayerGrid[NUMBER_OF_CELLS]; // is there a killable player in a cell bool humanPlayer[NUMBER_OF_CELLS]; // is there an human player in a cell int victoriesGrid[NUMBER_OF_CELLS]; // biggest number of victories for players in cell int lastPlayerGridUpdate = 0; void inline updatePlayerGrid() { if ((!lastPlayerGridUpdate) || (frameNumber() != lastPlayerGridUpdate)) { for (int i = 0; i < NUMBER_OF_CELLS; i++) { playerGrid[i] = 0; humanPlayer[i] = false; killablePlayerGrid[i] = false; victoriesGrid[i] = 0; } for (int i = 0; i < numberOfPlayers(); i++) { if (isAlive(i)) { int cell = cellPlayer(i); playerGrid[cell] = teamOfPlayer(i) | playerGrid[cell]; if (!isAIActiveForPlayer(i)) { humanPlayer[cell] = true; } if (invincibility(i) < FLAME_DURATION) { killablePlayerGrid[cell] = teamOfPlayer(i) | playerGrid[cell]; } int v = victories(i); if (victoriesGrid[cell] < v) { victoriesGrid[cell] = v; } } } for (int i = numberOfPlayers(); i < nb_dyna; i++) { if (isAlive(i)) { int cell = cellPlayer(i); playerGrid[cell] = monster_team | playerGrid[cell]; } } lastPlayerGridUpdate = frameNumber(); } } bool monsterInCell(int x, int y) { updatePlayerGrid(); return(playerGrid[CELLINDEX(x, y)] & monster_team); } bool playerInCell(int x, int y) { updatePlayerGrid(); return(playerGrid[CELLINDEX(x, y)] & (player_team1 | player_team2 | player_team3 | player_team4 | player_team5 | player_team6 | player_team7 | player_team8)); } bool playerInCell(int player, int x, int y) { int xp = xPlayer(player); int yp = yPlayer(player); return((x == xp) && (y == yp)); } bool killablePlayerNotFromMyTeamInCell(int player, int x, int y) { updatePlayerGrid(); int notMyTeamMask = (~teamOfPlayer(player)) & (~monster_team); return(notMyTeamMask & killablePlayerGrid[CELLINDEX(x, y)]); } bool enemyInCell(int player, int x, int y) { updatePlayerGrid(); if ((x >= grid_size_x - 1) || (!x) || (y >= grid_size_y - 1) || (!y)) { return(false); } int notMyTeamMask = ~teamOfPlayer(player); int cell = CELLINDEX(x, y); return(notMyTeamMask & playerGrid[cell]); } bool enemyAroundCell(int player, int x, int y) { updatePlayerGrid(); if ((x >= grid_size_x - 1) || (!x) || (y >= grid_size_y - 1) || (!y)) { return(false); } int notMyTeamMask = ~teamOfPlayer(player); int cell = CELLINDEX(x, y); int closeMonsterMask = 0; closeMonsterMask = closeMonsterMask | playerGrid[cell]; closeMonsterMask = closeMonsterMask | playerGrid[cell + 1]; closeMonsterMask = closeMonsterMask | playerGrid[cell - 1]; closeMonsterMask = closeMonsterMask | playerGrid[cell - grid_size_x]; closeMonsterMask = closeMonsterMask | playerGrid[cell - grid_size_x - 1]; closeMonsterMask = closeMonsterMask | playerGrid[cell - grid_size_x + 1]; closeMonsterMask = closeMonsterMask | playerGrid[cell + grid_size_x]; closeMonsterMask = closeMonsterMask | playerGrid[cell + grid_size_x - 1]; closeMonsterMask = closeMonsterMask | playerGrid[cell + grid_size_x + 1]; return(notMyTeamMask & closeMonsterMask); } bool shouldPlayerFearCulDeSac(int player, int x, int y) { enum Bonus bonus = bonusInCell(x, y); if ((bonus == bonus_heart) || (bonus == bonus_egg) || (bonus == bonus_bulletproofjacket)) { return(false); } if (invincibility(player)) { return(false); //TOFIX } return(true); } bool isCellCulDeSac(int x, int y) { if ((x >= grid_size_x - 1) || (!x) || (y >= grid_size_y - 1) || (!y)) { return(false); } int i = 0; if (!(brickOrSkullBonus(x, y + 1) || bombInCell(x, y + 1))) { i++; } if (i > 1) { return(false); } if (!(brickOrSkullBonus(x, y - 1) || bombInCell(x, y - 1))) { i++; } if (i > 1) { return(false); } if (!(brickOrSkullBonus(x - 1, y) || bombInCell(x - 1, y))) { i++; } if (i > 1) { return(false); } if (!(brickOrSkullBonus(x + 1, y) || bombInCell(x + 1, y))) { i++; } if (i > 1) { return(false); } return(true); } static int heuristicDistance(int x, int y, int xP, int yP) { return(abs(x - xP) + abs(y - yP)); } bool isPlayerFastestToCell(int player, int x, int y) { int xP = xPlayer(player); int yP = yPlayer(player); int myDistance = heuristicDistance(x, y, xP, yP) * framesToCrossACell(player); for (int i = 0; i < numberOfPlayers(); i++) { if (isAlive(i) && (i != player)) { int xP2 = xPlayer(i); int yP2 = yPlayer(i); int hisDistance = heuristicDistance(x, y, xP2, yP2) * framesToCrossACell(i); if (hisDistance < myDistance) { return(false); } } } return(true); } bool isSameTeamTwoFastestToCell(int x, int y) { int fatest = -1; int secondFatest = -1; int fatestTeam = -1; int secondFatestTeam = -1; for (int i = 0; i < numberOfPlayers(); i++) { if (isAlive(i)) { int xP2 = xPlayer(i); int yP2 = yPlayer(i); int distance = heuristicDistance(x, y, xP2, yP2) * framesToCrossACell(i); if ((fatest == -1) || (fatest > distance)) { secondFatest = fatest; secondFatestTeam = fatestTeam; fatest = distance; fatestTeam = teamOfPlayer(i); } else { if ((secondFatest == -1) || (secondFatest > distance)) { secondFatest = distance; secondFatestTeam = teamOfPlayer(i); } } } } return(secondFatestTeam == fatestTeam); } struct bombInfo *bombsGrid[grid_size_x][grid_size_y]; // NULL if no bomb, pointer to the bomb in m.liste_bombe int lastBombGridUpdate = 0; void iterateOnBombs(FunctionWithBombInfo f) { int nbBombs = m.liste_bombe; int index = 0; struct bombInfo *bombesInfoArray = (struct bombInfo *)&m.liste_bombe_array; while (nbBombs && index < liste_bombe_size) { if (bombesInfoArray[index].countDown != 0) { f(&bombesInfoArray[index]); nbBombs--; } index++; } assert(index < liste_bombe_size); } void drawBombFlames(int player, int cell, int flameSize, FunctionWithFlameDrawingHelpfulData f, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y], int&score) { int x = CELLX(cell); int y = CELLY(cell); f(player, x, y, 0, flameGrid, dangerGrid, score); int xx = x; int yy = y; int fs = flameSize; while ((xx > 0) && (fs)) { xx--; fs--; f(player, xx, yy, flameSize - fs, flameGrid, dangerGrid, score); if (somethingThatWouldStopFlame(xx, yy)) { break; } } xx = x; yy = y; fs = flameSize; while ((yy > 0) && (fs)) { yy--; fs--; f(player, xx, yy, flameSize - fs, flameGrid, dangerGrid, score); if (somethingThatWouldStopFlame(xx, yy)) { break; } } xx = x; yy = y; fs = flameSize; while ((xx < grid_size_x - 2) && (fs)) { xx++; fs--; f(player, xx, yy, flameSize - fs, flameGrid, dangerGrid, score); if (somethingThatWouldStopFlame(xx, yy)) { break; } } xx = x; yy = y; fs = flameSize; while ((yy < grid_size_y - 2) && (fs)) { yy++; fs--; f(player, xx, yy, flameSize - fs, flameGrid, dangerGrid, score); if (somethingThatWouldStopFlame(xx, yy)) { break; } } } bool flameInCell(int x, int y) { db z = m.truc2[x + y * grid_size_x_with_padding]; return((z > 4) && (z < 54)); } bool somethingThatWouldStopFlame(int x, int y) { if (bonusInCell(x, y) != no_bonus) { return(true); } if (brickInCell(x, y)) { return(true); } if (mudbrickInCell(x, y)) { return(true); } return(false); } #ifdef DEBUG int howToGoDebug; int howToGoDebugMax = 1; #endif enum Button howToGo(int player, int toX, int toY, const travelCostGrid& travelGrid, bool&shouldJump) { assert(toX >= 0); assert(toX < grid_size_x); assert(toY >= 0); assert(toY < grid_size_y); if ((xPlayer(player) == toX) && (yPlayer(player) == toY)) { #ifdef DEBUG if (tracesDecisions(player)) { log_debug("BOTTREEDECISIONS: player==toX %d %d\n", toX, toY); } #endif int adderX = getAdderX(player); int adderY = getAdderY(player); if (adderX < 0) { return(button_right); } if (adderX > 0) { return(button_left); } if (adderY > 0) { return(button_up); } if (adderY < 0) { return(button_down); } } #ifdef DEBUG howToGoDebug++; if (howToGoDebug > howToGoDebugMax) { howToGoDebugMax = howToGoDebug; assert(howToGoDebug < 100); } #endif enum Button result = button_error; int cost = TRAVELCOST_CANTGO; int toXChosen = -1; int toYChosen = -1; int adderXChosen = 0; int adderYChosen = 0; int initialCost = travelGrid.cost(toX, toY); // look to the left if (toX > 1) { int adderX = -1; int adderY = 0; enum Button direction = button_right; int calculatedCost = travelGrid.cost(toX + adderX, toY + adderY, direction); if ((calculatedCost < cost) && (initialCost >= calculatedCost)) { toXChosen = toX + adderX; toYChosen = toY + adderY; adderXChosen = adderX; adderYChosen = adderY; cost = calculatedCost; result = direction; } } // look to the right if (toX < grid_size_x - 2) { int adderX = +1; int adderY = 0; enum Button direction = button_left; int calculatedCost = travelGrid.cost(toX + adderX, toY + adderY, direction); if ((calculatedCost < cost) && (initialCost >= calculatedCost)) { toXChosen = toX + adderX; toYChosen = toY + adderY; adderXChosen = adderX; adderYChosen = adderY; cost = calculatedCost; result = direction; } } // look to the north if (toY > 1) { int adderX = 0; int adderY = -1; enum Button direction = button_down; int calculatedCost = travelGrid.cost(toX + adderX, toY + adderY, direction); if ((calculatedCost < cost) && (initialCost >= calculatedCost)) { toXChosen = toX + adderX; toYChosen = toY + adderY; adderXChosen = adderX; adderYChosen = adderY; cost = calculatedCost; result = direction; } } // look to the south if (toY < grid_size_y - 2) { int adderX = 0; int adderY = +1; enum Button direction = button_up; int calculatedCost = travelGrid.cost(toX + adderX, toY + adderY, direction); if ((calculatedCost < cost) && (initialCost >= calculatedCost)) { toXChosen = toX + adderX; toYChosen = toY + adderY; adderXChosen = adderX; adderYChosen = adderY; cost = calculatedCost; result = direction; } } if (result == button_error) { return(result); } if ((xPlayer(player) == toXChosen) && (yPlayer(player) == toYChosen)) { return(result); } else { if (travelGrid.wouldInvolveJumping(toXChosen, toYChosen, result)) // to avoid trying L turns on top of jump { toXChosen += adderXChosen; toYChosen += adderYChosen; if ((xPlayer(player) == toXChosen) && (yPlayer(player) == toYChosen)) { if (isInMiddleOfCell(player)) { shouldJump = true; return(result); } } } #ifdef DEBUG if (tracesDecisions(player)) { log_debug("-> %d/%d", toXChosen, toYChosen); } #endif return(howToGo(player, toXChosen, toYChosen, travelGrid, shouldJump)); } } static bool canPlayerJump(int player, int x, int y, int inVbls, int fromDirection, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]) { if (hasKangaroo(player)) { int x2 = x; int y2 = y; switch (fromDirection) { case button_right: x2++; if (x2 > grid_size_x - 1) { return(false); } break; case button_left: assert(x > 0); x2--; if (x2 < 0) { return(false); } break; case button_up: y2--; if (y2 < 0) { return(false); } break; case button_down: y2++; if (y2 > grid_size_x - 1) { return(false); } break; default: assert(0); break; } if (dangerGrid[x2][y2]) { return(false); } if (somethingThatIsNoTABombAndThatWouldStopPlayer(x2, y2)) { return(false); } int danger = flameGrid[x2][y2] - inVbls; if ((danger > 0) && (danger <= FLAME_DURATION)) { return(false); } return(true); } else { return(false); } } static bool canPlayerWalk(int player, int invincibility, int x, int y, int inVbls, int fromDirection, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]) { int danger = flameGrid[x][y] - inVbls; int shield = invincibility - inVbls; if (dangerGrid[x][y]) { return(false); } if ((danger > 0) && (danger <= FLAME_DURATION) && (shield <= 0) && (bonusInCell(x, y) != bonus_bulletproofjacket)) { return(false); } if (brickOrSkullBonus(x, y)) { return(false); } if ((monsterInCell(x, y)) && (shield <= 0)) { return(false); } if (bombInCell(x, y)) { if (hasPush(player)) { if ((playerInCell(x, y) || monsterInCell(x, y))) { return(false); } int x2 = x; int y2 = y; switch (fromDirection) { case button_right: assert(x < grid_size_x); x2++; break; case button_left: assert(x > 0); x2--; break; case button_up: assert(y > 0); y2--; break; case button_down: assert(y < grid_size_y); y2++; break; default: assert(0); return(false); break; } if (bombInCell(x2, y2)) { return(false); } if (somethingThatIsNoTABombAndThatWouldStopPlayer(x2, y2)) { return(false); } if (bonusInCell(x2, y2) != no_bonus) { return(false); } } else { return(false); } } return(true); } // fromDistance is the distance from the bomb center static int scoreForBombingCell(int player, int x, int y, int fromDistance, int flameSize, bool ignoreBricks) { int result = 0; if (killablePlayerNotFromMyTeamInCell(player, x, y)) { if (humanPlayer[CELLINDEX(x, y)]) { result++; // focus on humans } result += victoriesGrid[CELLINDEX(x, y)]; // focus on players with more victories result += 3; } if (monsterInCell(x, y)) { int monsterScore = 4 * (fromDistance + 1); result += monsterScore; } if (ignoreBricks) { return(result); } if (bombInCell(x, y)) { result += 2; } if (bonusInCell(x, y) == bonus_skull) { result += 2; } // dont care about blowing bricks when 1 bomb left and monster in the vicinity if ((nbBombsLeft(player) < 2) && canPlayerBeReachedByMonster(player)) { return(result); } // if has a bullet proof jacket, focus on attacking other players if (invincibility(player) > FLAME_DURATION && canPlayerReachEnemy(player)) { return(result); } if (mudbrickInCell(x, y)) { result++; if ((mudbrickInCell(x + 1, y)) || (brickInCell(x + 1, y))) { result++; } if ((mudbrickInCell(x - 1, y)) || (brickInCell(x - 1, y))) { result++; } if ((mudbrickInCell(x, y - 1)) || (brickInCell(x, y - 1))) { result++; } if ((mudbrickInCell(x, y + 1)) || (brickInCell(x, y + 1))) { result++; } } return(result); } static void updateScoreFunctionFunctionWithFlameDrawingHelpfulData(int player, int x, int y, int distance, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y], int&score) { score += scoreForBombingCell(player, x, y, distance, flameSize(player), false); flameGrid[x][y] = COUNTDOWN_DURATON + FLAME_DURATION; } void updateBestExplosionGrid(int player, uint32_t bestExplosionsGrid[grid_size_x][grid_size_y], const travelCostGrid& travelGrid, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]) { // calculate the best place to drop a bomb int currentCell = cellPlayer(player); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { int score = 0; if ( (dangerGrid[i][j] == false || ((CELLX(currentCell) == i) && (CELLY(currentCell)) == j)) && //authorise the current player cell even if it's in the dangerGrid travelGrid.canWalk(i, j) && (flameGrid[i][j] == 0 || travelGrid.cost(i, j) > flameGrid[i][j]) && bombInCell(i, j) == false ) { uint32_t grid[grid_size_x][grid_size_y]; memmove(grid, flameGrid, sizeof(grid)); bool unusedDangerGrid[grid_size_x][grid_size_y]; drawBombFlames(player, CELLINDEX(i, j), flameSize(player), updateScoreFunctionFunctionWithFlameDrawingHelpfulData, grid, unusedDangerGrid, score); // check that there is still a safe place in the grid: bool foundSafePlace = false; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (dangerGrid[i][j] == false && travelGrid.canWalk(i, j) && (grid[i][j] == 0 || travelGrid.cost(i, j) > grid[i][j]) ) { foundSafePlace = true; } } } if (!foundSafePlace) { score = -score; } } bestExplosionsGrid[i][j] = score; } } } static bool apocalyseDangerForCell(int x, int y) { if (isInTheApocalypse()) { db danger = m.truc_fin[x + y * grid_size_x_with_padding]; if (danger < COUNTDOWN_APOCALYPSE) { return(true); } } return(false); } static void visitCell(int player, bool ignoreFlames, int currentCell, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y], int adderX, int adderY, int framesPerCell, int direction, travelCostGrid& travelGrid, std::priority_queue >&queue, bool visited[NUMBER_OF_CELLS]) { int nextCell; uint32_t nextCost = travelGrid.cost(currentCell) + framesPerCell; int adderCell = 0; switch (direction) { case button_right: nextCost += -adderX + abs(adderY); adderCell = 1; break; case button_left: nextCost += adderX + abs(adderY); adderCell = -1; break; case button_up: nextCost += adderY + abs(adderX); adderCell = -grid_size_x; break; case button_down: nextCost += -adderY + abs(adderX); adderCell = grid_size_x; break; default: assert(0); break; } nextCell = currentCell + adderCell; int nextCell2 = nextCell + adderCell; uint32_t nextCost2 = nextCost + framesPerCell; if (!visited[nextCell]) { if (canPlayerWalk(player, ignoreFlames ? INFINITE_SHIELD : invincibility(player) - framesPerCell, CELLX(nextCell), CELLY(nextCell), nextCost, direction, flameGrid, dangerGrid)) { if (nextCost < travelGrid.cost(nextCell)) { travelGrid.setWalkingCost(nextCell, nextCost); queue.push(std::pair (-nextCost, nextCell)); } } else if (!ignoreFlames && (canPlayerJump(player, CELLX(nextCell), CELLY(nextCell), nextCost2, direction, flameGrid, dangerGrid))) { if ((travelGrid.jumpingCost(nextCell, direction) >= nextCost) && (travelGrid.cost(nextCell2) >= nextCost2)) { travelGrid.setJumpingCost(nextCell, nextCost, direction); travelGrid.setWalkingCost(nextCell2, nextCost2); queue.push(std::pair (-nextCost2, nextCell2)); } } } } void updateTravelGrid(int player, bool ignoreFlames, travelCostGrid& travelGrid, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]) { bool visited[NUMBER_OF_CELLS]; std::priority_queue > queue; std::pair pair; travelGrid.init(); int adderX = getAdderX(player); int adderY = getAdderY(player); int playerCell = cellPlayer(player); int currentCell = playerCell; int framesPerCell = framesToCrossACell(player); for (int i = 0; i < NUMBER_OF_CELLS; i++) { visited[i] = false; if (CELLX(i) == 0 || CELLX(i) == grid_size_x - 1 || CELLY(i) == 0 || CELLY(i) == grid_size_y - 1) { visited[i] = true; } } travelGrid.setWalkingCost(currentCell, 0); queue.push(std::pair (0, currentCell)); while (!queue.empty()) { pair = queue.top(); queue.pop(); currentCell = pair.second; if (!visited[currentCell]) { visited[currentCell] = true; visitCell(player, ignoreFlames, currentCell, flameGrid, dangerGrid, adderX, adderY, framesPerCell, button_right, travelGrid, queue, visited); visitCell(player, ignoreFlames, currentCell, flameGrid, dangerGrid, adderX, adderY, framesPerCell, button_left, travelGrid, queue, visited); visitCell(player, ignoreFlames, currentCell, flameGrid, dangerGrid, adderX, adderY, framesPerCell, button_up, travelGrid, queue, visited); visitCell(player, ignoreFlames, currentCell, flameGrid, dangerGrid, adderX, adderY, framesPerCell, button_down, travelGrid, queue, visited); adderX = 0; adderY = 0; } } travelGrid.setWalkingCost(playerCell, abs(getAdderX(player)) + abs(getAdderY(player))); } static void updateFlameAndDangerGridsFunctionFunctionWithThreeInts(int player, int x, int y, int distance, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y], int&countDown) { flameGrid[x][y] = flameGrid[x][y] ? std::min(flameGrid[x][y], uint32_t(countDown)) : countDown; if (!countDown) { dangerGrid[x][y] = true; } } static std::vector vec; static void addBombsIntoVector(struct bombInfo *bomb) { vec.push_back(bomb); } static void updateScoreFunctionFunction(int player, int x, int y, int distance, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y], int&score) { score += scoreForBombingCell(player, x, y, distance, flameSize(player), true); dangerGrid[x][y] = true; } bool shouldActivateRemote(int player) { if (!hasRemote(player)) { return(false); } int score = 0; uint32_t unusedFlameGrid[grid_size_x][grid_size_y]; bool bombedGrid[grid_size_x][grid_size_y]; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { bombedGrid[i][j] = false; } } vec.clear(); iterateOnBombs(addBombsIntoVector); for (std::vector ::iterator it = vec.begin(); it != vec.end(); ++it) { struct bombInfo *bomb = *it; int i = bomb->x(); int j = bomb->y(); if (bomb->getPlayer() == player) { drawBombFlames(player, CELLINDEX(i, j), flameSize(player), updateScoreFunctionFunction, unusedFlameGrid, bombedGrid, score); } } // chain effect for (int z = 0; z < 4; z++) { for (std::vector ::iterator it = vec.begin(); it != vec.end(); ++it) { struct bombInfo *bomb = *it; int i = bomb->x(); int j = bomb->y(); if ((bomb->getPlayer() != player) && (bombedGrid[i][j] == true)) { drawBombFlames(player, CELLINDEX(i, j), flameSize(bomb->getPlayer()), updateScoreFunctionFunction, unusedFlameGrid, bombedGrid, score); } } } int myTeam = teamOfPlayer(player); if (isSuicideOK(player)) { for (int i = 0; i < numberOfPlayers(); i++) { if (myTeam != teamOfPlayer(i) && isAlive(i)) { if (bombedGrid[xPlayer(i)][yPlayer(i)]) { log_debug("player %d suicide bombing? trying to kill player %d %d/%d %d/%d\n", player, i, xPlayer(i), yPlayer(i), myTeam, teamOfPlayer(i)); // check if my team would survive int nblives = 0; for (int j = 0; j < numberOfPlayers(); j++) { if (myTeam == teamOfPlayer(j) && isAlive(j)) { nblives += nbLives(j); if (bombedGrid[xPlayer(j)][yPlayer(j)] && (invincibility(j) < FLAME_DURATION)) { nblives--; } } } if (nblives) { return(true); } else { log_debug("Cancelled suicide bombing\n"); return(false); } } } } } // check if he would touch a friend or himself for (int i = 0; i < numberOfPlayers(); i++) { if (myTeam == teamOfPlayer(i) && isAlive(i)) { if (bombedGrid[xPlayer(i)][yPlayer(i)] && (invincibility(i) < FLAME_DURATION)) { return(false); } } } return(score); } void updateFlameAndDangerGridsWithBombs(int player, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y]) { struct bombInfo possibleShieldRemoteBombsUnderPlayer[nb_dyna]; for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { flameGrid[i][j] = 0; dangerGrid[i][j] = false; } } vec.clear(); iterateOnBombs(addBombsIntoVector); // add "virtual bombs" under other players that have remote + shields for (int i = 0; i < numberOfPlayers(); i++) { if (player != i && isAlive(i) && hasRemote(i) && invincibility(i)) { possibleShieldRemoteBombsUnderPlayer[i].remote = 1; possibleShieldRemoteBombsUnderPlayer[i].cell(cellPlayer(i)); possibleShieldRemoteBombsUnderPlayer[i].countDown = 0; possibleShieldRemoteBombsUnderPlayer[i].flameSize = flameSize(i); vec.push_back(&possibleShieldRemoteBombsUnderPlayer[i]); } } // for (std::vector ::iterator it = vec.begin(); it != vec.end(); ++it) { struct bombInfo *bomb = *it; int countDown = (int) bomb->countDown + FLAME_DURATION; if (bomb->remote) { countDown = 0; } int i = bomb->x(); int j = bomb->y(); if (flameGrid[i][j]) { countDown = std::min(flameGrid[i][j], uint32_t(countDown)); //this enable bomb explosions chains } drawBombFlames(player, CELLINDEX(bomb->x(), bomb->y()), bomb->flameSize, updateFlameAndDangerGridsFunctionFunctionWithThreeInts, flameGrid, dangerGrid, countDown); } for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (flameInCell(i, j)) { flameGrid[i][j] = FLAME_DURATION; //TODO be more precise. } } } } void updateDangerGridWithMonstersSickPlayersAndCulDeSacs(int player, bool dangerGrid[grid_size_x][grid_size_y]) { for (int i = 0; i < numberOfPlayers(); i++) { if (isAlive(i) && hasAnyDisease(i) && player != i) { int cell = cellPlayer(i); dangerGrid[CELLX(cell)][CELLY(cell)] = true; dangerGrid[CELLX(cell) - 1][CELLY(cell)] = true; dangerGrid[CELLX(cell) + 1][CELLY(cell)] = true; dangerGrid[CELLX(cell)][CELLY(cell) - 1] = true; dangerGrid[CELLX(cell)][CELLY(cell) + 1] = true; } } for (int i = numberOfPlayers(); i < nb_dyna; i++) { if (isAlive(i)) { int cell = cellPlayer(i); dangerGrid[CELLX(cell)][CELLY(cell)] = true; } } for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (apocalyseDangerForCell(i, j)) { dangerGrid[i][j] = true; } else { if (invincibility(player) > FLAME_DURATION) { dangerGrid[i][j] = false; } else { if ((enemyAroundCell(player, i, j)) && (isCellCulDeSac(i, j)) && shouldPlayerFearCulDeSac(player, i, j)) { dangerGrid[i][j] = true; } } } } } } void updateMonsterIsComingGrid(bool monsterIsComingGrid[NUMBER_OF_CELLS]) { for (int i = 0; i < NUMBER_OF_CELLS; i++) { monsterIsComingGrid[i] = false; } for (int i = numberOfPlayers(); i < nb_dyna; i++) { if (isAlive(i)) { monsterIsComingGrid[dangerousCellForMonster(i)] = true; } } } void updateDangerGridWithMonster4CellsTerritories(bool dangerGrid[grid_size_x][grid_size_y]) { static int frame; static bool init = true; static uint32_t noFlameGrid[grid_size_x][grid_size_y]; static bool noDangerGrid[grid_size_x][grid_size_y]; static bool resultDangerGrid[grid_size_x][grid_size_y]; if (init) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { noFlameGrid[i][j] = 0; noDangerGrid[i][j] = false; } } init = false; frame = frameNumber() - 1; } if (frame != frameNumber()) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { resultDangerGrid[i][j] = false; } } for (int monsterIndex = numberOfPlayers(); monsterIndex < nb_dyna; monsterIndex++) { if (isAlive(monsterIndex)) { int accessibleCells = 0; travelCostGrid travelGrid; updateTravelGrid(monsterIndex, true, travelGrid, noFlameGrid, noDangerGrid); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (travelGrid.canWalk(i, j)) { accessibleCells++; } } } if (accessibleCells <= 4) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (travelGrid.canWalk(i, j)) { resultDangerGrid[i][j] = true; } } } } } } } // update ... for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (resultDangerGrid[i][j]) { dangerGrid[i][j] = true; } } } frame = frameNumber(); } bool canPlayerBeReachedByMonster(int player) { static bool result[nb_dyna]; static int frame[nb_dyna]; static bool init = true; static uint32_t noFlameGrid[grid_size_x][grid_size_y]; static bool noDangerGrid[grid_size_x][grid_size_y]; if (isAlive(player) == false) { return(false); } travelCostGrid travelGrid; if (init) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { noFlameGrid[i][j] = 0; noDangerGrid[i][j] = false; } } init = false; for (int i = 0; i < nb_dyna; i++) { frame[i] = frameNumber() - 1; result[i] = false; } } if (frame[player] != frameNumber()) { frame[player] = frameNumber(); } else { return(result[player]); } updateTravelGrid(player, true, travelGrid, noFlameGrid, noDangerGrid); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (monsterInCell(i, j) && travelGrid.canWalk(i, j)) { result[player] = true; return(result[player]); } } } result[player] = false; return(result[player]); } bool canPlayerReachEnemy(int player) { static bool result[nb_dyna]; static int frame[nb_dyna]; static bool init = true; static uint32_t noFlameGrid[grid_size_x][grid_size_y]; static bool noDangerGrid[grid_size_x][grid_size_y]; if (isAlive(player) == false) { return(false); } travelCostGrid travelGrid; if (init) { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { noFlameGrid[i][j] = 0; noDangerGrid[i][j] = false; } } init = false; for (int i = 0; i < nb_dyna; i++) { frame[i] = frameNumber() - 1; result[i] = false; } } if (frame[player] != frameNumber()) { frame[player] = frameNumber(); } else { return(result[player]); } updateTravelGrid(player, true, travelGrid, noFlameGrid, noDangerGrid); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { if (enemyInCell(player, i, j) && travelGrid.canWalk(i, j)) { result[player] = true; return(result[player]); } } } result[player] = false; return(result[player]); } void printCellInfo(int cell, int player) { log_debug("printCellInfo %d: mudbrickInCell=%d brickInCell=%d bombInCell=%d bonusInCell=%d\n", cell, mudbrickInCell(CELLX(cell), CELLY(cell)), brickInCell(CELLX(cell), CELLY(cell)), bombInCell(CELLX(cell), CELLY(cell)), bonusInCell(CELLX(cell), CELLY(cell))); } mrboom-libretro-4.4/ai/GridFunctions.hpp000066400000000000000000000253021324457731200203660ustar00rootroot00000000000000#pragma once #include "mrboom.h" #include "common.hpp" #include "MrboomHelper.hpp" #include // std::min #ifndef DEBUG #define NDEBUG #endif #include "assert.h" #define MAX_PIXELS_PER_FRAME 8 #pragma pack(push, 1) typedef struct bombInfo { dd infojoueur; dd countDown; dd offsetCell; dw flameSize; dw remote; dw adderX; //+1,0,-1 dw adderY; //+1,0,-1 dw offsetX; // 0 = middle dw offsetY; void cell(int cell) { offsetCell = CELLX(cell) + CELLY(cell) * grid_size_x_with_padding; } int x() { return(CELLXWITHPADDING(offsetCell)); }; int y() { return(CELLYWITHPADDING(offsetCell)); }; int getPlayer() { int bombPlayer = -1; //will return -1 on a end of level 2 bomb. if (offsetof(struct Mem, j1) == infojoueur) { bombPlayer = 0; } if (offsetof(struct Mem, j2) == infojoueur) { bombPlayer = 1; } if (offsetof(struct Mem, j3) == infojoueur) { bombPlayer = 2; } if (offsetof(struct Mem, j4) == infojoueur) { bombPlayer = 3; } if (offsetof(struct Mem, j5) == infojoueur) { bombPlayer = 4; } if (offsetof(struct Mem, j6) == infojoueur) { bombPlayer = 5; } if (offsetof(struct Mem, j7) == infojoueur) { bombPlayer = 6; } if (offsetof(struct Mem, j8) == infojoueur) { bombPlayer = 7; } return(bombPlayer); } } bombInfo; #pragma pack(pop) #pragma pack(push, 1) typedef struct travelCostGrid { uint32_t _travelCostGrid[grid_size_x][grid_size_y]; // safe to walk walking distance, TRAVELCOST_CANTGO if cant go, -7 to +8 if player is here... uint32_t travelCostGridJumpLeftRight[grid_size_x][grid_size_y]; uint32_t travelCostGridJumpUpDown[grid_size_x][grid_size_y]; uint32_t cost(int i, int j, int direction) const { return(std::min(jumpingCost(i, j, direction), _travelCostGrid[i][j])); // min: to be able to jump on a flame and come back later using the walking way. (otherwise the comeback breaks the path) } bool wouldInvolveJumping(int i, int j, int direction) const { return(jumpingCost(i, j, direction) < _travelCostGrid[i][j]); } uint32_t cost(int i, int j) const { return(_travelCostGrid[i][j]); } uint32_t cost(int cell) const { return(cost(CELLX(cell), CELLY(cell))); } void setWalkingCost(int cell, uint32_t cost) { _travelCostGrid[CELLX(cell)][CELLY(cell)] = cost; } void printCell(int i, int j) { int lr = costLeftRight(i, j); int up = costUpDown(i, j); int w = cost(i, j); if (TRAVELCOST_CANTGO != w) { log_debug(" %03d ", w); } else { if ((lr != TRAVELCOST_CANTGO) || (up != TRAVELCOST_CANTGO)) { if (TRAVELCOST_CANTGO != lr) { log_debug("%03d/", lr); } else { log_debug("---/"); } if (TRAVELCOST_CANTGO != up) { log_debug("%03d ", up); } else { log_debug("--- "); } } else { log_debug(" --- "); } } } uint32_t jumpingCost(int i, int j, int direction) const { switch (direction) { case button_left: return(travelCostGridJumpLeftRight[i][j]); break; case button_right: return(travelCostGridJumpLeftRight[i][j]); break; case button_up: return(travelCostGridJumpUpDown[i][j]); break; case button_down: return(travelCostGridJumpUpDown[i][j]); break; default: assert(0); break; } return(0); } uint32_t jumpingCost(int cell, int direction) const { int i = CELLX(cell); int j = CELLY(cell); return(jumpingCost(i, j, direction)); } uint32_t costLeftRight(int i, int j) const { return(jumpingCost(i, j, button_left)); } uint32_t costUpDown(int i, int j) const { return(jumpingCost(i, j, button_up)); } void setJumpingCost(int i, int j, uint32_t cost, int direction) { switch (direction) { case button_left: travelCostGridJumpLeftRight[i][j] = cost; break; case button_right: travelCostGridJumpLeftRight[i][j] = cost; break; case button_up: travelCostGridJumpUpDown[i][j] = cost; break; case button_down: travelCostGridJumpUpDown[i][j] = cost; break; default: assert(0); break; } } void setJumpingCost(int cell, uint32_t cost, int direction) { int i = CELLX(cell); int j = CELLY(cell); return(setJumpingCost(i, j, cost, direction)); } bool canWalk(int i, int j) const { return(_travelCostGrid[i][j] != TRAVELCOST_CANTGO); } bool canWalk(int cell) const { return(canWalk(CELLX(cell), CELLY(cell))); } void init() { for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { _travelCostGrid[i][j] = TRAVELCOST_CANTGO; travelCostGridJumpLeftRight[i][j] = TRAVELCOST_CANTGO; travelCostGridJumpUpDown[i][j] = TRAVELCOST_CANTGO; } } } void print() { for (int i = 0; i < grid_size_x; i++) { log_debug("__%03d__ ", i); } log_debug("\n"); for (int j = 0; j < grid_size_y; j++) { for (int i = 0; i < grid_size_x; i++) { printCell(i, j); } log_debug("-%03d-", j); log_debug("\n"); } } } travelCostGrid; #pragma pack(pop) enum Button howToGo(int player, int toX, int toY, const travelCostGrid& travelGrid, bool&shouldJump); bool isPlayerFastestToCell(int player, int x, int y); bool isSameTeamTwoFastestToCell(int x, int y); typedef void (*FunctionWithBombInfo)(struct bombInfo *); void iterateOnBombs(FunctionWithBombInfo f); typedef void (*FunctionWithFlameDrawingHelpfulData)(int, int, int, int, uint32_t[grid_size_x][grid_size_y], bool[grid_size_x][grid_size_y], int&); void drawBombFlames(int player, int cell, int flameSize, FunctionWithFlameDrawingHelpfulData f, uint32_t[grid_size_x][grid_size_y], bool[grid_size_x][grid_size_y], int&); void updateBestExplosionGrid(int player, uint32_t bestExplosionsGrid[grid_size_x][grid_size_y], const travelCostGrid& travelGrid, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]); void updateTravelGrid(int player, bool ignoreFlames, travelCostGrid& travelGrid, const uint32_t flameGrid[grid_size_x][grid_size_y], const bool dangerGrid[grid_size_x][grid_size_y]); void updateFlameAndDangerGridsWithBombs(int player, uint32_t flameGrid[grid_size_x][grid_size_y], bool dangerGrid[grid_size_x][grid_size_y]); void updateDangerGridWithMonstersSickPlayersAndCulDeSacs(int player, bool dangerGrid[grid_size_x][grid_size_y]); void updateDangerGridWithMonster4CellsTerritories(bool dangerGrid[grid_size_x][grid_size_y]); bool flameInCell(int x, int y); Bonus inline bonusInCell(int x, int y) { /* * ;1 = bombe... (2,3,4) respirant... si c sup a 4; on est mort... * ;5 = centre de bombe. de 5 a 11 * ;12 = ligne droite... * ;19 = arrondie ligne droite vers la gauche... * ;26 = arrondie ligne droite vers la droite * ;33 = ligne verti * ;40 arrondie verti vers le haut * ;47-- bas * ;54-- bonus bombe... de 54 a 63 (offset 144) * ;64-- bonus flamme... de 64 a 73 (offset 144+320*16) * ;74-- tete de mort de 74 a 83 * ;84-- bonus parre balle. de 84 a 93 * ;94-- bonus COEUR !!! * ;104 -- bonus bombe retardement * ;114 --- bonus pousseur * ;124 --- patins a roulettes * ;134 --- HORLOGE * ;horloge * bonus_4 134 * bonus_3 144,1,tribombe * bonus_6 154 * ;oeuf * bonus_5 193 */ db z = m.truc2[x + y * grid_size_x_with_padding]; if ((z >= 54) && (z < 194)) { if (z < 64) { return(bonus_bomb); } if (z < 74) { return(bonus_flame); } if (z < 84) { return(bonus_skull); } if (z < 94) { return(bonus_bulletproofjacket); } if (z < 104) { return(bonus_heart); } if (z < 114) { return(bonus_remote); } if (z < 124) { return(bonus_push); } if (z < 134) { return(bonus_roller); } if (z < 144) { return(bonus_time); } if (z < 154) { return(bonus_tribomb); } if (z < 164) { return(bonus_banana); } return(bonus_egg); } return(no_bonus); } bool monsterInCell(int x, int y); bool playerInCell(int x, int y); bool enemyInCell(int player, int x, int y); bool enemyAroundCell(int player, int x, int y); bool isCellCulDeSac(int x, int y); extern int lastBombGridUpdate; extern struct bombInfo *bombsGrid[grid_size_x][grid_size_y]; // NULL if no bomb, pointer to the bomb in m.liste_bombe static void updateBombGrid(struct bombInfo *bomb) { bombsGrid[bomb->x()][bomb->y()] = bomb; } int inline updateBombGrid() { memset(bombsGrid, 0, sizeof(bombsGrid)); iterateOnBombs(updateBombGrid); return(frameNumber()); } bool inline bombInCell(int x, int y) { if ((!lastBombGridUpdate) || (frameNumber() != lastBombGridUpdate)) { lastBombGridUpdate = updateBombGrid(); } return(bombsGrid[x][y] != NULL); } bool somethingThatWouldStopFlame(int x, int y); bool inline mudbrickInCell(int x, int y) { db brickKind = m.truc[x + y * grid_size_x_with_padding]; return(brickKind == 2); } bool inline brickInCell(int x, int y) { db brickKind = m.truc[x + y * grid_size_x_with_padding]; return((brickKind == 1) || ((brickKind >= 3) && (brickKind <= 11))); } bool inline brickOrSkullBonus(int x, int y) { if (brickInCell(x, y)) { return(true); } if (mudbrickInCell(x, y)) { return(true); } if (bonusInCell(x, y) == bonus_skull) { return(true); } return(false); } bool inline somethingThatIsNoTABombAndThatWouldStopPlayer(int x, int y) { if (brickOrSkullBonus(x, y)) { return(true); } if (monsterInCell(x, y)) { return(true); } return(false); } void updateMonsterIsComingGrid(bool monsterIsComingGrid[NUMBER_OF_CELLS]); bool canPlayerBeReachedByMonster(int player); bool canPlayerReachEnemy(int player); void printCellInfo(int cell); bool shouldActivateRemote(int player); mrboom-libretro-4.4/ai/MrboomHelper.cpp000066400000000000000000000207301324457731200201760ustar00rootroot00000000000000#include "common.hpp" #include "MrboomHelper.hpp" #pragma GCC diagnostic ignored "-Warray-bounds" void addOneAIPlayer() { db *keys = m.total_t; keys[64 + 5 + m.nombre_de_dyna * 7] = 1; keys[8 * 7 + 2] = 1; m.nb_ai_bombermen++; } void addXAIPlayers(int x) { db *keys = m.total_t; for (int i = 0; i < x; i++) { keys[64 + 5 + i * 7] = 1; } keys[8 * 7 + 2] = 1; } void pressStart() { db *keys = m.total_t; keys[8 * 7] = 1; keys[8 * 7 + 2] = 1; } void pressESC() { m.sortie = 1; } bool isInTheApocalypse() { return(m.in_the_apocalypse == 1); } bool hasRollers(int player) { return(m.patineur[player] == 1); } bool hasRemote(int player) { return(m.j1[4 + player * 5]); } bool hasPush(int player) { return(m.pousseur[player] == 1); } bool hasTriBomb(int player) { return(m.tribombe[player] == 1); } // Warning will be zero if less then 1 int pixelsPerFrame(int player) { return(CELLPIXELSSIZE / framesToCrossACell(player)); } int framesToCrossACell(int player) { bool speed = hasSpeedDisease(player); bool slow = hasSlowDisease(player); if (hasRollers(player)) { if (slow) { return((CELLPIXELSSIZE / 2) * 4); //32 } if (speed) { return((CELLPIXELSSIZE / 2) / 4); //2 } return(CELLPIXELSSIZE / 2); //8 } if (slow) { return(CELLPIXELSSIZE * 4); //64 } if (speed) { return(CELLPIXELSSIZE / 4); //4 } return(CELLPIXELSSIZE); //16 } int nbLives(int player) { if (isAlive(player)) { return(m.nombre_de_coups[player] + 1); } else { return(0); } } bool isAlive(int player) { return(m.vie[player] == 1); } bool isAIActiveForPlayer(int player) { return((m.control_joueur[player] >= 64) && (m.control_joueur[player] <= 64 * 2)); } bool playerGotDisease() { static int current = 0; int diseases = 0; bool result = false; for (int i = 0; i < numberOfPlayers(); i++) { if (isAlive(i) && hasAnyDisease(i)) { diseases++; } } if (current < diseases) { result = true; } current = diseases; return(result); } bool hasAnyDisease(int player) { return(m.maladie[player * 2] != 0); } bool hasSlowDisease(int player) { return(m.maladie[player * 2] == 2); } bool hasSpeedDisease(int player) { return(m.maladie[player * 2] == 1); } bool hasInvertedDisease(int player) { return(m.maladie[player * 2] == 4); } bool hasDiarrheaDisease(int player) { return(m.maladie[player * 2] == 3); } bool hasSmallBombDisease(int player) { return(m.maladie[player * 2] == 6); } bool hasConstipationDisease(int player) { return(m.maladie[player * 2] == 5); } void setDisease(int player, int disease, int duration) { m.maladie[player * 2] = disease; m.maladie[player * 2 + 1] = duration; } int numberOfPlayers() { return(m.nombre_de_dyna); } bool replay() { return(m.action_replay != 0); } int level() { if (replay()) { return(-1); } if (inTheMenu()) { return(-1); } return(m.viseur_liste_terrain); } void chooseLevel(int level) { m.viseur_liste_terrain = level; } bool inTheMenu() { return((isGameActive() == false) && m.ordre == 'S'); } bool isGameActive() { if ((m.ordre == 1) && (m.ordre2 == 3)) { return(true); } return(false); } bool isAboutToWin() { return(isGameActive() && (m.attente_avant_med < 100)); } bool isDrawGame() { return(m.ordre2 == 'D'); } bool won() { return(m.ordre2 == 'Z'); } int nbBombsLeft(int player) { if (m.nombre_de_vbl_avant_le_droit_de_poser_bombe) { return(0); } if (hasConstipationDisease(player)) { return(0); } if (isAboutToWin()) { return(0); } return(m.j1[player * 5]); //nb of bombs } bool isApocalypseSoon() { return(isGameActive() && ((m.temps & 0x3FFF) < 3)); } void activeApocalypse() { m.temps = 2; } int invincibility(int player) { return(m.invinsible[player]); } void activeCheatMode() { log_info("activeCheatMode\n"); m.temps = 10; for (db i = 0; i < nb_dyna; i++) { // setDisease(i,3,1000); Diarrhea /* * if (i>= m.nombre_de_dyna) * m.nombre_de_coups[i]=1; //number of lifes * else * m.nombre_de_coups[i]=99; */ if (i < m.nombre_de_dyna) { m.j1[i * 5] = 1; //nb of bombs //m.j1[1+i*5]=5; // power of bombs m.j1[4 + i * 5] = 1; // remote m.pousseur[i] = 1; // bomb pusher m.lapipipino[i] = 1; m.nombre_de_coups[i]++; } } setNoMonsterMode(true); } void setNoMonsterMode(bool on) { m.nomonster = on; } bool bonusPlayerWouldLike(int player, enum Bonus bonus) { switch (bonus) { case no_bonus: case bonus_skull: case bonus_time: return(false); case bonus_roller: return(hasRollers(player) == false); case bonus_remote: return(hasRemote(player) == false); case bonus_tribomb: return(false); // return (hasTriBomb(player)==false); case bonus_push: return(hasPush(player) == false); case bonus_egg: return(hasKangaroo(player) == false); default: break; } return(true); } void setFrameNumber(int frame) { m.changement = frame; } int flameSize(int player) { if (hasSmallBombDisease(player)) { return(1); } return(m.j1[1 + player * 5]); } void setTeamMode(int teamMode) { m.team3_sauve = teamMode; } int teamMode() { return(m.team3_sauve); } void setAutofire(bool on) { if (on) { m.autofire = 1; } else { m.autofire = 0; } } bool autofire() { return(m.autofire == 1); } int xPlayer(int player) { return((m.donnee[player] + DELTA_X) / CELLPIXELSSIZE); } int yPlayer(int player) { return((m.donnee[nb_dyna + player] + DELTA_Y) / CELLPIXELSSIZE); } int cellPlayer(int player) { return(xPlayer(player) + yPlayer(player) * grid_size_x); } bool tracesDecisions(int player) { return(debugTracesPlayer(player) && (traceMask & DEBUG_MASK_BOTTREEDECISIONS)); } bool isInMiddleOfCell(int player) { int step = pixelsPerFrame(player); assert(step <= MAX_PIXELS_PER_FRAME); int x = GETXPIXELSTOCENTEROFCELL(player); int y = GETYPIXELSTOCENTEROFCELL(player); if (step < 1) { return((!x) && (!y)); } return((x >= -step / 2) && (x <= step / 2) && (y <= step / 2) && (y >= -step / 2)); } int dangerousCellForMonster(int player) { int cell = cellPlayer(player); int index = m.viseur_change_in[player] / 4; index--; if (index < 0) { index = 15; } switch (m.changeiny[index]) { case 0: return(cell + grid_size_x); case 8: return(cell + 1); case 16: return(cell - 1); case 24: return(cell - grid_size_x); } assert(0); return(0); } int victories(int player) { int mode = teamMode(); switch (mode) { case 0: return(m.victoires[player]); break; case 1: // color mode return(m.victoires[player / 2]); break; case 2: // sex mode return(m.victoires[player % 2]); break; default: assert(0); break; } return(0); } void pauseGameButton() { if (replay()) { pressESC(); return; } if (m.pauseur2) { m.pauseur2 = 0; } else { m.pauseur2 = 4; } } bool isGameUnPaused() { bool result = false; static bool prev = isGamePaused(); if ((isGamePaused() == false) && (prev)) { result = true; } prev = isGamePaused(); return(result); } bool isGamePaused() { return(m.pauseur2 && isGameActive()); } bool isSuicideOK(int player) { int myTeam = teamOfPlayer(player); int nbLivesEnemies = 0; int nbLivesFriends = 0; for (int i = 0; i < numberOfPlayers(); i++) { if (myTeam == teamOfPlayer(i)) { nbLivesFriends += nbLives(i); if (invincibility(i)) { nbLivesFriends++; } } if (myTeam != teamOfPlayer(i)) { nbLivesEnemies += nbLives(i); if (invincibility(i)) { nbLivesEnemies++; } } } return((nbLivesFriends > 1) && (nbLivesEnemies == 1)); } bool someHumanPlayersAlive() { for (int i = 0; i < numberOfPlayers(); i++) { if (isAIActiveForPlayer(i) == false) { if (isAlive(i)) { return(true); } } } return(false); } mrboom-libretro-4.4/ai/MrboomHelper.hpp000066400000000000000000000101761324457731200202060ustar00rootroot00000000000000#pragma once #include #include "mrboom.h" #include "common.hpp" #ifndef DEBUG #define NDEBUG #endif #include "assert.h" #ifdef __cplusplus extern "C" { #endif #define DELTA_X 3 #define DELTA_Y 14 #define COUNTDOWN_DURATON 256 #define CELLPIXELSSIZE 16 #define grid_size_x_with_padding (32) #define grid_size_x (grid_size_x_with_padding - 13) #define grid_size_y (13) #define NUMBER_OF_CELLS (grid_size_x * grid_size_y) #define GETXPIXELSTOCENTEROFCELL(player) (-7 + ((m.donnee[player] + DELTA_X) % CELLPIXELSSIZE)) #define GETYPIXELSTOCENTEROFCELL(player) (-7 + ((m.donnee[nb_dyna + player] + DELTA_Y) % CELLPIXELSSIZE)) #define CELLINDEX(cellx, celly) (((celly) * grid_size_x) + (cellx)) #define CELLX(cell) (cell % grid_size_x) #define CELLY(cell) (cell / grid_size_x) #define CELLXWITHPADDING(cell) (cell % grid_size_x_with_padding) #define CELLYWITHPADDING(cell) (cell / grid_size_x_with_padding) #define TRAVELCOST_CANTGO 9999 #define FLAME_DURATION (16 * 5 + 6 * 4 * 2) #define MAX_PIXELS_PER_FRAME 8 enum Bonus { no_bonus, bonus_bomb, bonus_flame, bonus_skull, bonus_bulletproofjacket, bonus_heart, bonus_remote, bonus_push, bonus_roller, bonus_time, bonus_tribomb, bonus_banana, bonus_egg }; bool someHumanPlayersAlive(); bool isInTheApocalypse(); bool isAlive(int player); bool isAIActiveForPlayer(int player); void addOneAIPlayer(); void addXAIPlayers(int x); void pressStart(); void pressESC(); bool inline hasKangaroo(int player) { return(m.lapipipino[player] == 1); } bool hasRemote(int player); bool hasRollers(int player); bool hasPush(int player); bool hasTriBomb(int player); bool hasAnyDisease(int player); bool hasSlowDisease(int player); bool hasSpeedDisease(int player); bool hasInvertedDisease(int player); bool hasDiarrheaDisease(int player); bool hasSmallBombDisease(int player); bool hasConstipationDisease(int player); void setDisease(int player, int disease, int duration); int nbBombsLeft(int player); bool bonusPlayerWouldLike(int player, enum Bonus bonus); int numberOfPlayers(); bool inTheMenu(); bool isGameActive(); bool isAboutToWin(); bool isDrawGame(); bool won(); void activeCheatMode(); void activeApocalypse(); bool isApocalypseSoon(); bool playerGotDisease(); void setNoMonsterMode(bool on); int invincibility(int player); int framesToCrossACell(int player); int pixelsPerFrame(int player); int inline frameNumber() { return(m.changement); } void setFrameNumber(int frame); int flameSize(int player); void chooseLevel(int level); bool replay(); int level(); void setTeamMode(int teamMode); int teamMode(); void setAutofire(bool on); bool autofire(); void pauseGameButton(); bool isGamePaused(); bool isGameUnPaused(); int xPlayer(int player); int yPlayer(int player); int cellPlayer(int player); bool tracesDecisions(int player); bool isInMiddleOfCell(int player); int dangerousCellForMonster(int player); int victories(int player); int inline getAdderX(int player) { return(GETXPIXELSTOCENTEROFCELL(player) * framesToCrossACell(player) / CELLPIXELSSIZE); } int inline getAdderY(int player) { return(GETYPIXELSTOCENTEROFCELL(player) * framesToCrossACell(player) / CELLPIXELSSIZE); } bool isSuicideOK(int player); int nbLives(int player); enum playerKind { player_team1 = 1, player_team2 = 2, player_team3 = 4, player_team4 = 8, player_team5 = 16, player_team6 = 32, player_team7 = 64, player_team8 = 128, monster_team = 256 }; enum playerKind inline teamOfPlayer(int player) { enum playerKind result = monster_team; int mode = teamMode(); switch (mode) { case 0: result = static_cast (1 << player); break; case 1: // color mode result = static_cast (1 << player / 2); break; case 2: // sex mode result = static_cast (1 << player % 2); break; default: assert(0); break; } return(result); } #ifdef __cplusplus } #endif mrboom-libretro-4.4/ai/README.md000066400000000000000000000010001324457731200163430ustar00rootroot00000000000000![alt tag](bt.png) Using a c++98 port of arvidsson's [behavior tree library](https://github.com/arvidsson/bt). See [logs](../tests/ai.txt). ![alt tag](../tests/screenshots/mrboom-0.gif) ![alt tag](../tests/screenshots/mrboom-1.gif) ![alt tag](../tests/screenshots/mrboom-2.gif) ![alt tag](../tests/screenshots/mrboom-3.gif) ![alt tag](../tests/screenshots/mrboom-4.gif) ![alt tag](../tests/screenshots/mrboom-5.gif) ![alt tag](../tests/screenshots/mrboom-6.gif) ![alt tag](../tests/screenshots/mrboom-7.gif) mrboom-libretro-4.4/ai/bt.hpp000066400000000000000000000003561324457731200162170ustar00rootroot00000000000000#pragma once #pragma GCC diagnostic ignored "-Wunused-function" #include "bt/BehaviorTree.hpp" #include "bt/Composite.hpp" #include "bt/Node.hpp" // composites #include "bt/composites/Selector.hpp" #include "bt/composites/Sequence.hpp" mrboom-libretro-4.4/ai/bt.png000066400000000000000000000610431324457731200162140ustar00rootroot00000000000000PNG  IHDRoSof pHYs  1zTXtmxGraphModelMTH NR88/[90wxLIQ鄱햕MzyM';eӄ'T . a\vS?'9ݔ6gM:#_RgzO#`.yE.TѵʞNb`, rAv_.s^H#/<4)u]/ K;@yB'M0a{/?ȏ@x?ȣp XGtAZRrQ\jٲi`Q5Y6G+EmkU˜\%.[=]4t8֖tk!8!nSիK|aVo_[_$>L*yREFu[D(;j#? #QO}h̢(Lx LW3'~C`w͍qrފ =F%\фZ*pSm8T0>!v!-)m{c0x,mN8KMeeudXdc܋Q"۩(<1azO)&m<7ֱ6-«?QQj"]FB N(6d wAFqO-l٫wMogHv.megﲭ pM:-TY/30n-R7';~A k4%ٲj|So~>Fq_7m$эdZرa;0V_x_(>' 5| kf〦ijXBS~9 > +*YC܎q!7/[u#SX@B{lo Ybg 9.f)Dq<TШ[1Z z<:D(Aw"Yu IDATxyxT$a (0lK"(n`|ZC-6T܋ZmblX[h`V%5T h ~x@%@2gf\׹3gι3,""\ZZ2~EDD1H;|;#@DDDDZOɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"CDb7-n㎹KJ!V,ZȒz3njFȜ\|YT׆A)yJh) (5vI)ɞ?,c '4̉i:EUxq3ud}A)\pUYɊw[foKg3̾ !1t,ݓ תz|% Ll>gg,<`x?=6t"=J!?>>P/"""Җ{5V4@yY_Tl`9VUYj*3+0[QiU2,P9ϲ9VZYcFVU`*,`+~sXiIñ*g`sJŝUPsxira`YVSo5ߥy~#Ț)1?X[Eن? ,#Hd Ze_"`e- dXY{d|iȠM⌺MEDV: +JtPKe:2>{:G&a0~ֵY#V~FƖ#ӚH_Wȝs_!8ƃ_czmeܨ76`eavsmv+x,짋tx25OOo :V6մ`RjwR>{wU,C 2O>'5ѳ'I-KHҁYi)lko+֊LDZG-o"" mΐF0ǹ6Ξ4f1wI-P;LLʀBozIgN#lm??{Fpэ90;Kj~t*r`/›RHozˡrG#ሬcJz<ʃ{XT\6܍* V[v~< ? ;< B"70[cE9{ggd[Y6ͿV4O1og|^ "Ÿ񱥮z5ÀFޢihQZzדꖰ_jWk ڿ7Dm:vtA鞷|>2h&MƼ%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""C%o""""1DɛH Q&"""CxҖ|1^!#--mƍCDDDS 4m'E:`Jx&?kM߁{<`;pus ^ΌD"""" @!"j3/3 qgmHpp#k_6m`>p'͡zᵯ%?;版ĽD\kC@% x#M|g+w#P<;qcn;󈈈ĤTB۸7 7]KEaIU%mx^r0 PT[%[u%V|3 x(Nm눈$g1 mk^ T%Vr1CrEDDD< \ KǍ{&WnMDDDDu x ؁kz 9B|76n!n\\W#+d;-7XYxZfF#r܄ 637kSA%\!눎p$,%p`4nBƳ SÁˀfJ~ޞ ﻌK2OK7"""Nŵ ׊%OZ.EL$J⧞qu """.g_ōGn|Jx<;R'MX/pS:i_ɸ^fxVZr^ 7#W=%í˺x!"""G?FZ>%]\ Ÿjm]2{sq |`DZH+ ŭ:c*V%xJU I5;+.U"%:%u?'Uo෸cFu@DDD:N2q]c!MܢpM*뉛q,Ѯ {2#"""zčz؅+s5ئDPsܺyk WWDDDB: ܉d6.1kӼ .h@b܅u;A/3@x( 2?]I۹|ׁDp*ŭ X ,~L*q'nulM^""")g>~[3W ;54x];##[Ibci3݁opyKq|Ӂq@WƸoJ܇{%Nʀ=EDD䈤Z xXk zx 0Ƴ˭qJDKpC_FDD$ տ*Rxõ@ B&m7Vma^"""(\2s`=  ,Z` Y6bpxķ14\uWpݞpݠ |Ytϕǀa"G.@DZHMn"2`.n] x| X+Bw&240XDD$.'\IOJuYtIDzq,"13MJtx(EtGp;v:7&rQ+n9KQM&kpDZH`M:i{q 7vf;nAp tD(6X"""G!8Tof*@9&g~X$d?D޸ono!` 0 MWJ9 Wus"yHGwn&]@ P luw>ٳ褽 <+f,Zn:`$p Gw9P+pDZHJ­ vL~`1x3DgI ŕei pO8+p nN a-do M0YKEJܪ(==ED$;pkJqET|7kGI胛\!nH$lÍpX/עn d\M."^7I"7~C`=IaE' VD޸q=""1+}=umn%!"r q=⾨D.YM `[?WJ׵.QDSpm.q8UWq3 f`P4oO>Jƭ7`DZĪp_:X2G<6X Im._y҆M^"+fm35Ӏ>1H|:7N矸/+"Ҷ9{HTpv5@ hpnV݉ T#g&ϼkuu*s9#)ĭi@$/EF` # oܬpekD$. anl\f)~Yt":2TJ$܆u %Җ7g[iզx|πܔt`>8". /SO /Dcc#@{EƏ+S07pDqƢJdff&LH[m&LpV2simk EDЙ*O&I-pj79qK݅[8^DbIDZHt:^qVǡM$֕>m8 JD_n-=\yq,{9M:cJDW'#  į<|ܘކ#AɛH|*1hD$.Յ{x7KUℒ72FquDcz7+y\Fℒ7\@W`Hyl'n69VJDb۷pKzv{DB}t`nTAJDb׀0xܚ!O#X­z) 8ӈ)y-7L`6O#X<|j+==HZMɛHlWin.ӈD$,2%UW$)ynWj7݌@2nfoÑCQ&.M7\&C`p!i4r@Z>Niaܢnoyt`qnmŭܰpd%oqJ[̙K6Ɯm8"";ᒸކ#6gpU鸛7&uሒ7od/~oqWzNǥn8nӨu6{;{hDDq!W cQEӁq ޅj."%q㊆QT} j(=7⊇? ,~m877A;(-;pPDD"ϸ:`uoÉoJD^%0i}CqB`nٙ KD3pk/])m,+-dIua㞉>|۔H^Xp]//'ZDD:7[ J2s3,6 ?mUVUYee7rTE$R/zzk;ܴi^%",PGH*.,0Yk99V`fVcs$65fVQ4y_'y(jAceffְrfYa*wT,ɳi͏ͪ1 _jıhM-rרW`8VhP^2ٛӈDDb EgzHF4 -,ܒ5Vef`fCN~܂Uoͭc5e͏6 dYY,?ye+p6mV͂k*|o\h oܻDDbJ20X'G uT#c$$Lqij"U8猇ux?D#@ca d䗳vZ֮]Y9LyLatg&3=o&kP8ᣕ/)c>>R/,4l&--m-[:6޺% bl-m?ӈb[2ݟ )}zwYObȾPkKx9{@=A7 }V$'An#/kRUQJEɛ7wLD:7l7pݣx%1&+fOgjrkv!JS1LʀBozIgN#lmO9q +^%KXx1 /3b4~V/9++sݟҺ+RRƷdÙf&1i@fn"""my\m7-Yr-HEVe7wd1f fb޷+'+*Ye98n}iE*<bNnr J,<.ʂf93J!ògΩ+Ǿk{H|j:hՋw.&^pç'`? EzԠ$ ղn$A)}6g RXmjl J7 [_$rFDҁqcpEw08i\6d5yOSTND4xM7"gE<}o)~_+n̈ɱP&"Qh .{Xcob;S7naSiR&Bɛ}.DlSX1o"+ "pDD$.MVΠ=N^L_& rKe >nLf_HMW@" x'/PP̚@@MsrS)4W1I㱙piMHuOq̫boSي:׺j웘2}&wLs?||sbwTVsPW̰terӬaFj<ƔLw)3 b93i՜?ַͫ'""&L`8&L`i?5Ǽ϶yeVp#+-/, ZeY)(R VYyAVyV\~ mVqLYM̲ fټƬ"ߦZqi75ȝe%6-ßgk,XQ|Ң𵳬4vvUEuҎ-˭*׸,܊6'S󊬬 ZUR4odYsJUSb~Šg9sr-qn(0Ȱw,"Vyff\UjS[$B[c{C(f`s,?E\5%yVMD_doT`Җ]GoKKK[c˘0a&L_ W¬yy i' q)=>=%%|t_$fs,~1k i[5Ie IDATϷg/9)66TS-J"""N7+Ʃ-&5]{~Řn  à3`e`އS 'i|j^A"[;9[ .dTDL|, 3@Sz糦@ Hy%r0""""GΣ(.l;2c*)ƽ6uV5X2 rjTͩ1͙ob-0^;IusNmFvj0[Uufͱ5 k|^[֔|$? *̪fmVE,Xiyn̛ME$DFA~mk,?Λe4OXhVXV|}˰YO%oV KǪXN~6//cek\c9MDbBdoT`ZPVw(p;= #}!duիٰ0\]0l~'e[hJH`Qv}m5v@>uj'oVp]HkQυtsPePX$%o">OkD䓯`ά}DDC7k9Z&"GA  c;V=S/ k~X"&`YH[<o"0i<JDDD$4R=8 ̾vɣ|U @ )+),9SCĂuWOETVVPQQAqQ.OOfcGg{Sk6xSP"#w{JUqAyZNNU5ؼS+6˝WuV-WcfU#mZV?TYfMh72h©l+*]Ng>Wh ̟WڼNYVYQnlO 56-*s\WgiH$VOnyVV\ؼrLi4?-XZi%2 Tγ,0NV8q Q\5*W{u/eX^YiŅS|)Vj6'%`W+Ye`0hʊ6xUo 5mM- +k0 VpVֵU UPn,;$UE򶦼敭2 TX?:Wַ.#mZv-\t1o"""N(YGSٳ `bZMM|súUj0SaW,^^k5T_UO}qT|R>a:JžsMۀptL OH8 0mN9kVUQUUEU^c[oxfI Lc̰a)0Y=%o"""N>VLH Hл*C]Pm{sRW#%}rneFxy@а`?5jj[!  P^2N@?$33a|z9:pEsXU,Haijx {j/e=%o"""N3{q5 5H&e@XQE7 H ԧ!LnuBT/]O_QH 9jq +[VvK3,У1=ͯe”yƽ55r(B0D9+P#+~[;2p QWto}՛3p-.|9QSǔQ8B;]Dִ.')* <1)yc-)ypυՋSX!W9-~(yc-)yυ|m*"""K%o""""1DɛH Q&"""C%o""""1HlJKK[x7n: 9ii0 J ;p#0 x$|""QIEz#@EzEp( hn>>Br\B'""jyD\d\\ڞm~r.y=KDD:E6"\6 I^:sj5n@ݪ"q)y%o;vm2o\xϿ Q\ָuMDD♒P&f {DmO [uRtJ"@ɛ:{@G]ZVx-I,ޱkr68tUE$)y%o+Ƴu-cמk]DvuHP>R7N=p-_[p vSV7܀(zIKK[/,Nku׌~G@5*,"""Qb~]nH=nk\`H 4•ݥ0/tC5#a` ePӭXK5UDDDJgRKY^upKcݸ٪zĂׁ灝3@cjqzq3Q# XsFtd:v2UnU, </N2ݥnUq7[?W [wik[UDD$Mu z/⹻ԭ*""7  7Vθ UEDDb"`%1EB uQLݪ"""Q+.iK^yeP67fOOݪ"""|hµ:Muv4.=zViGx x7 ӈKۆUEDDQ p n6\w *ʨ[UDDdwq/zzTꂛA+gnxHt[`-(-O%7V"zݸq"""@Y2NAŘqKyI nL'O#igZ+X<|} uFq%jvPİD2x\k^']Cݪ""cF!)Lt/3.~V6% xű;ԭ*""l^ 2RnUӀ͸+^\y'Oݪ"".>ƵiDh}nU9j X| J 3\E=]1[UDD\'2=0ӈi`' .ԭ*"k[yl7 `#>0]"s;V踎[*\˝D1n,ԢCsڄK Q7zxpݪ[q3ED$ } {3ۯ@X >[Sx_qIط[e&3p".ygщ4e:\k_pkUrԆ>$nۀ?xD,`n䓸nv0.9p߂p9n\Į20p$LO#>kiAI:DbʹLmq1B7\1:u:.55u`=tڵлgSSS݆ a͚5v:|f 0Ϸt¼[>-۸qfƩ7n<8bܗq3Rw]pO:\+"`m.]$''ҥW^6m4l׮]wNعsg®]6zpΝwtҥSNM;wnLKK[S__eΝkmΝ;k@ ^#"f%yԹs1 ЩSsn:j֭G?Nvѻwo_JJ ϔ l޼KorJ~m6l@ff]tE۟{9V^ݽW^{j]k׮zn|>KU8{1o߾cC@`>{aÆ%wq;?]ҭ[7LJJ"  hhhhsǎ||_֮]۰nݺ_x&))5ՇIIII_NNNm۶Sz;vl3<3aь=O}"ơmڴ+V/|CPhkbb?^wff|>?JގEۥK;q:,:,YCCCsBꫯ6ޱ{fj -Oc g߰sΫꫯN9Smgʕ趝;w~ qXJ␒Ccǎ[z:mڴ뮻IK.eҥTVVtRҸ رcGsu,55UIޖ-[HJJ⥗^bӦM;crg2vX֯_??ܴ}-]túu~f΋m0`744xҤI sOʺuɋ/ؽ{֯_?ˣP!%oֵkoON馛zivc?***KҷoO%Q)))w0TҸe˖wy 8_|9==;wf]]ݯwh."QM>_z.C iƄkpgii'ݺu!%o{|~~%t{|=q<ӵkם??v'ڍ7h6l֯_o_tIrJܹ#G^{-"m%F1v/Yu将 [nYomtJ3 ;^*s)ܹs핕-֬z뭷E$F… }'viuYJ#<ѣ9illl뭷}YNv0DikC l{l2:tkM qmڿ-wn3ݻ7cٲeh/R;v~4Y$Fhy߿IN*e̘1\|ř ױ3[믧(,,?F1fB3ghߥK^}Ք]v0<i+IIIM[n[bGyf޽{63S[!NZFU׿O 7q]wuOWRRb'|2GD5=7&'%=zmݖxgXDҸq_{_=C,^GyĻ:yh"~}999\ycah"r1:wS׮*i jjj:44l1c|gIس_wu/2{e&Oܴ~-[CU[---嵵 dffGy}wޡO>2S?5&"9rwygDP}$ض{nFU?§*y z 39b8y󥤤lY~}JݛwݛjRSS= M6n'L]]]m۶1dȐ-`!*"QpajjjHΠGEd2YxCXt# .e¥ˌԩ}RG ĊE YR]g6eC,ZP;0uKcI'n~|Y\gd2EmqtӧO/&nƍW%X`矿VLaaar߾}04iNIII_ڷ`c@" ;$<xh6#0kS&?Ax&O3)));jFJ'3zOEeUgAU:wQ^dO濍uw41:zr XSC0PCEq b=KZqlo _"ܽ{7|s3fpsz'++}ku]n. _N.2i_dɒv(/0ȰiS]郌V\?ZcsD03kl+ȷl0Ȳ+e"`f2ϵ 0[ޜ w`eaE`eE?gjnŬirGXFN4m_aÆB3k]ßeyoU f{*6˝Wu{kJ H.-jz-u:IDATZZo+NSxrssiܸq:_&p*((p.߿ (a^' gΜq $eRĒB&@E)YzL'JZ"=)@)bITJ.5KL.&oTviYͰǧIґQFjT1uH_4iR"'\Ӂ3ͨmtR:i3IN:0HYҎ늌AOND&-BD'b:%< g$ uze$ZKDRAEiYTjjy hHSz})zSs$PffV&SFj<S'mHI-vu|~Q6c…裏RvvvΗ ȑ#4{Vbbb qƘpRܥF]"ǘE*?~,>%J$c5kǗɾ6Ae"4iIv윒T UBV'[iBDMTTJ&JVI:q $2ݾ vFN޴ *BL3-JOeY:Α\RHjJ,"S=K7b;d#y+" YHER"|IբJԶyM&hR`S6=C)b3:,~Ix #>SzuLX)++˹{nR(=1FJ\PHnz.݄ <ȇ"\Ld>M7 9H[FC2@Dũ+_Λߣ-wJAAA1'fg'lOh3r'CWerFUwR[akET_<53`jYgr'N ;;v>b"РQw;0s2|~߽I6wf6@ s/]8B?ٌ`=I:h}zрk A į__| NT>vZ ۶mÚ5k К5km6r|||Cc3*36EDDdpDǍzty1tk!ǽXP5#:ރ3C뇽CVxT}(5~8>ձM\HZVI6)QfNk'C27en (1<EeNZ|bHZwlcN(Q68UWC:mBza{p7oXlkԩS[ &Zh)tcC*OQQQ/YZ\Lť=Z`"*.5vz\D&mڴ&J_aQ[.RҴΎř+ Hl1Iu`2Zue2RQQ1(!2z줃6j)ur~=ޓ͞{^o\~W$0Ƅ@W]]XOܺu bB.seF|c?d!8 4Yf9gTܹs1t1lꌒ#O>Gx99F ;Ν<̙3|iӦFlp1XV*݁c"\y(v0`s~@HH[c5DD }+.9$Nl]_Evm\(inXplhߏ(wODDd$"?kݏ?/Dzd'kXpx;*A$-[&Nc-Փ)#44S?(yyyݪvQbzϦ~~~eΝs=f***r[9fgNHdtwMuI)-BLMh(]$,-:sĴ ȠjD*&"c昤 g9u"j>~LR:e4uԌcEg f$ۧxPhW9s =墢"Wk].M8qK/ F/Rddꈈ$yJθmj "2j3ZucNfm6  ^Ȏ=T걤-PV4T%I&Ĥ[olq嘷θRcHh3Bp.WTTT* 0ϣbjj㙱fؾ}{ݩS^:zJT^8y8Ys'4(iZ ! jZ3ǩ[G {TYg>@<XcE6ľ1i0SG:6dpoFP@n>(J{`uGL aÆ:__MB؝0 _\Gee.Tf6/t>K_mWpheLQF('>>5(HΌ1@WxtoQQQi*++a0c;Z%oW^_淼i^Ǒ#G^wcaNH$-5BLa`k0ؒ+o !r /mEgiI8,fƉldg֍+Xx2̏&` rxeJӺyYaPQgƟ|ʻPB=Q^^+** Jo9 X7o_qҥ)g6>}ZRp̛7acaNb׃x XwuQ`U@@r2ݛccXy}&7c$b(<> Ar@ l/txs0H`uOF/zRP tȱllqܩۓJxzz0RDO]R4SJڙ!Ĥ%5Ԥ5J֌EWd1RQ/eطo /T*>cvHԸ`B+X'm˗/D<ծ^zѣcnC9l )S ƌ1NV+ P+ S~Z 6c(zێH3E\nva7ι|1[YYY@0:ᕟ̲)*EJvС}]z=՚_:?#4 w$!2RЁhgjV5[cgQ 3gSBq322rٳg ~cקOv=d0B3aX uPUU@3ƹ.77EH\&"3Ss:??/322+6l؀>@Xw_v.޽u.X%VdK.x`.\z\^܍@Di_^Cs9s&;vLct!;wnuO> ݠc.n%K4\pA!a?bcc ik(~"䭯AKޠP(rC^^EDDU|A:ssO?%BqLs++q.\zv!!!؉0y0&44ylڴ+Hڵ^}V tcLRQF};jԨӟϞ=+UXXHzȑ#o5[Te N$ "c۶m -EW_}%H9;vfΜj]jjjB-tbuM |d2_\\tѢE;v{_[g6E"^́"D{u>E"ӻTZZ*s ٳgP(իW1a9UUUaU&i1ƺɝ_  X,W*3===޸q#끣G?>l6\.rtddQUUEbtܹ~~~goݺ`ȶl ]=DWx孉F;gΜؤ$;vĉHK_m5k}9O۴i?;;;^1MqFzxx`X`j`Z2dH˗CRE"z{{J$36XSSSf2 {z tEEx`P(r+**&ǝ MNNֹ'>3ڵK/K,ALL -Z\#))taaDCc1ؽ߿lB3fpׯ5FC֝?*n1) AUYV N#DB;sɓ'ӳ0-LJm1c@OO[~mD&L@[nug^4i$lX,n4hc1zKnڵkiٲenKn%K6yFRY,t`1X/3n8s=g=hԩ$9~8EFFݻۼb  ]1륦NcZnO2Ν;GӦM7nﱷϧ##F|>rȺ4n8Z`ݻו=Cϧ'Rrrreeeш#G@eI3c-7z3>hӧMVO~iڿ+s(Oi$(..{ڈN:E>,,PTF]w1zРAŋ[Fcξ}h4`Zz59rU9K>|VZE'|222:ܶ-Zd ) 't]1܋oferJ3gtk?Wž}o>b 0&MBdd$"##P(x'q):u zfX߿Tick()); } void SetRoot(Node *node) { root = node; } void serialize(memstream_t *stream) { root->serialize(stream); } void unserialize(memstream_t *stream) { root->unserialize(stream); } private: Node *root; }; } mrboom-libretro-4.4/ai/bt/Composite.hpp000066400000000000000000000020531324457731200201550ustar00rootroot00000000000000#pragma once #include "Node.hpp" namespace bt { class Composite : public Node { public: Composite() { index = 0; } virtual ~Composite() { } void AddChild(Node *child) { children.push_back(child); } bool HasNoChildren() const { return(children.empty()); } int GetIndex() const { return(index); } void serialize(memstream_t *stream) { // TOFIX big endian Node::serialize(stream); memstream_write(stream, &index, sizeof(index)); for (int i = 0; i < (signed)children.size(); i++) { bt::Node *child = children.at(i); child->serialize(stream); } } void unserialize(memstream_t *stream) { // TOFIX big endian Node::unserialize(stream); memstream_read(stream, &index, sizeof(index)); for (int i = 0; i < (signed)children.size(); i++) { bt::Node *child = children.at(i); child->unserialize(stream); } } protected: std::vector children; uint8_t index; }; } mrboom-libretro-4.4/ai/bt/Node.hpp000066400000000000000000000024361324457731200171050ustar00rootroot00000000000000#pragma once #include #include #include "streams/memory_stream.h" namespace bt { enum Status { Invalid, Success, Failure, Running }; class Node { public: Node() { status = Invalid; } virtual ~Node() { } virtual Status Update() = 0; virtual void Initialize() { } virtual void Terminate(Status s) { } virtual void serialize(memstream_t *stream) { uint8_t s = (uint8_t)status; memstream_write(stream, &s, sizeof(s)); } virtual void unserialize(memstream_t *stream) { uint8_t s; memstream_read(stream, &s, sizeof(s)); status = bt::Status(s); } Status Tick() { if (status != Running) { Initialize(); } status = Update(); if (status != Running) { Terminate(status); } return(status); } bool IsSuccess() const { return(status == Success); } bool IsFailure() const { return(status == Failure); } bool IsRunning() const { return(status == Running); } bool IsTerminated() const { return(IsSuccess() || IsFailure()); } void Reset() { status = Invalid; } protected: Status status; }; //using Nodes = std::vector; } mrboom-libretro-4.4/ai/bt/composites/000077500000000000000000000000001324457731200176675ustar00rootroot00000000000000mrboom-libretro-4.4/ai/bt/composites/Selector.hpp000066400000000000000000000017721324457731200221670ustar00rootroot00000000000000#pragma once #include "../Composite.hpp" namespace bt { /* * The Selector composite ticks each child node in order. * If a child succeeds or runs, the sequence returns the same status. * In the next tick, it will try to run each child in order again. * If all children fails, only then does the selector fail. */ class Selector : public Composite { public: void Initialize() { index = 0; } Status Update() { if (HasNoChildren()) { return(Success); } // Keep going until a child behavior says it's running. while (1) { bt::Node * child = children.at(index); bt::Status status = child->Tick(); // If the child succeeds, or keeps running, do the same. if (status != Failure) { return(status); } // Hit the end of the array, it didn't end well... if (++index == (signed)children.size()) { return(Failure); } } } }; } mrboom-libretro-4.4/ai/bt/composites/Sequence.hpp000066400000000000000000000017561324457731200221610ustar00rootroot00000000000000#pragma once #include "../Composite.hpp" namespace bt { /* * The Sequence composite ticks each child node in order. * If a child fails or runs, the sequence returns the same status. * In the next tick, it will try to run each child in order again. * If all children succeeds, only then does the sequence succeed. */ class Sequence : public Composite { public: void Initialize() { index = 0; } Status Update() { if (HasNoChildren()) { return(Success); } // Keep going until a child behavior says it's running. while (1) { bt::Node * child = children.at(index); bt::Status status = child->Tick(); // If the child fails, or keeps running, do the same. if (status != Success) { return(status); } // Hit the end of the array, job done! if (++index == (signed)children.size()) { return(Success); } } } }; } mrboom-libretro-4.4/common.cpp000066400000000000000000000464461324457731200165160ustar00rootroot00000000000000#ifdef _WIN32 #include #else #include #endif #include #include #include "mrboom.h" #include "common.hpp" #include "MrboomHelper.hpp" #include "BotTree.hpp" #include #ifdef __LIBSDL2__ #define LOAD_FROM_FILES #endif #ifdef LOAD_FROM_FILES #include "streams/file_stream.h" #include #endif #define SOUND_VOLUME 2 #ifdef __LIBSDL2__ #define NB_WAV 21 #else #define NB_WAV 16 #endif #define NB_VOICES 28 #define keyboardCodeOffset 32 #define keyboardReturnKey 28 #define keyboardExitKey 1 #define keyboardDataSize 8 #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) #define keyboardExtraSelectStartKeysSize 2 #define offsetExtraKeys keyboardDataSize * nb_dyna + keyboardCodeOffset #pragma GCC diagnostic ignored "-Woverlength-strings" #pragma GCC diagnostic ignored "-Warray-bounds" #ifdef __LIBRETRO__ #include "retro_data.h" #include "retro.hpp" #ifdef LOAD_FROM_FILES #include