From 01d17f241a5e0078106d9e6cf5d87a48caf37db1 Mon Sep 17 00:00:00 2001 From: David Bloomin Date: Fri, 4 Sep 2026 05:29:18 +0000 Subject: [PATCH 1/5] bc20: the Battlecode 2020 rule set as a Nim year module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `src/battlecode/years/bc20/` — world, flood, pollution, blockchain, cows, maps, knobs, rules and the two chassis — plus the committed data the sim reads: 18 converted `.map20` maps, the JDK-generated float32 water-level table, and the 2020 sprite atlas cut from the official client. The year-neutral machinery (`match`, `results`, `replay`, `render`, `broadcast`, `decide`, `server`, the wasm entry) now goes through `years/dispatch.nim`, the one place a year is named; `sheet.nim` is thinned to the envelope and each year's knob table moves to `years//knobs.nim`, with the tolerant-parsing primitives extracted UNCHANGED into `sheet_common.nim`. bc26 semantics are untouched: every existing test passes in debug and release. --- data/atlas_bc20.json | 1 + data/atlas_bc20.png | Bin 0 -> 9915 bytes data/bc20/water_levels.json | 1 + data/maps/bc20/ALandDivided.json | 1 + data/maps/bc20/BeachFrontProperty.json | 1 + data/maps/bc20/CentralLake.json | 1 + data/maps/bc20/CentralSoup.json | 1 + data/maps/bc20/Climb.json | 1 + data/maps/bc20/Constriction.json | 1 + data/maps/bc20/Hills.json | 1 + data/maps/bc20/Hourglass.json | 1 + data/maps/bc20/Infinity.json | 1 + data/maps/bc20/Maze.json | 1 + data/maps/bc20/OmgThisIsProcedural.json | 1 + data/maps/bc20/Spiral.json | 1 + data/maps/bc20/Squares.json | 1 + data/maps/bc20/TheHighGround.json | 1 + data/maps/bc20/Toothpaste.json | 1 + data/maps/bc20/TwoLakeLand.json | 1 + data/maps/bc20/WateredDown.json | 1 + data/maps/bc20/maptestsmall.json | 1 + replay-viewer/bc_replay.nim | 13 +- src/battlecode/baselines.nim | 94 +- src/battlecode/broadcast.nim | 190 ++++ src/battlecode/decide.nim | 141 ++- src/battlecode/match.nim | 128 ++- src/battlecode/render.nim | 153 ++- src/battlecode/replay.nim | 46 +- src/battlecode/results.nim | 68 +- src/battlecode/server.nim | 24 +- src/battlecode/sheet.nim | 360 +------ src/battlecode/sheet_common.nim | 92 ++ src/battlecode/years/bc20/blockchain.nim | 62 ++ src/battlecode/years/bc20/chassis/boc.nim | 59 ++ .../years/bc20/chassis/designschool.nim | 29 + src/battlecode/years/bc20/chassis/drone.nim | 210 ++++ .../years/bc20/chassis/fulfillment.nim | 22 + src/battlecode/years/bc20/chassis/hq.nim | 91 ++ src/battlecode/years/bc20/chassis/kit.nim | 170 +++ .../years/bc20/chassis/landscaper.nim | 217 ++++ src/battlecode/years/bc20/chassis/lattice.nim | 71 ++ src/battlecode/years/bc20/chassis/miner.nim | 195 ++++ src/battlecode/years/bc20/chassis/netgun.nim | 12 + src/battlecode/years/bc20/chassis/pathing.nim | 106 ++ .../years/bc20/chassis/scaffold.nim | 133 +++ src/battlecode/years/bc20/chassis/signals.nim | 76 ++ src/battlecode/years/bc20/constants.nim | 123 +++ src/battlecode/years/bc20/cows.nim | 75 ++ src/battlecode/years/bc20/flood.nim | 103 ++ src/battlecode/years/bc20/knobs.nim | 178 ++++ src/battlecode/years/bc20/maps.nim | 155 +++ src/battlecode/years/bc20/pollution.nim | 35 + src/battlecode/years/bc20/rules.nim | 231 ++++ src/battlecode/years/bc20/world.nim | 998 ++++++++++++++++++ src/battlecode/years/bc26/chassis/kit.nim | 4 +- src/battlecode/years/bc26/knobs.nim | 248 +++++ src/battlecode/years/bc26/rules.nim | 10 +- src/battlecode/years/dispatch.nim | 262 +++++ src/battlecode/years/registry.nim | 9 +- tests/test_determinism.nim | 24 +- tests/test_perf.nim | 2 +- tests/test_replay.nim | 24 +- tests/test_viewer.nim | 14 +- tools/JavaWaterLevels.java | 64 ++ .../convert_maps_bc20.cpython-311.pyc | Bin 0 -> 18695 bytes tools/build_sprite_atlas_bc20.py | 106 ++ tools/convert_maps_bc20.py | 298 ++++++ tools/gen_year_constants.py | 159 ++- tools/map_pools_bc20.json | 9 + 69 files changed, 5421 insertions(+), 492 deletions(-) create mode 100644 data/atlas_bc20.json create mode 100644 data/atlas_bc20.png create mode 100644 data/bc20/water_levels.json create mode 100644 data/maps/bc20/ALandDivided.json create mode 100644 data/maps/bc20/BeachFrontProperty.json create mode 100644 data/maps/bc20/CentralLake.json create mode 100644 data/maps/bc20/CentralSoup.json create mode 100644 data/maps/bc20/Climb.json create mode 100644 data/maps/bc20/Constriction.json create mode 100644 data/maps/bc20/Hills.json create mode 100644 data/maps/bc20/Hourglass.json create mode 100644 data/maps/bc20/Infinity.json create mode 100644 data/maps/bc20/Maze.json create mode 100644 data/maps/bc20/OmgThisIsProcedural.json create mode 100644 data/maps/bc20/Spiral.json create mode 100644 data/maps/bc20/Squares.json create mode 100644 data/maps/bc20/TheHighGround.json create mode 100644 data/maps/bc20/Toothpaste.json create mode 100644 data/maps/bc20/TwoLakeLand.json create mode 100644 data/maps/bc20/WateredDown.json create mode 100644 data/maps/bc20/maptestsmall.json create mode 100644 src/battlecode/sheet_common.nim create mode 100644 src/battlecode/years/bc20/blockchain.nim create mode 100644 src/battlecode/years/bc20/chassis/boc.nim create mode 100644 src/battlecode/years/bc20/chassis/designschool.nim create mode 100644 src/battlecode/years/bc20/chassis/drone.nim create mode 100644 src/battlecode/years/bc20/chassis/fulfillment.nim create mode 100644 src/battlecode/years/bc20/chassis/hq.nim create mode 100644 src/battlecode/years/bc20/chassis/kit.nim create mode 100644 src/battlecode/years/bc20/chassis/landscaper.nim create mode 100644 src/battlecode/years/bc20/chassis/lattice.nim create mode 100644 src/battlecode/years/bc20/chassis/miner.nim create mode 100644 src/battlecode/years/bc20/chassis/netgun.nim create mode 100644 src/battlecode/years/bc20/chassis/pathing.nim create mode 100644 src/battlecode/years/bc20/chassis/scaffold.nim create mode 100644 src/battlecode/years/bc20/chassis/signals.nim create mode 100644 src/battlecode/years/bc20/constants.nim create mode 100644 src/battlecode/years/bc20/cows.nim create mode 100644 src/battlecode/years/bc20/flood.nim create mode 100644 src/battlecode/years/bc20/knobs.nim create mode 100644 src/battlecode/years/bc20/maps.nim create mode 100644 src/battlecode/years/bc20/pollution.nim create mode 100644 src/battlecode/years/bc20/rules.nim create mode 100644 src/battlecode/years/bc20/world.nim create mode 100644 src/battlecode/years/bc26/knobs.nim create mode 100644 src/battlecode/years/dispatch.nim create mode 100644 tools/JavaWaterLevels.java create mode 100644 tools/__pycache__/convert_maps_bc20.cpython-311.pyc create mode 100644 tools/build_sprite_atlas_bc20.py create mode 100644 tools/convert_maps_bc20.py create mode 100644 tools/map_pools_bc20.json diff --git a/data/atlas_bc20.json b/data/atlas_bc20.json new file mode 100644 index 0000000..5b4fc86 --- /dev/null +++ b/data/atlas_bc20.json @@ -0,0 +1 @@ +{"sprites":{"cow":{"h":16,"w":16,"x":0,"y":0},"design_school_blue":{"h":16,"w":16,"x":16,"y":0},"design_school_red":{"h":16,"w":16,"x":32,"y":0},"drone_blue":{"h":16,"w":16,"x":48,"y":0},"drone_blue_carry":{"h":16,"w":16,"x":64,"y":0},"drone_red":{"h":16,"w":16,"x":80,"y":0},"drone_red_carry":{"h":16,"w":16,"x":96,"y":0},"fulfillment_center_blue":{"h":16,"w":16,"x":112,"y":0},"fulfillment_center_red":{"h":16,"w":16,"x":0,"y":16},"hq_blue":{"h":16,"w":16,"x":16,"y":16},"hq_red":{"h":16,"w":16,"x":32,"y":16},"landscaper_blue":{"h":16,"w":16,"x":48,"y":16},"landscaper_red":{"h":16,"w":16,"x":64,"y":16},"miner_blue":{"h":16,"w":16,"x":80,"y":16},"miner_red":{"h":16,"w":16,"x":96,"y":16},"net_gun_blue":{"h":16,"w":16,"x":112,"y":16},"net_gun_red":{"h":16,"w":16,"x":0,"y":32},"refinery_blue":{"h":16,"w":16,"x":16,"y":32},"refinery_red":{"h":16,"w":16,"x":32,"y":32},"soup":{"h":16,"w":16,"x":48,"y":32},"vaporator_blue":{"h":16,"w":16,"x":64,"y":32},"vaporator_red":{"h":16,"w":16,"x":80,"y":32}},"tile":16} diff --git a/data/atlas_bc20.png b/data/atlas_bc20.png new file mode 100644 index 0000000000000000000000000000000000000000..0317af606ac5dd2237186f0248c2d9630a23ebdc GIT binary patch literal 9915 zcmV;sCPdkZP)1UgWFdQQa+jHTet*mal8``JZSD8yISqqweQWm zyrSjLKmXUCZQ7JO`{>c)6;64a0b3)TmK6#mC1-R<2yRdi?nDBLVHP#~z!Xlaup= zw+ayv8Zj9PkffxfR<>>Tsi~=nj2JP(r|Wv$f&~jAwr%%&{PD*Z1IL;$H{S*Chwc-g zBrX~q_*13MQ?AumIiRp`OGl+b*F#i?C40bH2bKHb(5Id-Io}7qJ4`5nxPj5=PtQ|I z6#y$?z?QXpu&I~Q-N^Q5A!Vd;R0WDziT!>VFgu$5!u}{I71@?A`3CK2( zNw5cK?@fG@_!&xBpD3J1m9n=hWi^GLI(4dS+O$b>a&lzl%9XNb&t8c{%H`8fMgII} zk>{Tm*|R4iH8mx&apPuLv0{a+S+hnqZQ3MLr%n~7O`GNe7qD{n7cC|`W>h3wwF+YW_7vDK?rhegD?>Z+?g0j7wE^3-vylbEh)+Qyr2zF8J6 zT4XODvgT*HQVW$3Oe5{AA)p`;L3=5ua%`hNsT9H}sqb zcUQv_fz1L-1^y;*<9;yicgI@Mz_D$J55WflxdO{P{0+^*_k?+OL-i7n%^*ub{swX* z>^I({S$x_0equUogy4uwMYi!Z)t8`Gyxk3IGH|F5n{Em?Pe1)+|7V_gVav{)JO5(a_Mbgpg2wP= zrKOtB=cBs1nl`V$t_5zm!I}HaGyR```stJKozonfv~7$0h4-TeWM*wr;Iz8#n5z36HU|1 z)hm_nW=-p=ot*w>)-G9qm<%Tzxoy1wr%5r3(hANvv}_L-_Y#>9}^~a zWY(-ZIB?(~GiJeLBc*D*~K!x2%(Dk|n*^W>AIBYO6{CeG(;HQ+}-`pJuL zyrIpQGlyqhc;U$Ak3Y6vd-TyWwyj&|-)!5oYSk(c_}kE-L-#CQy40uZ`rIvBwk+Jc z_aCqK@81u<-=wIhm?=}HP*8A;#~$lQR@P6jES)dE+#y@HZk?!U+L7J6ck5o5Id{vJ zEerp#ckk={1`NRT`zbCeV#@vZQ&dpES&u)?HRqj&9*eQ#%P&vD*J%!{jzCo+gkOb< z<&fmSV^vU@37T~(Cv^IdA)&x4Ow&ZuG}^UmM?yjZy?b9k*G?vFQ-t$6CDFBuMyR?Q85tP>SeAuh7&wk|LK<~V zTfThTw&g&VNx%R7=zV|s(@R@(b4R44rtY7&WXWHL^zAz@`|Y>?c;Rig{kFKYG!fW- z<&{^OOP4OKb{r?$w(V{{pHgqVwLq$>s@Sw?4TXh8Jn_WioO^B-rKKgbXpw|cO2uNa zZAz)X*LjF++qSzYzh5nQ^G&G=g;=v`6GerEJpRNJWSw&kB_$;!B_}tEuiuVV1Mfgw z1o%gt1G~IOwVM=x>}(Bix=$SPx4$oBV6AFadR*wE*IC-(4am;Uwrj)TMtg^9;GH;E z?bqwyTeeO1pPfBj1DqifyZ?=ae?Q)ToUTH={?St2=x|d1fk1%Ot5@^pn{Seun##TR z+{2e&@1pdJ&zL*$LTV0d=gvQ@;g=6R!lsRznERKxoPGA${NfkC!0-2;@TP6Y@l6{% z*qQtJ=Sh<$Oeptj+M*X;e6gbfUt(fn^^~WcDlaK0cw_Mc58UL~b`78=Po9jIgpdv$ zIwWRiuVTZ735Y;`{$gg%oJzND-6$?DM%U{_jEQ5%j@1_~T&TRff^_K6A#qi9Hscm8 z0@qkGb>__Dc+D`H!ds1uW#ojH51`o~(bH#PvH?iR)XRHb_xJMhA}6K0YdC3q+gOJLs7y<$u4fXSq{U9z&FQ@WPH2gJ>UVtarcP&;0rG$;`|| z*L9wL`e}ai+uyS7)n~})-WPN~Ke-}-K7IOd)5M=~>*UG2@WKlW7%+gixVU=ZSoD#c z9Dp6bnLs2`S-IYm!YEZ$XO8UBCA@6!-g9stMu3>7(rl=pyxd1>>UE@~Kr~v7X&$1w zI*f>5+cvgs<2Vig@4WMlT}K_-6{d-=xR`N39PTp84+N+Qg_?tJq$xSoYHO@LGV~D( zpl_=UZ_jz+zV~bQ7n3GR6<2{mDb*}KLb>DfFZI8nRo}Podt%P<{?jOyVx_8->oC{r zKNgD-7Z=CXS6_{$X*@P(4t_&t)8Rsn{%r}dsw(2o=tO>A9;s=mTyxDeY~H+?Xf%4< z1T<{6QmR};P)dbsGk;2{=(4?gQA$PXc;9s-F+P-@o+xEyZ%Z_)7r(68S(rR#CT z#ref?9FmffBq=FLfKWqxd>{}?Y~5PkY1dA45&0q*t%l{`+j#s)bSN zt=N)(k`DQ9ZK1lhUjL`+Jvlj9&O7fs9(dpZG)?28i!P#LhYtMlrB|?QN1+scO-X5K zDUKstd+oJ^LLqLy{dTr*-`?bP)rrukZkTk^zS^XW1={_=V9z;$Kn%-@k(!!;t_LE~ zXvD9SLPRhOgOZXGHf`GU8{n}z2^~v=`+Ek1b7Bbz#B7_4q$C2m9*IPwr^8$Sj!E!x z9Nu6kQ2$vbvbp_VbkRkUm6b(7K>};_AU&a4lr-%of;#kA+YaX8robO$D0MH*8 z5DW%edERGpE~b0zp=O%q`577Q7MdpAfVM!UmlSC>zFkJfLQJ!{_*!psq^`IAgWsPq z3z+U|i)P*baeQm|Lk1kjHi@eE-WD<&!sV-5Qg-?y_SSUF68np^m+9VeFN6 z->vS~tJg_2K@vN-f$XB0Ml$Yr+060`t_4g&z^R4;6RDCZ$HZdl9e)>T*V%{ zMmv}z;0T0lhzdk!x#~38JV^_PYT7bFAdvNJzkV{jUq2Zh2xL81=a{Np^-ZH83!W7i zE-*X`o~v_A{qvLW)^sRkF74X&L+R0@;#N&5Wow#d`Tc(Tth3Il9CG#5DgD>1F^;|d zx?1?hKdLoDhA8dMJGrq>pSUjl`|Iz#^N#(^H{V1xO|z9!b;BIp3sHVPWy++VKm72G z7kBJvE`XY3pYNa!L-XLxFM-R!PV{(i)foQfDN}xa%fk;}9M`eqf+!>(_4)LRAn!x? z(uON+XbgW`gNuYp%gdK4rHJ?WG&35FnN?NmqL3zz6IG|v{~;({>Rvlu12YEZI*6vZ z$pm$BxGAc4Z+SQvl(pX9gYXLzxIG#@*gX`=9~27Z4~j(gcLVMQ@zwe+%B2~Vz{3J- zy}t*4;R@sJ2cdgD49bT=`=J|fx40(opY(mkX|UGPwB$vdJHK~%%a)@`O*83pLqz(D zh>Av|l9Q8@IQqKl#Ocuke_R|H@4rv_jvZLNd#kW%%8}i>+lxqF%d))KACnW=5#@!4 zT^29yZ-+u1o$BhUvtj3qwQEDzcJ@NgjQBj~#dc;Wk!%-y$dbeEc%=_7rm^&J} zOox$a5dIJnKZem7RH);5kK^jTod}C`oq=A80!`zhUcH_j^X<3Jt*NP#s^j9?4g)y* z>`z|VzklMo3ock7xw(C6)qOiJ?=`TlbN?)iap2ZTP~8@W0ek|lOoVj{K>GZ1vu`N% zo+aXpZPVu7tJ2d)MIzytPdUV^sZ*!Q)mL9lI2W72LkHUKocIvdF>cD|Z5*?=}P!0?}48=X* z+tL%-ul`K}RsuZwG(32?Q>UtK2M#3mbsP$SYvFJddVC9|s(u*SSW5!#1P*$Ik*|R< z8mu>f%4S3lC*chkZNW+S9zA4;VQ__M@=z?s<1RdJ3bcPDE{-|TD4iXLWu9P+*}Pe; za?OUD&=D8{Tmd`;E#u{*eZ-EYG!QK$mOdw==18OO-eyv!R_bOrJ( zt7Kp(ly`P~e0xOD{XR2h)a8>WOqjrtBS%=Dlf!SHdWyoQo~C2BZv1LMe-0fuz>FC) zNK8zuE#Sa3O-^tDTxFUWlSJaqb)21y9lJ@7k8csp%gfh`i;Il)>&GH8{uW4{1aEJE zk}F`a2067k^Q+p!BtM+HPt!O!cC1Q@kB{Z&m#E@Zb^ViAHe!@xN4D$MIo!9a#Ar(9VbH7DOFa=o;Xa z0}^w67<8qG1cMlk!>!>kVNIh_gbL3DRCxP-IcN}nS-)OYdQucS5kV30CiYe!?3tVj zud@no-!Fl`DEF8T^oQc(`=%Y5iQ1R|^H;ciS z3F;F_ssd-N8wq%;R|~syW4r{huAbsDMXX`f)!Xi?Ltee#nDeheSlp3+DF(;!77W`4 zr^5rvQA$E0#I|fvv0zY+Mx*RjiY$-cLWG29l%-11rK*ZJZ*ptxrz0-M5mRmr;Zb0> zmm!VwPD=omdL{g|_?AkU1KYQsxl7aH>_DKkzo6jd;ROYAvXhgqJaXolPke3L;iSsS zHM76{_TIZEOj_vmzPduT?Ze6p*4lmk@g zdJ79u9Hrj!-FV{`Qd3i-zCN)dA$s+BqsMTpnGxTfbK+{u5?3v)xb&sm$!jd?_A-6*C=Bz)=Ky~A8* zz-UQ&`dx#7TT4pj?I|gl2SB%O`+R{w*UnW{`AUQmP4kI}g>foAxl^}ppoFWgh-;Eh z2CfJMx<6>!<>|IvW+)XFkzTy@*2NfxTfZ2MmLg&Un{{XjoE3zD-@x1NLdC1+!-M6J zUIs=D$hnTgMQ^=@-!KrMCK^>C5n~@{DL_XE2H~3z;N42(8Zd^XrjGrvWy=X_@7}8r zvBRK`1$&%Z93qQ>m%V{=8f4(afMGv@4{rc(@(v$JDwnvL9_4{pn}0KjM{Y8p+nOL z09Bw73~n|P6DuVr=eG}lu7MJzj%QKDI3=I_*3#4R$w1pP&wR08P0g3&!<)mqGN{`{XIa<+^uQct?TY8b)#Bd2M+ptZrJJBepA63as3XI>)|7Cx0^Y1@t!Go-OEqt&0uI7 z%P!a3Z#?$cV=5~v3)2(N-Me=)Y0{*Ie6mtX5g#9aoKK!LYZkk9?E(l?RD5VgB72C% z@^HOQKbrOqUAta@WyR3-`B;{q)NVw+1rF%&5fDlN0w06<4>tiqic*w_kgeMPrR6n8jyzOqnn|&;vQ<)9x%?K;mO4)K-R7jp%Aw{# zC{2af3Xo6X77dn0p;`FcTonV+be-9?WKd!v5{d94@Vc(oCEN(dV$9jGgX%gn6Z3q5 z*>#AaS`F}egYLp!f~fk`#A37dp}q$`Sp%;0)J}8W{;jj zS`|226RV^#l~@n2L6vhNd}$8;e{K6$3PO}eV4Nffg9i`Ra&mI4yY9NHZy*pzu`KKQ zgoK3TP$+bdrfDs0+x9*A z^NsoCFMqlDs;jP=@A`$b3kHMphYcIHS=aS3Ua6M$Z@~W`&G=^~e=z7dE(Q4BdvN*1 zFu8?G)ESm3%|F6{m#(Q8nF!o-!Y7|5)Fb#+YDlf)RyzxZb?|ZOqRU)=9|8~a1MESt1E2#(rQiPXl3Qjs&Kf3q@@1T z)HqmHQ}~lYpEJ)SMasO1PCjnCM1X5G0&#JDj&aLz2O{P~Ww_ho>zXAEh>F}CJ zrgziL#s611fyen|FH_Y%1>P%$P6BDV8;1N?Q^qC{E4gjk-xsxNHPMVjL^RD%x?Y2b ziDl_1<>2#$DJpt9T2L^%jZ%7tA*CDdiNlZLIG!_%ZB$b?W zaY(OD(tmMZh4#oJb-ei0RL(6e4Q)zHOg@^PPAnSblDs^2CL}n=TDBw>i^*mA`KqT# z=##`m{YYh{rs@wqqk5&Bfzq?%1OAks4Y(*JJ-roEYq9IgAqp5$}^x4)iapcI740vX*dyGY=7*7)zy8`9{UB-)KDmQJH zu>zWd|G@;n%02nmC?F_c6u5*$UI`4$gRbqphc)Y@BW*jRIgX~HQA2c1qdaCHqHFOe z9J@?3!%*cBQ3aKi#gESP)kYxh;Fp$C1Da;rO4ZbePt%Bq5Q)Szr4*GQ8mN$n%B!p_ z_PMc0Endz{CPh%$w_kQyW<~lD1F*21=0Aj z=u+CX{*fvvexCCZR|Yy~w0U*?;e!9CV2hmiNYZgK-merTd3ltzP9+_Owg(PyKd4uL z!i@C5MaZk`4j-$BZ`YxP7LLW*zM8#Ee7aA5+UH`eRjafUZaYr1 zv9>K*pjA|y9LnjsZrQe7HF4r~zq{(HhlR(zGA@ArN7HrwW>MzA4C9JnlNxC?1}@sHTYkK;d3DSN;vWad>w|QH8A^L zxGN7Xd)TG)V$`dN7IalgD7=ARpS_vij~ED=#h*TTmzkem!W%zc#!m+J@?%{#>E`*X zM!fRXp)3(8Gn9hVRHU?&h+(k9=cC`y9!wq93k;LTKH9`R**h42&4tYFf1aPHHRYxjFd`lkHH8oV33G8UwhA}PLFnjG@rA$3+q@KV7YWu)5$@>g?*4Kke5rv-Xh;V) zXpRy|5YXBjI<)aR=&RhYW#ewp6eP&0@jr+FSb+M%^9O>!bT^Kz(tyoiR)YK%9?;!~ zs7p;A*8oZ>B4LY;9SwY=#!*-pqp&cKV?{;uPRXEZ=oqRB3pkpWXB5TdOZ@h4%<}T` z<0&zvX%smQ31@YJQ9q+JKcBq9LW+xu=$&!~6+=f;*$BRtD&2aZ>|kcwz~I8|p=?tl z<-Cr*lnyQ3#g<Rhfeg7sIc6q1lO!i6#n(>VR_tp6W zx}Ln%(?EMfk`a;Sd?Vn8ass7KeUc-FSM1iOaq>e>PUV^wrePVSRdUAiSJhcs8ioOt z)itc%e}pqvl)EqW8M;+eUBNadY?s&7og!8oSaGFA(q;Mos5k-5G^wnvA$#8u&RkJ; zD!c&dKz{fj(5sD~lx=oo^^OD4WB>;#57jtB!yFF*n+m}+rBsU!9Wnx;P~TB4TI@$D zMP=oHQN_g@0!pbCBB%b+se%?685x10hYycR@;Vv@qjp0;gBJFGQ5yJEiJ)fl$+gF9 zt0*lTt03JUHdW!KZ)#3fahYvrIn3|fJpzDrf+nA9^64lkSb@;$Oxg_m>9UIuF^JAKA zuhX;^KAdBc9v@aG1l=JNvq>;(-+fdLwyhObRj-Y@MUMc!G`KDqeC3)Z@k#M@?yFh& z<_M}f4IiLSn>I}zdE}Ab3>q|O=8u0ozz~UG+ZK6w#eDYJHf!(Ry$|7X@v4z*WIg=A z;lquvQdpKn@sT5JTeIeL`2W=c@ZZ=}Gl`&v8gXcPdU|zHQIYMI>luJ4iHV|DRaM0U tt7-b96!q|i`|UqW5J^mI4*oyh{y)=6q7h;}bQ%Bv002ovPDHLkV1hFIx3d5M literal 0 HcmV?d00001 diff --git a/data/bc20/water_levels.json b/data/bc20/water_levels.json new file mode 100644 index 0000000..eab0bb1 --- /dev/null +++ b/data/bc20/water_levels.json @@ -0,0 +1 @@ +{"source":"battlecode20 GameConstants.getWaterLevel","encoding":"float32 bit pattern, 8 hex digits, big-endian","max_round":1500,"levels":["00000000","3b4e397b","3bce7030","3c1afd29","3c4edd92","3c816ca8","3c9b7830","3cb59161","3ccfb839","3ce9ecb8","3d02176e","3d0f3f53","3d1c6e08","3d29a38f","3d36dfe6","3d44230d","3d516d02","3d5ebdc6","3d6c1558","3d7973b6","3d836c70","3d8a226b","3d90dbcb","3d979891","3d9e58ba","3da51c48","3dabe339","3db2ad8d","3db97b44","3dc04c5e","3dc720d9","3dcdf8b5","3dd4d3f3","3ddbb291","3de2948e","3de979eb","3df062a6","3df74ec0","3dfe3e38","3e029886","3e06139f","3e099065","3e0d0eda","3e108efb","3e1410ca","3e179446","3e1b196e","3e1ea042","3e2228c2","3e25b2ee","3e293ec4","3e2ccc46","3e305b72","3e33ec48","3e377ec8","3e3b12f1","3e3ea8c4","3e42403f","3e45d962","3e49742d","3e4d10a0","3e50aeba","3e544e7b","3e57efe2","3e5b92f0","3e5f37a2","3e62ddfa","3e6685f7","3e6a2f98","3e6ddadd","3e7187c5","3e753650","3e78e67e","3e7c984d","3e8025df","3e820069","3e83dbc3","3e85b7ec","3e8794e6","3e8972af","3e8b5148","3e8d30b0","3e8f10e7","3e90f1ec","3e92d3c0","3e94b662","3e9699d2","3e987e10","3e9a631b","3e9c48f3","3e9e2f98","3ea0170a","3ea1ff47","3ea3e851","3ea5d227","3ea7bcc8","3ea9a834","3eab946b","3ead816d","3eaf6f39","3eb15dcf","3eb34d2f","3eb53d58","3eb72e4b","3eb92006","3ebb1289","3ebd05d5","3ebef9e9","3ec0eec5","3ec2e467","3ec4dad1","3ec6d201","3ec8c9f8","3ecac2b5","3eccbc37","3eceb67f","3ed0b18b","3ed2ad5c","3ed4a9f2","3ed6a74c","3ed8a569","3edaa449","3edca3ed","3edea453","3ee0a57b","3ee2a765","3ee4aa11","3ee6ad7e","3ee8b1ab","3eeab699","3eecbc48","3eeec2b5","3ef0c9e3","3ef2d1cf","3ef4da7a","3ef6e3e3","3ef8ee09","3efaf8ed","3efd048f","3eff10ed","3f008f03","3f0195ee","3f029d37","3f03a4dd","3f04ace1","3f05b542","3f06bdff","3f07c71a","3f08d091","3f09da64","3f0ae493","3f0bef1e","3f0cfa05","3f0e0548","3f0f10e5","3f101cde","3f112931","3f1235e0","3f1342e8","3f14504b","3f155e07","3f166c1e","3f177a8e","3f188957","3f199879","3f1aa7f4","3f1bb7c8","3f1cc7f4","3f1dd878","3f1ee954","3f1ffa88","3f210c13","3f221df6","3f233030","3f2442c0","3f2555a7","3f2668e4","3f277c77","3f289060","3f29a49e","3f2ab932","3f2bce1b","3f2ce359","3f2df8eb","3f2f0ed2","3f30250c","3f313b9b","3f32527d","3f3369b3","3f34813b","3f359917","3f36b145","3f37c9c5","3f38e297","3f39fbbb","3f3b1531","3f3c2ef8","3f3d4910","3f3e6379","3f3f7e32","3f40993c","3f41b495","3f42d03e","3f43ec37","3f45087e","3f462515","3f4741fa","3f485f2e","3f497cb0","3f4a9a7f","3f4bb89c","3f4cd707","3f4df5be","3f4f14c2","3f503412","3f5153af","3f527397","3f5393cb","3f54b44b","3f55d515","3f56f62a","3f58178a","3f593933","3f5a5b27","3f5b7d64","3f5c9fea","3f5dc2ba","3f5ee5d2","3f600932","3f612cdb","3f6250cc","3f637504","3f649983","3f65be49","3f66e356","3f6808a9","3f692e43","3f6a5422","3f6b7a46","3f6ca0b0","3f6dc75f","3f6eee52","3f701589","3f713d04","3f7264c3","3f738cc5","3f74b50a","3f75dd92","3f77065d","3f782f69","3f7958b7","3f7a8247","3f7bac17","3f7cd629","3f7e007b","3f7f2b0d","3f802af0","3f80c079","3f815621","3f81ebe9","3f8281d0","3f8317d7","3f83adfc","3f844440","3f84daa3","3f857125","3f8607c4","3f869e82","3f87355e","3f87cc58","3f886370","3f88faa5","3f8991f7","3f8a2966","3f8ac0f3","3f8b589c","3f8bf063","3f8c8845","3f8d2044","3f8db860","3f8e5097","3f8ee8ea","3f8f8159","3f9019e3","3f90b289","3f914b4a","3f91e426","3f927d1d","3f93162f","3f93af5b","3f9448a1","3f94e202","3f957b7c","3f961511","3f96aebf","3f974886","3f97e267","3f987c61","3f991674","3f99b0a0","3f9a4ae5","3f9ae542","3f9b7fb7","3f9c1a44","3f9cb4ea","3f9d4fa7","3f9dea7b","3f9e8567","3f9f206b","3f9fbb85","3fa056b6","3fa0f1fe","3fa18d5d","3fa228d2","3fa2c45d","3fa35ffe","3fa3fbb5","3fa49781","3fa53363","3fa5cf5b","3fa66b67","3fa70789","3fa7a3bf","3fa8400a","3fa8dc69","3fa978dc","3faa1564","3faab1ff","3fab4eae","3fabeb71","3fac8847","3fad2530","3fadc22c","3fae5f3b","3faefc5c","3faf9990","3fb036d6","3fb0d42e","3fb17199","3fb20f14","3fb2aca2","3fb34a41","3fb3e7f0","3fb485b1","3fb52383","3fb5c165","3fb65f58","3fb6fd5b","3fb79b6e","3fb83991","3fb8d7c4","3fb97606","3fba1458","3fbab2b9","3fbb5128","3fbbefa7","3fbc8e34","3fbd2cd0","3fbdcb7a","3fbe6a32","3fbf08f7","3fbfa7cb","3fc046ac","3fc0e59a","3fc18496","3fc2239e","3fc2c2b4","3fc361d6","3fc40104","3fc4a03e","3fc53f85","3fc5ded8","3fc67e36","3fc71d9f","3fc7bd14","3fc85c94","3fc8fc1f","3fc99bb5","3fca3b56","3fcadb00","3fcb7ab5","3fcc1a74","3fccba3d","3fcd5a10","3fcdf9ec","3fce99d1","3fcf39c0","3fcfd9b7","3fd079b7","3fd119c0","3fd1b9d0","3fd259ea","3fd2fa0b","3fd39a34","3fd43a65","3fd4da9d","3fd57adc","3fd61b22","3fd6bb70","3fd75bc4","3fd7fc1e","3fd89c7f","3fd93ce7","3fd9dd54","3fda7dc7","3fdb1e3f","3fdbbebd","3fdc5f40","3fdcffc9","3fdda056","3fde40e8","3fdee17e","3fdf8219","3fe022b8","3fe0c35b","3fe16402","3fe204ac","3fe2a55a","3fe3460b","3fe3e6bf","3fe48776","3fe52830","3fe5c8ec","3fe669aa","3fe70a6b","3fe7ab2e","3fe84bf2","3fe8ecb9","3fe98d80","3fea2e49","3feacf13","3feb6fde","3fec10aa","3fecb176","3fed5242","3fedf30f","3fee93dc","3fef34a8","3fefd574","3ff07640","3ff1170b","3ff1b7d5","3ff2589e","3ff2f966","3ff39a2c","3ff43af1","3ff4dbb4","3ff57c75","3ff61d35","3ff6bdf1","3ff75eac","3ff7ff63","3ff8a018","3ff940ca","3ff9e178","3ffa8224","3ffb22cc","3ffbc370","3ffc6410","3ffd04ac","3ffda544","3ffe45d8","3ffee667","3fff86f1","400013bb","400063fc","4000b439","40010474","400154ac","4001a4e1","4001f514","40024543","4002956f","4002e598","400335bd","400385e0","4003d5fe","4004261a","40047631","4004c645","40051655","40056661","4005b669","4006066e","4006566e","4006a669","4006f661","40074654","40079643","4007e62d","40083612","400885f3","4008d5cf","400925a6","40097578","4009c546","400a150e","400a64d1","400ab48e","400b0446","400b53f9","400ba3a7","400bf34e","400c42f1","400c928d","400ce223","400d31b4","400d813f","400dd0c3","400e2042","400e6fba","400ebf2c","400f0e97","400f5dfd","400fad5b","400ffcb3","40104c05","40109b4f","4010ea93","401139d0","40118906","4011d835","4012275d","4012767d","4012c596","401314a8","401363b3","4013b2b6","401401b1","401450a5","40149f91","4014ee76","40153d52","40158c27","4015daf3","401629b8","40167874","4016c728","401715d4","40176478","4017b313","401801a5","4018502f","40189eb1","4018ed29","40193b99","40198a00","4019d85e","401a26b4","401a7500","401ac343","401b117d","401b5fad","401badd4","401bfbf2","401c4a07","401c9812","401ce613","401d340b","401d81f9","401dcfdd","401e1db8","401e6b88","401eb94f","401f070b","401f54be","401fa266","401ff004","40203d98","40208b21","4020d8a0","40212615","4021737f","4021c0de","40220e33","40225b7d","4022a8bd","4022f5f1","4023431b","40239039","4023dd4d","40242a56","40247753","4024c446","4025112d","40255e08","4025aad9","4025f79e","40264458","40269106","4026dda8","40272a3f","402776ca","4027c34a","40280fbd","40285c25","4028a881","4028f4d1","40294115","40298d4d","4029d979","402a2598","402a71ac","402abdb3","402b09ae","402b559c","402ba17e","402bed54","402c391d","402c84d9","402cd089","402d1c2c","402d67c3","402db34d","402dfec9","402e4a3a","402e959d","402ee0f3","402f2c3c","402f7778","402fc2a8","40300dc9","403058de","4030a3e6","4030eee0","403139cd","403184ad","4031cf7f","40321a44","403264fb","4032afa5","4032fa42","403344d0","40338f51","4033d9c5","4034242a","40346e82","4034b8cc","40350308","40354d37","40359757","4035e16a","40362b6e","40367565","4036bf4d","40370927","403752f3","40379cb1","4037e661","40383003","40387996","4038c31b","40390c91","403955f9","40399f53","4039e89f","403a31db","403a7b0a","403ac429","403b0d3b","403b563d","403b9f31","403be816","403c30ed","403c79b5","403cc26e","403d0b18","403d53b4","403d9c41","403de4be","403e2d2d","403e758d","403ebdde","403f0620","403f4e53","403f9678","403fde8d","40402692","40406e89","4040b671","4040fe4a","40414613","40418dcd","4041d578","40421d14","404264a1","4042ac1e","4042f38c","40433aea","4043823a","4043c97a","404410aa","404457cc","40449edd","4044e5e0","40452cd3","404573b6","4045ba8a","4046014f","40464804","40468ea9","4046d53f","40471bc5","4047623c","4047a8a3","4047eefb","40483543","40487b7c","4048c1a4","404907be","40494dc7","404993c1","4049d9ab","404a1f85","404a6550","404aab0b","404af0b7","404b3652","404b7bde","404bc15a","404c06c7","404c4c24","404c9170","404cd6ae","404d1bdb","404d60f9","404da606","404deb04","404e2ff3","404e74d1","404eb9a0","404efe5f","404f430e","404f87ad","404fcc3c","405010bc","4050552c","4050998c","4050dddc","4051221d","4051664d","4051aa6e","4051ee7f","40523280","40527672","4052ba53","4052fe25","405341e7","4053859a","4053c93c","40540ccf","40545052","405493c5","4054d728","40551a7c","40555dc0","4055a0f4","4055e418","4056272d","40566a32","4056ad27","4056f00d","405732e3","405775a9","4057b860","4057fb07","40583d9e","40588025","4058c29d","40590506","4059475f","405989a8","4059cbe2","405a0e0c","405a5026","405a9231","405ad42d","405b1619","405b57f6","405b99c3","405bdb80","405c1d2f","405c5ecd","405ca05d","405ce1dd","405d234e","405d64af","405da601","405de744","405e2878","405e699c","405eaab1","405eebb7","405f2cad","405f6d95","405fae6d","405fef36","40602ff0","4060709c","4060b138","4060f1c5","40613243","406172b2","4061b312","4061f363","406233a5","406273d9","4062b3fd","4062f413","4063341a","40637412","4063b3fc","4063f3d7","406433a3","40647361","4064b310","4064f2b0","40653242","406571c5","4065b13a","4065f0a1","40662ff9","40666f43","4066ae7e","4066edab","40672cca","40676bda","4067aadd","4067e9d1","406828b7","4068678f","4068a659","4068e515","406923c3","40696263","4069a0f6","4069df7a","406a1df0","406a5c59","406a9ab4","406ad902","406b1741","406b5573","406b9398","406bd1af","406c0fb8","406c4db4","406c8ba3","406cc984","406d0758","406d451f","406d82d9","406dc085","406dfe24","406e3bb6","406e793b","406eb6b3","406ef41f","406f317d","406f6ece","406fac13","406fe94b","40702676","40706395","4070a0a7","4070ddac","40711aa5","40715791","40719471","4071d145","40720e0d","40724ac8","40728777","4072c41a","407300b1","40733d3b","407379ba","4073b62d","4073f294","40742eef","40746b3f","4074a783","4074e3bb","40751fe8","40755c09","4075981e","4075d429","40761028","40764c1b","40768804","4076c3e1","4076ffb3","40773b7a","40777736","4077b2e7","4077ee8e","40782a29","407865ba","4078a140","4078dcbc","4079182d","40795393","40798ef0","4079ca41","407a0589","407a40c6","407a7bf9","407ab722","407af241","407b2d56","407b6861","407ba363","407bde5a","407c1948","407c542c","407c8f07","407cc9d8","407d04a0","407d3f5f","407d7a14","407db4c0","407def63","407e29fd","407e648e","407e9f16","407ed995","407f140b","407f4e79","407f88de","407fc33a","407ffd8e","40801bed","4080390f","4080562c","40807346","4080905b","4080ad6d","4080ca7a","4080e783","40810489","4081218b","40813e89","40815b83","40817879","4081956c","4081b25b","4081cf47","4081ec2f","40820913","408225f4","408242d1","40825fab","40827c82","40829955","4082b625","4082d2f2","4082efbc","40830c82","40832945","40834605","408362c3","40837f7d","40839c34","4083b8e8","4083d59a","4083f248","40840ef4","40842b9d","40844844","408464e7","40848189","40849e27","4084bac3","4084d75d","4084f3f4","40851089","40852d1b","408549ab","40856639","408582c5","40859f4f","4085bbd6","4085d85b","4085f4df","40861160","40862de0","40864a5d","408666d9","40868353","40869fcb","4086bc42","4086d8b7","4086f52a","4087119c","40872e0c","40874a7b","408766e9","40878355","40879fc0","4087bc29","4087d892","4087f4f9","4088115f","40882dc4","40884a28","4088668b","408882ed","40889f4f","4088bbaf","4088d80f","4088f46e","408910cc","40892d2a","40894988","408965e4","40898241","40899e9d","4089baf8","4089d753","4089f3ae","408a1009","408a2c64","408a48bf","408a6519","408a8174","408a9dcf","408aba29","408ad684","408af2e0","408b0f3b","408b2b97","408b47f4","408b6450","408b80ae","408b9d0b","408bb96a","408bd5c9","408bf229","408c0e89","408c2aeb","408c474d","408c63b1","408c8015","408c9c7a","408cb8e1","408cd549","408cf1b1","408d0e1c","408d2a87","408d46f4","408d6362","408d7fd2","408d9c44","408db8b7","408dd52c","408df1a2","408e0e1b","408e2a95","408e4711","408e638f","408e800f","408e9c92","408eb916","408ed59d","408ef226","408f0eb1","408f2b3f","408f47cf","408f6461","408f80f7","408f9d8e","408fba29","408fd6c6","408ff366","40901009","40902caf","40904958","40906604","409082b3","40909f66","4090bc1b","4090d8d4","4090f590","40911250","40912f13","40914bda","409168a4","40918572","4091a244","4091bf1a","4091dbf3","4091f8d0","409215b2","40923297","40924f81","40926c6f","40928961","4092a657","4092c352","4092e051","4092fd55","40931a5e","4093376b","4093547c","40937193","40938eae","4093abce","4093c8f4","4093e61e","4094034d","40942082","40943dbc","40945afb","40947840","4094958a","4094b2d9","4094d02e","4094ed89","40950aea","40952850","409545bc","4095632e","409580a6","40959e24","4095bba9","4095d933","4095f6c4","4096145b","409631f8","40964f9c","40966d47","40968af8","4096a8b0","4096c66e","4096e434","40970200","40971fd3","40973dae","40975b8f","40977978","40979768","4097b55f","4097d35e","4097f164","40980f71","40982d87","40984ba4","409869c8","409887f5","4098a62a","4098c466","4098e2ab","409900f7","40991f4c","40993da9","40995c0f","40997a7d","409998f3","4099b772","4099d5fa","4099f48b","409a1324","409a31c6","409a5071","409a6f25","409a8de2","409aaca9","409acb78","409aea51","409b0934","409b2820","409b4715","409b6614","409b851d","409ba430","409bc34c","409be273","409c01a3","409c20de","409c4023","409c5f72","409c7ecb","409c9e2f","409cbd9d","409cdd16","409cfc9a","409d1c28","409d3bc1","409d5b65","409d7b14","409d9ace","409dba93","409dda64","409dfa40","409e1a27","409e3a19","409e5a18","409e7a21","409e9a37","409eba58","409eda85","409efabf","409f1b04","409f3b55","409f5bb2","409f7c1c","409f9c92","409fbd15","409fdda4","409ffe40","40a01ee9","40a03f9e","40a06060","40a0812f","40a0a20b","40a0c2f5","40a0e3eb","40a104ef","40a12600","40a1471f","40a1684b","40a18985","40a1aacd","40a1cc22","40a1ed85","40a20ef7","40a23076","40a25204","40a273a0","40a2954a","40a2b703","40a2d8ca","40a2faa0","40a31c84","40a33e77","40a36079","40a3828a","40a3a4ab","40a3c6da","40a3e918","40a40b66","40a42dc4","40a45030","40a472ad","40a49539","40a4b7d4","40a4da80","40a4fd3c","40a52008","40a542e3","40a565cf","40a588cc","40a5abd9","40a5cef6","40a5f224","40a61563","40a638b2","40a65c12","40a67f84","40a6a306","40a6c69a","40a6ea3f","40a70df5","40a731bc","40a75596","40a77980","40a79d7d","40a7c18b","40a7e5ac","40a809de","40a82e23","40a85279","40a876e2","40a89b5e","40a8bfec","40a8e48d","40a90940","40a92e06","40a952df","40a977cb","40a99cca","40a9c1dd","40a9e702","40aa0c3b","40aa3188","40aa56e8","40aa7c5c","40aaa1e4","40aac77f","40aaed2f","40ab12f3","40ab38cb","40ab5eb7","40ab84b7","40abaacd","40abd0f6","40abf735","40ac1d88","40ac43f1","40ac6a6e","40ac9100","40acb7a8","40acde65","40ad0538","40ad2c20","40ad531d","40ad7a31","40ada15a","40adc89a","40adefef","40ae175b","40ae3edd","40ae6675","40ae8e24","40aeb5e9","40aeddc6","40af05b9","40af2dc3","40af55e4","40af7e1c","40afa66b","40afced2","40aff751","40b01fe7","40b04894","40b0715a","40b09a37","40b0c32d","40b0ec3b","40b11561","40b13e9f","40b167f6","40b19165","40b1baed","40b1e48e","40b20e48","40b2381b","40b26207","40b28c0d","40b2b62c","40b2e064","40b30ab6","40b33522","40b35fa8","40b38a48","40b3b501","40b3dfd5","40b40ac4","40b435cc","40b460f0","40b48c2e","40b4b787","40b4e2fb","40b50e89","40b53a33","40b565f9","40b591d9","40b5bdd6","40b5e9ee","40b61621","40b64271","40b66edc","40b69b64","40b6c808","40b6f4c8","40b721a5","40b74e9f","40b77bb5","40b7a8e8","40b7d638","40b803a6","40b83130","40b85ed8","40b88c9d","40b8ba80","40b8e881","40b916a0","40b944dc","40b97337","40b9a1b0","40b9d048","40b9fefe","40ba2dd2","40ba5cc6","40ba8bd8","40babb0a","40baea5a","40bb19ca","40bb4959","40bb7908","40bba8d7","40bbd8c5","40bc08d3","40bc3902","40bc6951","40bc99c0","40bcca4f","40bcfaff","40bd2bd0","40bd5cc2","40bd8dd5","40bdbf09","40bdf05f","40be21d6","40be536e","40be8528","40beb704","40bee902","40bf1b22","40bf4d65","40bf7fca","40bfb251","40bfe4fc","40c017c9","40c04ab9","40c07dcc","40c0b102","40c0e45c","40c117d9","40c14b7a","40c17f3f","40c1b328","40c1e735","40c21b67","40c24fbc","40c28437","40c2b8d6","40c2ed9a","40c32283","40c35791","40c38cc4","40c3c21d","40c3f79c","40c42d40","40c4630a","40c498fa","40c4cf11","40c5054d","40c53bb1","40c5723b","40c5a8ec","40c5dfc3","40c616c2","40c64de9","40c68536","40c6bcab","40c6f448","40c72c0d","40c763fa","40c79c10","40c7d44d","40c80cb3","40c84542","40c87dfa","40c8b6da","40c8efe4","40c92918","40c96274","40c99bfb","40c9d5ab","40ca0f85","40ca4989","40ca83b8","40cabe11","40caf895","40cb3344","40cb6e1d","40cba922","40cbe452","40cc1fae","40cc5b35","40cc96e8","40ccd2c7","40cd0ed2","40cd4b09","40cd876d","40cdc3fe","40ce00bc","40ce3da6","40ce7abe","40ceb803","40cef575","40cf3316","40cf70e4","40cfaee0","40cfed0b","40d02b64","40d069eb","40d0a8a1","40d0e787","40d1269b","40d165df","40d1a552","40d1e4f5","40d224c8","40d264ca","40d2a4fd","40d2e561","40d325f5","40d366ba","40d3a7af"]} diff --git a/data/maps/bc20/ALandDivided.json b/data/maps/bc20/ALandDivided.json new file mode 100644 index 0000000..098313e --- /dev/null +++ b/data/maps/bc20/ALandDivided.json @@ -0,0 +1 @@ +{"elevation":[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-1073741824,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-100,-100,-100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":32,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":26},{"id":1,"team":"B","type":"hq","x":35,"y":26},{"id":2,"team":"neutral","type":"cow","x":0,"y":0},{"id":3,"team":"neutral","type":"cow","x":40,"y":0}],"initial_water":0,"name":"ALandDivided","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":118811,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"vertical","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":41} diff --git a/data/maps/bc20/BeachFrontProperty.json b/data/maps/bc20/BeachFrontProperty.json new file mode 100644 index 0000000..89246c3 --- /dev/null +++ b/data/maps/bc20/BeachFrontProperty.json @@ -0,0 +1 @@ +{"elevation":[-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,-4,-4,3,3,3,3,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,-4,-4,-4,-4,-4,-4,0,0,0,1,1,2,2,2,2,2,1,1,1,1,1,1,0,0,0,-4,-4,3,3,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,0,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,2,2,2,1,1,1,1,1,1,1,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,1,1,0,0,-4,0,0,0,0,0,0,0,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,1,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,5,5,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,0,0,-4,0,0,1,1,1,1,1,1,1,2,2,3,3,3,2,2,2,2,2,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,5,3,3,2,2,1,1,0,0,0,0,-4,0,0,1,1,1,1,1,1,2,2,3,3,2,2,3,3,3,3,2,2,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,1,1,2,2,3,4,3,3,3,3,3,3,3,5,5,3,2,2,1,1,1,1,1,0,0,0,0,1,1,2,2,2,2,2,2,3,1,1,1,1,1,3,3,3,2,2,2,2,1,1,0,0,0,-4,-4,-4,-4,-4,-4,0,0,0,0,1,1,2,2,2,3,4,3,3,3,3,3,3,3,5,5,3,2,2,1,1,1,1,1,0,0,0,0,1,1,2,2,2,2,2,3,2,1,0,0,0,1,1,3,3,3,2,2,2,1,1,0,0,0,-4,-4,-4,-4,-4,-4,0,0,0,1,1,1,2,2,2,4,4,3,3,3,3,3,3,3,3,5,3,2,2,2,2,2,1,1,0,1,1,1,1,1,2,2,3,3,3,3,2,1,0,0,0,0,1,2,3,3,3,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,1,1,0,1,1,1,1,2,2,2,3,3,3,5,3,2,0,0,0,0,1,2,3,3,3,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,2,3,3,3,3,5,5,5,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,2,2,2,2,2,2,2,3,3,3,3,3,5,3,0,0,3,5,3,3,3,3,2,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,5,5,5,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,5,5,5,5,5,3,3,3,3,3,2,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,1,3,3,3,3,3,3,3,3,5,5,3,5,5,5,5,5,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,3,3,3,4,4,3,3,3,5,5,3,5,5,5,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,4,4,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,3,3,4,5,5,4,4,3,4,5,5,5,5,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,5,5,4,4,3,4,6,6,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,3,3,3,3,3,3,4,5,4,3,3,4,6,6,5,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,3,3,3,4,5,4,3,4,5,6,6,5,5,3,10,10,10,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,3,3,4,4,3,3,5,6,6,5,3,3,10,3,3,3,10,3,3,3,3,3,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,4,6,6,5,5,3,3,3,10,3,10,3,3,3,3,3,3,4,4,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,6,6,4,5,4,3,3,3,3,10,3,3,3,3,3,3,3,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,4,4,3,3,3,5,5,5,5,3,3,3,3,3,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,3,3,3,10,3,3,3,10,3,3,3,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,4,4,3,3,3,3,5,5,5,5,5,5,5,3,3,5,3,3,4,4,4,5,4,4,4,4,3,3,3,10,10,3,10,3,10,10,3,3,3,3,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,4,3,3,3,3,3,3,5,5,5,5,5,5,5,5,5,3,3,4,4,4,5,5,5,5,4,4,3,3,3,10,10,10,10,10,3,3,3,3,4,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,4,3,3,3,3,3,3,3,3,3,5,5,5,5,5,3,3,3,4,4,4,6,7,6,6,5,4,4,3,3,3,3,10,10,3,3,3,3,3,3,4,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,10,10,3,3,3,3,3,3,3,3,3,2,2,21,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,4,4,3,3,3,3,3,3,10,10,10,10,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,10,10,10,10,3,3,3,3,3,3,4,4,3,3,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,21,2,2,3,3,3,3,3,3,3,3,3,10,10,3,3,3,3,3,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,3,3,3,3,3,3,10,10,3,3,3,3,4,4,5,6,6,7,6,4,4,4,3,3,3,5,5,5,5,5,3,3,3,3,3,3,3,3,3,4,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,4,3,3,3,3,10,10,10,10,10,3,3,3,4,4,5,5,5,5,4,4,4,3,3,5,5,5,5,5,5,5,5,5,3,3,3,3,3,3,4,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,3,3,3,3,10,10,3,10,3,10,10,3,3,3,4,4,4,4,5,4,4,4,3,3,5,3,3,5,5,5,5,5,5,5,3,3,3,3,4,4,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,3,3,3,10,3,3,3,10,3,3,3,3,3,3,3,3,3,4,4,4,3,3,3,3,3,3,3,3,3,3,3,5,5,5,5,3,3,3,4,4,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,3,3,3,3,3,3,3,10,3,3,3,3,4,5,4,6,6,3,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,4,4,3,3,3,3,3,3,10,3,10,3,3,3,5,5,6,6,4,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,4,3,3,3,3,3,10,3,3,3,10,3,3,5,6,6,5,3,3,4,4,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,10,10,10,3,5,5,6,6,5,4,3,4,5,4,3,3,3,3,3,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,5,6,6,4,3,3,4,5,4,3,3,3,3,3,3,3,3,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,6,6,4,3,4,4,5,5,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,5,5,5,5,4,3,4,4,5,5,4,3,3,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,4,4,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,5,5,5,3,5,5,3,3,3,4,4,3,3,3,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,3,3,3,3,3,3,3,5,5,5,5,5,3,5,5,3,3,3,3,3,3,3,3,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,2,3,3,3,3,3,5,5,5,5,5,3,3,3,3,3,3,2,2,2,2,2,2,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,5,5,5,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,2,2,2,3,3,3,3,5,3,0,0,3,5,3,3,3,3,3,2,2,2,2,2,2,2,0,1,1,2,2,3,3,3,3,3,3,3,3,3,3,5,5,5,3,3,3,3,2,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,3,3,3,2,1,0,0,0,0,2,3,5,3,3,3,2,2,2,1,1,1,1,0,1,1,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,3,3,3,2,1,0,0,0,0,1,2,3,3,3,3,2,2,1,1,1,1,1,0,1,1,2,2,2,2,2,3,5,3,3,3,3,3,3,3,3,4,4,2,2,2,1,1,1,0,0,0,-4,-4,-4,-4,-4,-4,0,0,0,1,1,2,2,2,3,3,3,1,1,0,0,0,1,2,3,2,2,2,2,2,1,1,0,0,0,0,1,1,1,1,1,2,2,3,5,5,3,3,3,3,3,3,3,4,3,2,2,2,1,1,0,0,0,0,-4,-4,-4,-4,-4,-4,0,0,0,1,1,2,2,2,2,3,3,3,1,1,1,1,1,3,2,2,2,2,2,2,1,1,0,0,0,0,1,1,1,1,1,2,2,3,5,5,3,3,3,3,3,3,3,4,3,2,2,1,1,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,2,2,3,3,3,3,2,2,3,3,2,2,1,1,1,1,1,1,0,0,-4,0,0,0,0,1,1,2,2,3,3,5,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,1,2,2,2,2,2,2,2,3,3,3,2,2,1,1,1,1,1,1,1,0,0,-4,0,0,0,0,1,1,2,2,3,3,3,3,3,3,3,3,3,5,5,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,1,1,1,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,0,0,0,0,0,-4,0,0,1,1,1,1,2,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,1,1,1,0,0,-4,-4,-4,-4,-4,-4,0,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,3,3,-4,-4,0,0,0,1,1,1,1,1,1,2,2,2,2,2,1,1,0,0,0,-4,-4,-4,-4,-4,-4,0,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,-4,3,3,3,3,-4,-4,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,1,1,1,1,1,1,1,1,1,0,0,-4,-4,-4,-4,-4,-4,-4,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-4,-4,-4,-4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824],"height":64,"initial_bodies":[{"id":0,"team":"neutral","type":"cow","x":4,"y":57},{"id":1,"team":"neutral","type":"cow","x":59,"y":6},{"id":2,"team":"neutral","type":"cow","x":5,"y":18},{"id":3,"team":"neutral","type":"cow","x":58,"y":45},{"id":4,"team":"neutral","type":"cow","x":5,"y":26},{"id":5,"team":"neutral","type":"cow","x":58,"y":37},{"id":6,"team":"neutral","type":"cow","x":5,"y":38},{"id":7,"team":"neutral","type":"cow","x":58,"y":25},{"id":8,"team":"neutral","type":"cow","x":5,"y":48},{"id":9,"team":"neutral","type":"cow","x":58,"y":15},{"id":10,"team":"neutral","type":"cow","x":6,"y":33},{"id":11,"team":"neutral","type":"cow","x":57,"y":30},{"id":12,"team":"A","type":"hq","x":17,"y":7},{"id":13,"team":"B","type":"hq","x":46,"y":56},{"id":14,"team":"neutral","type":"cow","x":28,"y":6},{"id":15,"team":"neutral","type":"cow","x":35,"y":57},{"id":16,"team":"neutral","type":"cow","x":28,"y":13},{"id":17,"team":"neutral","type":"cow","x":35,"y":50},{"id":18,"team":"neutral","type":"cow","x":29,"y":34},{"id":19,"team":"neutral","type":"cow","x":34,"y":29}],"initial_water":0,"name":"BeachFrontProperty","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"width":64} diff --git a/data/maps/bc20/CentralLake.json b/data/maps/bc20/CentralLake.json new file mode 100644 index 0000000..ee967fd --- /dev/null +++ b/data/maps/bc20/CentralLake.json @@ -0,0 +1 @@ +{"elevation":[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-1073741824,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":41,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":7,"y":7},{"id":1,"team":"B","type":"hq","x":33,"y":33},{"id":2,"team":"neutral","type":"cow","x":36,"y":36},{"id":3,"team":"neutral","type":"cow","x":4,"y":4},{"id":4,"team":"neutral","type":"cow","x":35,"y":35},{"id":5,"team":"neutral","type":"cow","x":5,"y":5}],"initial_water":0,"name":"CentralLake","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":432,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400,400,400,400,400,400,400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600,600,600,600,600,600,600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,600,600,600,600,600,600,600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,400,400,400,400,400,400,400,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":41} diff --git a/data/maps/bc20/CentralSoup.json b/data/maps/bc20/CentralSoup.json new file mode 100644 index 0000000..33fb240 --- /dev/null +++ b/data/maps/bc20/CentralSoup.json @@ -0,0 +1 @@ +{"elevation":[-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-1073741824,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,15,15,15,15,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,3,3,3,3,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,3,3,3,3,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,3,3,3,3,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,3,3,3,3,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,15,15,15,15,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-1073741824,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25,-25],"height":48,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":10,"y":10},{"id":1,"team":"B","type":"hq","x":37,"y":37},{"id":2,"team":"neutral","type":"cow","x":12,"y":14},{"id":3,"team":"neutral","type":"cow","x":35,"y":33},{"id":4,"team":"neutral","type":"cow","x":8,"y":18},{"id":5,"team":"neutral","type":"cow","x":39,"y":29}],"initial_water":0,"name":"CentralSoup","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":219,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,2000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,800,800,800,800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,800,800,800,800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,800,800,800,800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,800,800,800,800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,2000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"width":48} diff --git a/data/maps/bc20/Climb.json b/data/maps/bc20/Climb.json new file mode 100644 index 0000000..c9ce8fb --- /dev/null +++ b/data/maps/bc20/Climb.json @@ -0,0 +1 @@ +{"elevation":[100,100,100,100,97,94,91,88,85,82,79,76,73,70,67,64,61,58,55,52,49,46,43,40,37,34,31,28,25,22,19,16,13,10,7,4,2,2,1,-1073741824,100,100,100,100,97,94,91,88,85,82,79,76,73,70,67,64,61,58,55,52,49,46,43,40,37,34,31,28,25,22,19,16,13,10,7,4,2,2,1,1,100,100,100,100,97,94,91,88,85,82,79,76,73,70,67,64,61,58,55,52,49,46,43,40,37,34,31,28,25,22,19,16,13,10,7,4,2,2,2,2,100,100,100,100,97,94,91,88,85,82,79,76,73,70,67,64,61,58,55,52,49,46,43,40,37,34,31,28,25,22,19,16,13,10,7,4,2,2,2,2,103,103,103,103,274,274,274,274,271,268,265,262,259,256,253,250,247,244,241,238,235,232,229,226,223,220,217,214,211,208,205,202,199,196,193,190,187,187,187,187,106,106,106,106,274,274,274,274,271,268,265,262,259,256,253,250,247,244,241,238,235,232,229,226,223,220,217,214,211,208,205,202,199,196,193,190,187,187,187,187,109,109,109,109,274,274,274,274,271,268,265,262,259,256,253,250,247,244,241,238,235,232,229,226,223,220,217,214,211,208,205,202,199,196,193,190,187,187,187,187,112,112,112,112,274,274,274,274,271,268,265,262,259,256,253,250,247,244,241,238,235,232,229,226,223,220,217,214,211,208,205,202,199,196,193,190,187,187,187,187,115,115,115,115,277,277,277,277,400,400,400,400,397,394,391,388,385,382,379,376,373,370,367,364,361,358,355,352,349,346,343,340,337,337,337,337,184,184,184,184,118,118,118,118,280,280,280,280,400,400,400,400,397,394,391,388,385,382,379,376,373,370,367,364,361,358,355,352,349,346,343,340,337,337,337,337,181,181,181,181,121,121,121,121,283,283,283,283,400,400,400,400,397,394,391,388,385,382,379,376,373,370,367,364,361,358,355,352,349,346,343,340,337,337,337,337,178,178,178,178,124,124,124,124,286,286,286,286,400,400,400,400,397,394,391,388,385,382,379,376,373,370,367,364,361,358,355,352,349,346,343,340,337,337,337,337,175,175,175,175,127,127,127,127,289,289,289,289,403,403,403,403,478,478,478,478,475,472,469,466,463,460,457,454,451,448,445,442,439,439,439,439,334,334,334,334,172,172,172,172,130,130,130,130,292,292,292,292,406,406,406,406,478,478,478,478,475,472,469,466,463,460,457,454,451,448,445,442,439,439,439,439,331,331,331,331,169,169,169,169,133,133,133,133,295,295,295,295,409,409,409,409,478,478,478,478,475,472,469,466,463,460,457,454,451,448,445,442,439,439,439,439,328,328,328,328,166,166,166,166,136,136,136,136,298,298,298,298,412,412,412,412,478,478,478,478,475,472,469,466,463,460,457,454,451,448,445,442,439,439,439,439,325,325,325,325,163,163,163,163,139,139,139,139,301,301,301,301,415,415,415,415,481,481,481,481,496,499,500,500,500,500,499,496,493,493,493,493,436,436,436,436,322,322,322,322,160,160,160,160,142,142,142,142,304,304,304,304,418,418,418,418,484,484,484,484,496,499,500,500,500,500,499,496,493,493,493,493,433,433,433,433,319,319,319,319,157,157,157,157,145,145,145,145,307,307,307,307,421,421,421,421,487,487,487,487,496,499,500,500,500,500,499,496,493,493,493,493,430,430,430,430,316,316,316,316,154,154,154,154,148,148,148,148,310,310,310,310,424,424,424,424,490,490,490,490,496,499,500,-5,-5,500,499,496,493,493,493,493,427,427,427,427,313,313,313,313,151,151,151,151,151,151,151,151,313,313,313,313,427,427,427,427,493,493,493,493,496,499,500,-5,-5,500,499,496,490,490,490,490,424,424,424,424,310,310,310,310,148,148,148,148,154,154,154,154,316,316,316,316,430,430,430,430,493,493,493,493,496,499,500,500,500,500,499,496,487,487,487,487,421,421,421,421,307,307,307,307,145,145,145,145,157,157,157,157,319,319,319,319,433,433,433,433,493,493,493,493,496,499,500,500,500,500,499,496,484,484,484,484,418,418,418,418,304,304,304,304,142,142,142,142,160,160,160,160,322,322,322,322,436,436,436,436,493,493,493,493,496,499,500,500,500,500,499,496,481,481,481,481,415,415,415,415,301,301,301,301,139,139,139,139,163,163,163,163,325,325,325,325,439,439,439,439,442,445,448,451,454,457,460,463,466,469,472,475,478,478,478,478,412,412,412,412,298,298,298,298,136,136,136,136,166,166,166,166,328,328,328,328,439,439,439,439,442,445,448,451,454,457,460,463,466,469,472,475,478,478,478,478,409,409,409,409,295,295,295,295,133,133,133,133,169,169,169,169,331,331,331,331,439,439,439,439,442,445,448,451,454,457,460,463,466,469,472,475,478,478,478,478,406,406,406,406,292,292,292,292,130,130,130,130,172,172,172,172,334,334,334,334,439,439,439,439,442,445,448,451,454,457,460,463,466,469,472,475,478,478,478,478,403,403,403,403,289,289,289,289,127,127,127,127,175,175,175,175,337,337,337,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,400,400,400,286,286,286,286,124,124,124,124,178,178,178,178,337,337,337,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,400,400,400,283,283,283,283,121,121,121,121,181,181,181,181,337,337,337,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,400,400,400,280,280,280,280,118,118,118,118,184,184,184,184,337,337,337,337,340,343,346,349,352,355,358,361,364,367,370,373,376,379,382,385,388,391,394,397,400,400,400,400,277,277,277,277,115,115,115,115,187,187,187,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,274,274,274,112,112,112,112,187,187,187,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,274,274,274,109,109,109,109,187,187,187,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,274,274,274,106,106,106,106,187,187,187,187,190,193,196,199,202,205,208,211,214,217,220,223,226,229,232,235,238,241,244,247,250,253,256,259,262,265,268,271,274,274,274,274,103,103,103,103,2,2,2,2,4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,100,100,100,2,2,2,2,4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,100,100,100,1,1,2,2,4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,100,100,100,-1073741824,1,2,2,4,7,10,13,16,19,22,25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,73,76,79,82,85,88,91,94,97,100,100,100,100],"height":40,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":4,"y":37},{"id":1,"team":"B","type":"hq","x":35,"y":2}],"initial_water":0,"name":"Climb","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[300,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,117,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,117,117,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,117,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,117,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,117,117,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,117,117,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,117,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,117,117,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,117,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,117,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,117,117,117,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,117,117,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,0,0,117,0,117,0,0,0,0,0,0,0,0,0,0,0,0,117,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,0,300],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":40} diff --git a/data/maps/bc20/Constriction.json b/data/maps/bc20/Constriction.json new file mode 100644 index 0000000..b3aa405 --- /dev/null +++ b/data/maps/bc20/Constriction.json @@ -0,0 +1 @@ +{"elevation":[-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,1,1,3,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,5,3,1,1,3,5,10,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,7,9,9,9,9,7,7,7,5,3,1,1,3,5,10,0,0,0,0,0,3,6,9,6,3,3,3,3,3,3,6,9,10,10,10,10,10,10,10,10,7,12,12,12,10,7,7,7,5,3,1,1,3,5,10,0,300,0,0,0,3,6,9,6,3,3,3,0,0,3,6,9,10,10,10,10,10,10,10,10,7,15,15,13,10,10,10,8,5,3,1,1,3,5,10,0,300,0,0,0,3,6,9,6,3,3,3,0,0,3,6,9,10,10,10,10,10,10,10,10,7,18,16,13,13,13,11,8,5,3,1,1,3,5,10,0,300,0,0,0,3,6,9,6,6,6,3,3,3,3,6,9,10,10,10,10,10,10,10,10,7,19,16,16,16,14,11,8,5,3,1,1,3,5,10,0,300,300,300,300,300,300,300,300,9,300,300,300,300,300,6,9,10,10,7,7,7,7,7,7,7,19,19,19,17,14,11,8,5,3,1,1,3,5,10,0,0,0,0,0,0,0,0,3,6,8,1000,1000,1000,300,9,9,10,10,7,22,22,22,22,22,22,7,7,7,7,7,7,7,5,3,1,1,3,5,11,0,0,0,0,0,0,0,0,3,3,8,1000,1000,1000,300,10,10,10,10,7,22,22,22,22,22,22,7,10,10,10,10,10,10,5,3,1,1,3,5,11,0,0,0,0,0,0,0,0,0,0,8,1000,1000,1000,300,10,10,10,10,7,22,22,22,22,22,22,7,10,13,13,13,13,10,5,3,1,1,3,5,11,2,0,0,0,0,0,0,0,0,0,8,300,300,300,300,7,7,50,50,50,22,22,22,22,22,22,7,10,13,600,600,13,10,5,3,1,1,3,5,11,5,2,0,0,0,0,0,0,0,0,8,300,300,300,300,4,4,50,50,50,22,22,22,22,22,22,7,10,13,600,600,13,10,5,3,1,1,3,5,11,8,5,2,0,0,0,0,0,0,0,8,300,300,300,300,1,1,50,50,50,22,22,22,22,22,22,7,10,13,600,600,13,10,5,3,1,1,3,5,11,8,5,3,3,3,3,3,3,3,3,8,300,300,300,300,-2,-2,50,50,50,50,50,50,7,7,7,7,10,13,600,600,13,10,5,3,1,1,3,5,11,8,6,6,6,6,6,6,6,6,6,8,300,300,300,300,-5,-5,50,50,50,50,50,50,80,10,10,10,10,13,13,13,13,10,5,3,1,1,3,5,11,9,9,9,9,9,9,9,9,9,9,8,300,300,300,300,-8,-8,50,50,50,50,50,50,10,10,10,10,10,10,10,10,10,10,5,3,1,1,3,5,11,12,12,12,12,12,12,12,12,12,12,8,20,20,20,20,-11,-11,20,20,20,20,7,7,7,7,7,7,7,7,7,7,7,7,5,3,1,1,3,5,7,7,7,7,7,7,7,7,7,7,7,7,20,20,20,20,-11,-11,20,20,20,20,8,12,12,12,12,12,12,12,12,12,12,11,5,3,1,1,3,5,10,10,10,10,10,10,10,10,10,10,50,50,50,50,50,50,-8,-8,300,300,300,300,8,9,9,9,9,9,9,9,9,9,9,11,5,3,1,1,3,5,10,13,13,13,13,10,10,10,10,80,50,50,50,50,50,50,-5,-5,300,300,300,300,8,6,6,6,6,6,6,6,6,6,8,11,5,3,1,1,3,5,10,13,600,600,13,10,7,7,7,7,50,50,50,50,50,50,-2,-2,300,300,300,300,8,3,3,3,3,3,3,3,3,5,8,11,5,3,1,1,3,5,10,13,600,600,13,10,7,22,22,22,22,22,22,50,50,50,1,1,300,300,300,300,8,0,0,0,0,0,0,0,2,5,8,11,5,3,1,1,3,5,10,13,600,600,13,10,7,22,22,22,22,22,22,50,50,50,4,4,300,300,300,300,8,0,0,0,0,0,0,0,0,2,5,11,5,3,1,1,3,5,10,13,600,600,13,10,7,22,22,22,22,22,22,50,50,50,7,7,300,300,300,300,8,0,0,0,0,0,0,0,0,0,2,11,5,3,1,1,3,5,10,13,13,13,13,10,7,22,22,22,22,22,22,7,10,10,10,10,300,1000,1000,1000,8,0,0,0,0,0,0,0,0,0,0,11,5,3,1,1,3,5,10,10,10,10,10,10,7,22,22,22,22,22,22,7,10,10,10,10,300,1000,1000,1000,8,3,3,0,0,0,0,0,0,0,0,11,5,3,1,1,3,5,7,7,7,7,7,7,7,22,22,22,22,22,22,7,10,10,9,9,300,1000,1000,1000,8,6,3,0,0,0,0,0,0,0,0,10,5,3,1,1,3,5,8,11,14,17,19,19,19,7,7,7,7,7,7,7,10,10,9,6,300,300,300,300,300,9,300,300,300,300,300,300,300,300,0,10,5,3,1,1,3,5,8,11,14,16,16,16,19,7,10,10,10,10,10,10,10,10,9,6,3,3,3,3,6,6,6,9,6,3,0,0,0,300,0,10,5,3,1,1,3,5,8,11,13,13,13,16,18,7,10,10,10,10,10,10,10,10,9,6,3,0,0,3,3,3,6,9,6,3,0,0,0,300,0,10,5,3,1,1,3,5,8,10,10,10,13,15,15,7,10,10,10,10,10,10,10,10,9,6,3,0,0,3,3,3,6,9,6,3,0,0,0,300,0,10,5,3,1,1,3,5,7,7,7,10,12,12,12,7,10,10,10,10,10,10,10,10,9,6,3,3,3,3,3,3,6,9,6,3,0,0,0,0,0,10,5,3,1,1,3,5,7,7,7,9,9,9,9,7,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,5,3,1,1,3,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5,3,1,1,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824],"height":40,"initial_bodies":[{"id":0,"team":"neutral","type":"cow","x":6,"y":8},{"id":1,"team":"neutral","type":"cow","x":33,"y":31},{"id":2,"team":"neutral","type":"cow","x":11,"y":13},{"id":3,"team":"neutral","type":"cow","x":28,"y":26},{"id":4,"team":"neutral","type":"cow","x":13,"y":15},{"id":5,"team":"neutral","type":"cow","x":26,"y":24},{"id":6,"team":"neutral","type":"cow","x":27,"y":17},{"id":7,"team":"neutral","type":"cow","x":12,"y":22},{"id":8,"team":"neutral","type":"cow","x":33,"y":16},{"id":9,"team":"neutral","type":"cow","x":6,"y":23},{"id":10,"team":"A","type":"hq","x":36,"y":3},{"id":11,"team":"B","type":"hq","x":3,"y":36}],"initial_water":0,"name":"Constriction","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,49,49,49,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,196,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,0,0,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,196,0,0,0,0,0,0,0,0,0,0,49,49,49,0,0,0,0,0,49,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,196,0,0,0,0,0,0,0,0,0,0,49,49,49,0,0,0,0,0,49,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,196,0,0,196,196,0,0,0,0,196,49,49,49,49,0,0,0,0,0,49,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,196,49,49,0,0,0,0,0,0,0,49,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,196,196,196,0,0,0,0,0,0,0,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,0,0,0,0,0,0,0,196,196,196,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,49,0,0,0,0,0,0,0,49,49,196,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,49,0,0,0,0,0,49,49,49,49,196,0,0,0,0,196,196,0,0,196,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,49,0,0,0,0,0,49,49,49,0,0,0,0,0,0,0,0,0,0,196,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,49,0,0,0,0,0,49,49,49,0,0,0,0,0,0,0,0,0,0,196,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,0,0,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,196,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,196,196,0,0,0,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,0,0,0,196,196,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,49,49,49,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,49,49,49,49,49,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"width":40} diff --git a/data/maps/bc20/Hills.json b/data/maps/bc20/Hills.json new file mode 100644 index 0000000..bbfae35 --- /dev/null +++ b/data/maps/bc20/Hills.json @@ -0,0 +1 @@ +{"elevation":[-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-6,-6,-6,0,-6,-6,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,0,0,0,6,0,0,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,50,120,50,50,50,0,0,0,0,0,0,0,0,0,0,-1073741824,0,6,6,12,6,6,0,0,-6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,50,120,120,190,120,120,50,50,0,0,0,0,0,0,0,0,0,-1073741824,6,12,12,18,12,12,6,0,-6,0,0,0,0,0,-1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,120,190,190,260,190,190,120,50,0,0,0,0,0,0,0,0,0,-1073741824,6,12,18,24,18,12,6,0,-6,0,0,-1,-1,-1,0,-1,-1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,120,190,260,330,260,190,120,50,0,0,0,0,0,0,0,0,0,-1073741824,12,18,24,30,24,18,12,6,0,-6,-1,-1,0,0,1,0,1,2,3,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,120,190,260,330,400,330,260,190,120,50,0,0,0,0,0,0,0,0,-1073741824,6,12,18,24,18,12,6,0,-6,0,-1,0,1,1,2,1,1,1,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,120,190,260,330,260,190,120,50,0,0,0,0,0,0,0,0,0,-1073741824,6,12,12,18,12,12,6,0,-6,0,-1,0,1,2,3,2,1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,120,190,190,260,190,190,120,50,0,0,0,0,0,0,0,0,0,-1073741824,0,6,6,12,6,6,0,0,-6,-1,0,1,2,3,4,3,2,1,0,-1,0,0,0,1,1,1,18,1,1,1,0,0,0,0,0,0,0,0,0,0,0,50,50,120,120,190,120,120,50,50,0,0,0,0,0,0,0,0,0,-1073741824,0,0,0,6,0,0,0,-6,0,0,-1,0,1,2,3,2,1,0,-1,0,0,0,1,18,18,18,35,18,18,18,1,0,0,0,0,0,0,0,0,0,0,0,50,50,50,120,50,50,50,0,0,0,0,0,0,0,0,0,0,-1073741824,-6,-6,-6,0,-6,-6,-6,0,0,0,-1,0,1,1,2,1,1,0,-1,0,0,1,18,35,35,35,52,35,35,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,-6,0,0,0,0,0,0,-1,-1,0,0,1,0,0,-1,-1,0,1,18,35,35,52,52,69,52,52,35,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,0,-1,-1,-1,0,0,1,18,35,52,69,69,86,69,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,1,18,35,52,69,86,103,86,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,86,103,120,103,86,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,86,103,86,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,-8,-8,-8,-8,-1,-8,-8,-8,-8,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,69,86,69,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,-8,-8,-1,-1,-1,6,-1,-1,-1,-8,-8,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,35,52,52,69,52,52,35,35,18,1,0,0,0,0,0,0,0,0,0,0,-8,-1,-1,6,6,6,13,6,6,6,-1,-1,-8,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,35,35,52,35,35,35,18,1,0,0,0,0,0,0,0,0,0,0,-8,-1,-1,6,13,13,13,20,13,13,13,6,-1,-1,-8,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,18,18,35,18,18,18,1,0,0,0,0,0,0,0,0,0,0,-8,-8,-1,6,13,20,20,20,27,20,20,20,13,6,-1,-8,-8,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,18,1,1,1,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,20,27,27,34,27,27,20,20,13,6,-1,-8,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,27,34,34,41,34,34,27,20,13,6,-1,-8,0,-1073741824,-1073741824,0,0,0,0,0,-8,-8,-8,-8,-1,-8,-8,-8,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,27,34,41,48,41,34,27,20,13,6,-1,-8,0,-1073741824,-1073741824,0,0,0,0,-8,-8,-1,-1,-1,6,-1,-1,-1,-8,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,27,34,41,48,55,48,41,34,27,20,13,6,-1,-8,-1073741824,-1073741824,0,0,0,-8,-1,-1,6,6,6,13,6,6,6,-1,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,27,34,41,48,41,34,27,20,13,6,-1,-8,0,-1073741824,-1073741824,0,0,-8,-1,-1,6,13,13,13,20,13,13,13,6,-1,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,27,34,34,41,34,34,27,20,13,6,-1,-8,0,-1073741824,-1073741824,0,-8,-8,-1,6,13,20,20,20,27,20,20,20,13,6,-1,-8,-8,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,-8,-1,6,13,20,20,27,27,34,27,27,20,20,13,6,-1,-8,0,-1073741824,-1073741824,0,-8,-1,6,13,20,20,27,27,34,27,27,20,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,-8,-8,-1,6,13,20,20,20,27,20,20,20,13,6,-1,-8,-8,0,-1073741824,-1073741824,0,-8,-1,6,13,20,27,34,34,41,34,34,27,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,-1,6,13,13,13,20,13,13,13,6,-1,-1,-8,0,0,-1073741824,-1073741824,0,-8,-1,6,13,20,27,34,41,48,41,34,27,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-1,-1,6,6,6,13,6,6,6,-1,-1,-8,0,0,0,-1073741824,-1073741824,-8,-1,6,13,20,27,34,41,48,55,48,41,34,27,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-8,-1,-1,-1,6,-1,-1,-1,-8,-8,0,0,0,0,-1073741824,-1073741824,0,-8,-1,6,13,20,27,34,41,48,41,34,27,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,-8,-8,-8,-1,-8,-8,-8,-8,0,0,0,0,0,-1073741824,-1073741824,0,-8,-1,6,13,20,27,34,34,41,34,34,27,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-8,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,-8,-1,6,13,20,20,27,27,34,27,27,20,20,13,6,-1,-8,0,0,0,0,0,0,0,0,0,0,0,1,1,1,18,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,-8,-8,-1,6,13,20,20,20,27,20,20,20,13,6,-1,-8,-8,0,0,0,0,0,0,0,0,0,0,1,18,18,18,35,18,18,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,-8,-1,-1,6,13,13,13,20,13,13,13,6,-1,-1,-8,0,0,0,0,0,0,0,0,0,0,1,18,35,35,35,52,35,35,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,-8,-1,-1,6,6,6,13,6,6,6,-1,-1,-8,0,0,0,0,0,0,0,0,0,0,1,18,35,35,52,52,69,52,52,35,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,-8,-8,-1,-1,-1,6,-1,-1,-1,-8,-8,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,69,86,69,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,-8,-8,-8,-8,-1,-8,-8,-8,-8,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,86,103,86,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,-8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,86,103,120,103,86,69,52,35,18,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,86,103,86,69,52,35,18,1,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,52,69,69,86,69,69,52,35,18,1,0,0,-1,-1,-1,0,-1,-1,-1,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,35,52,52,69,52,52,35,35,18,1,0,-1,-1,0,0,1,0,0,-1,-1,0,0,0,0,0,0,-6,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,1,18,35,35,35,52,35,35,35,18,1,0,0,-1,0,1,1,2,1,1,0,-1,0,0,0,-6,-6,-6,0,-6,-6,-6,-1073741824,0,0,0,0,0,0,0,0,0,0,50,50,50,120,50,50,50,0,0,0,0,0,0,0,0,0,0,0,1,18,18,18,35,18,18,18,1,0,0,0,-1,0,1,2,3,2,1,0,-1,0,0,-6,0,0,0,6,0,0,0,-1073741824,0,0,0,0,0,0,0,0,0,50,50,120,120,190,120,120,50,50,0,0,0,0,0,0,0,0,0,0,0,1,1,1,18,1,1,1,0,0,0,-1,0,1,2,3,4,3,2,1,0,-1,-6,0,0,6,6,12,6,6,0,-1073741824,0,0,0,0,0,0,0,0,0,50,120,190,190,260,190,190,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,1,2,3,2,1,0,-1,0,-6,0,6,12,12,18,12,12,6,-1073741824,0,0,0,0,0,0,0,0,0,50,120,190,260,330,260,190,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,1,1,2,1,1,0,-1,0,-6,0,6,12,18,24,18,12,6,-1073741824,0,0,0,0,0,0,0,0,50,120,190,260,330,400,330,260,190,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,2,1,0,1,0,0,-1,-1,-6,0,6,12,18,24,30,24,18,12,-1073741824,0,0,0,0,0,0,0,0,0,50,120,190,260,330,260,190,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,-1,-1,0,-1,-1,-1,0,0,-6,0,6,12,18,24,18,12,6,-1073741824,0,0,0,0,0,0,0,0,0,50,120,190,190,260,190,190,120,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,-1,0,0,0,0,0,-6,0,6,12,12,18,12,12,6,-1073741824,0,0,0,0,0,0,0,0,0,50,50,120,120,190,120,120,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,0,0,6,6,12,6,6,0,-1073741824,0,0,0,0,0,0,0,0,0,0,50,50,50,120,50,50,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,0,0,0,6,0,0,0,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,-6,-6,0,-6,-6,-6,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-6,0,0,-1073741824,-1073741824,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824],"height":60,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":14,"y":11},{"id":1,"team":"B","type":"hq","x":45,"y":48},{"id":2,"team":"neutral","type":"cow","x":24,"y":52},{"id":3,"team":"neutral","type":"cow","x":35,"y":7},{"id":4,"team":"neutral","type":"cow","x":35,"y":30},{"id":5,"team":"neutral","type":"cow","x":24,"y":29}],"initial_water":0,"name":"Hills","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":198248,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,15,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,15,36,15,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,36,36,57,36,36,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,57,78,57,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,78,99,78,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,170,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,36,57,78,99,120,99,78,57,36,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,78,99,78,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,57,78,57,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,36,36,57,36,36,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,15,36,15,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,15,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,15,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,15,36,15,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,36,36,57,36,36,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,57,78,57,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,78,99,78,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,36,57,78,99,120,99,78,57,36,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,170,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,78,99,78,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,130,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,15,36,57,57,78,57,57,36,15,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,90,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,36,36,57,36,36,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,15,15,15,36,15,15,15,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,15,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,30,30,30,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"width":60} diff --git a/data/maps/bc20/Hourglass.json b/data/maps/bc20/Hourglass.json new file mode 100644 index 0000000..c9c180c --- /dev/null +++ b/data/maps/bc20/Hourglass.json @@ -0,0 +1 @@ +{"elevation":[-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,2,2,2,2,2,2,2,2,2,2,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,2,2,2,3,3,3,3,3,3,3,3,3,2,2,2,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,100,100,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,3,3,100,3,3,3,100,3,3,3,100,3,3,3,100,3,3,2,2,2,2,2,2,2,2,2,2,2,2,1,2,2,2,2,2,2,100,100,100,2,3,3,100,3,3,3,100,3,3,3,100,3,3,3,100,3,3,2,2,2,2,2,2,2,100,100,2,2,1,1,1,1,1,2,2,2,100,2,2,2,3,3,100,100,100,100,100,100,3,100,100,100,100,100,100,3,3,2,2,2,2,2,2,2,100,1,1,1,1,1,1,1,1,1,2,2,100,2,2,2,2,3,3,3,3,3,3,100,100,100,3,3,3,3,3,3,2,2,2,2,2,2,2,100,100,1,1,1,1,-4,1,1,1,1,1,2,100,2,2,100,2,2,3,3,3,3,3,100,100,100,3,3,3,3,3,2,2,100,100,2,2,2,2,1,1,1,1,1,-4,-4,-4,1,1,1,1,2,2,2,2,100,2,2,2,3,3,3,3,100,100,100,3,3,3,3,2,2,100,100,2,100,100,100,100,100,1,1,1,-4,-4,-4,-4,-4,1,1,1,2,2,100,100,100,100,100,100,2,100,3,3,100,100,100,3,100,2,2,2,2,100,2,2,2,2,2,2,100,1,1,-4,-4,-4,-1073741824,-4,-4,-4,1,1,2,2,2,2,2,2,2,2,100,100,100,100,100,100,100,100,100,2,2,2,2,2,2,2,2,2,2,2,100,1,-4,-4,-4,-1073741824,-1073741824,-4,-4,-4,1,1,2,2,2,2,2,2,2,2,100,100,100,100,100,100,100,100,100,2,2,2,2,2,2,2,2,2,2,2,100,1,-4,-4,-4,-1073741824,-4,-4,-4,1,1,1,2,2,100,100,100,100,100,100,2,100,3,3,100,100,100,3,100,2,2,2,2,100,2,2,2,2,2,2,100,1,1,-4,-4,-4,-4,-4,1,1,1,1,2,2,2,2,100,2,2,2,3,3,3,3,100,100,100,3,3,3,3,2,2,100,100,2,100,100,100,100,100,1,1,1,-4,-4,-4,1,1,1,1,1,2,100,2,2,100,2,2,3,3,3,3,3,100,100,100,3,3,3,3,3,2,2,100,100,2,2,2,2,1,1,1,1,1,-4,1,1,1,1,1,2,2,100,2,2,2,2,3,3,3,3,3,3,100,100,100,3,3,3,3,3,3,2,2,2,2,2,2,2,100,100,1,1,1,1,1,1,1,1,2,2,2,100,2,2,2,3,3,100,100,100,100,100,100,3,100,100,100,100,100,100,3,3,2,2,2,2,2,2,2,100,1,1,1,1,1,2,2,2,2,2,2,100,100,100,2,3,3,100,3,3,3,100,3,3,3,100,3,3,3,100,3,3,2,2,2,2,2,2,2,100,100,2,2,1,2,2,2,2,2,2,2,2,100,2,2,3,3,100,3,3,3,100,3,3,3,100,3,3,3,100,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,100,100,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,2,2,2,3,3,3,3,3,3,3,3,3,2,2,2,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,2,2,2,2,2,2,2,2,2,2,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4],"height":32,"initial_bodies":[{"id":0,"team":"neutral","type":"cow","x":0,"y":22},{"id":1,"team":"neutral","type":"cow","x":0,"y":9},{"id":2,"team":"A","type":"hq","x":19,"y":22},{"id":3,"team":"B","type":"hq","x":19,"y":9},{"id":4,"team":"neutral","type":"cow","x":39,"y":22},{"id":5,"team":"neutral","type":"cow","x":39,"y":9}],"initial_water":0,"name":"Hourglass","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,0,0,0,2000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,0,0,0,2000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,175,175,175,175,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"horizontal","water":[true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true],"width":40} diff --git a/data/maps/bc20/Infinity.json b/data/maps/bc20/Infinity.json new file mode 100644 index 0000000..a27e93b --- /dev/null +++ b/data/maps/bc20/Infinity.json @@ -0,0 +1 @@ +{"elevation":[0,0,0,0,4,4,6,6,6,4,4,4,6,8,6,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,8,6,8,6,8,6,6,6,8,8,8,6,6,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,6,8,8,8,8,8,8,6,6,10,8,8,6,6,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,8,10,8,10,8,8,8,6,6,10,8,6,6,8,8,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,10,10,10,8,8,8,6,4,4,6,6,6,8,8,6,4,2,0,0,0,0,0,-12,-12,-12,0,0,0,0,0,0,6,8,8,10,10,10,6,8,2,2,0,0,4,8,8,8,6,4,2,2,0,0,0,-12,-12,-12,-12,-12,0,0,0,0,0,6,8,10,10,10,8,4,0,0,0,0,0,0,4,6,6,4,4,4,2,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,0,0,6,8,8,10,8,6,0,0,0,0,0,0,0,4,4,6,6,6,4,4,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,0,0,6,6,8,8,8,4,0,0,0,0,0,0,0,2,6,6,6,6,6,2,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,0,0,4,8,10,10,8,4,0,0,0,0,0,0,0,4,4,8,8,8,4,4,0,0,0,-12,-12,-12,-12,-12,0,0,0,0,0,4,8,8,8,6,6,2,0,0,0,0,0,0,2,6,6,6,6,6,2,0,0,0,0,-12,-12,-12,0,0,0,0,0,0,6,6,6,8,8,8,6,2,0,0,0,0,0,4,4,6,6,6,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,6,8,8,8,8,8,2,2,0,0,0,0,2,4,4,4,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,6,8,8,8,6,8,6,8,2,6,4,6,2,24,24,24,2,6,4,6,2,6,4,4,0,0,0,0,0,0,0,0,4,6,8,8,6,8,8,8,8,10,6,6,4,26,22,22,22,26,4,6,6,10,6,6,4,4,0,0,0,0,0,0,0,2,4,6,6,8,8,10,8,12,10,10,6,26,24,26,22,26,24,26,6,10,10,10,6,8,6,8,2,2,0,0,0,0,0,2,2,4,8,8,8,8,10,10,10,6,26,24,24,20,24,24,26,6,10,10,10,8,8,8,8,4,2,2,0,0,0,0,0,2,2,8,6,8,6,10,10,10,6,26,24,26,22,26,24,26,6,10,10,12,8,10,8,8,6,6,4,2,0,0,0,0,0,0,0,4,4,6,6,10,6,6,4,26,22,22,22,26,4,6,6,10,8,8,8,8,6,8,8,6,4,0,0,0,0,0,0,0,0,4,4,6,2,6,4,6,2,24,24,24,2,6,4,6,2,8,6,8,6,8,8,8,6,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,4,4,4,2,0,0,0,0,2,2,8,8,8,8,8,6,4,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,6,6,6,4,4,0,0,0,0,0,2,6,8,8,8,6,6,6,0,0,0,0,0,0,-12,-12,-12,0,0,0,0,2,6,6,6,6,6,2,0,0,0,0,0,0,2,6,6,8,8,8,4,0,0,0,0,0,-12,-12,-12,-12,-12,0,0,0,4,4,8,8,8,4,4,0,0,0,0,0,0,0,4,8,10,10,8,4,0,0,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,2,6,6,6,6,6,2,0,0,0,0,0,0,0,4,8,8,8,6,6,0,0,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,4,4,6,6,6,4,4,0,0,0,0,0,0,0,6,8,10,8,8,6,0,0,0,0,-12,-12,-12,-12,-12,-12,-12,0,0,2,4,4,4,6,6,4,0,0,0,0,0,0,4,8,10,10,10,8,6,0,0,0,0,0,-12,-12,-12,-12,-12,0,0,0,2,2,4,6,8,8,8,4,0,0,2,2,8,6,10,10,10,8,8,6,0,0,0,0,0,0,-12,-12,-12,0,0,0,0,0,2,4,6,8,8,6,6,6,4,4,6,8,8,8,10,10,10,8,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,8,8,6,6,8,10,6,6,8,8,8,10,8,10,8,6,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,6,6,6,8,8,10,6,6,8,8,8,8,8,8,6,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,6,6,8,8,8,6,6,6,8,6,8,6,8,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,6,6,8,6,4,4,4,6,6,6,4,4,0,0,0,0],"height":33,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":10,"y":10},{"id":1,"team":"B","type":"hq","x":22,"y":22},{"id":2,"team":"neutral","type":"cow","x":22,"y":11},{"id":3,"team":"neutral","type":"cow","x":10,"y":21},{"id":4,"team":"neutral","type":"cow","x":32,"y":23},{"id":5,"team":"neutral","type":"cow","x":0,"y":9},{"id":6,"team":"neutral","type":"cow","x":27,"y":32},{"id":7,"team":"neutral","type":"cow","x":5,"y":0},{"id":8,"team":"neutral","type":"cow","x":0,"y":32},{"id":9,"team":"neutral","type":"cow","x":32,"y":0}],"initial_water":0,"name":"Infinity","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":9999,"soup":[2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,2,2,2,2,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,8,2,2,2,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,8,2,2,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,8,0,0,0,0,0,0,2,2,4,4,4,6,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,6,6,6,6,6,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,4,4,8,8,6,6,6,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,6,4,6,8,8,8,6,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,4,6,6,6,8,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,4,4,8,1012,1010,1008,6,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,4,6,8,1010,1008,1010,6,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,4,6,6,1008,1006,1006,6,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,6,6,4,6,6,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,4,4,4,6,6,4,0,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,4,4,2,0,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,2,0,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,32,32,0,2,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,32,32,0,2,4,4,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,32,32,0,4,6,6,4,4,4,6,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,4,6,6,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,6,1006,1006,1008,6,6,4,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,4,6,1010,1008,1010,8,6,4,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,6,1008,1010,1012,8,4,4,4,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,6,6,8,6,6,6,4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,6,8,8,8,6,4,6,4,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,6,6,6,8,8,4,4,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,4,6,6,6,6,6,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,4,6,4,4,4,2,2,0,0,0,0,0,0,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,0,0,0,0,0,0,2,2,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,0,0,0,0,0,0,2,2,2,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,0,0,0,0,0,0,2,2,2,2,8,8,8,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,2,2,2],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":33} diff --git a/data/maps/bc20/Maze.json b/data/maps/bc20/Maze.json new file mode 100644 index 0000000..14520cd --- /dev/null +++ b/data/maps/bc20/Maze.json @@ -0,0 +1 @@ +{"elevation":[3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,2,2,2,2,2,2,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,2,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,2,2,2,2,2,2,2,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,2,2,2,2,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,3,2,2,2,2,3,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,-1073741824,2,2,2,2,2,2,2,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,1,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,100,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,1,3,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,100,1,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,1,3,3,1,1,3,3,1,1,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,100,3,1,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,1,1,3,3,1,1,3,3,1,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,100,100,100,100,100,1,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,3,1,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,1,1,3,3,1,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,1,1,3,3,1,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,1,3,3,1,1,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,1,3,3,1,1,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,1,3,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,1,100,100,100,100,100,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,1,3,3,1,1,3,3,1,1,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,1,3,100,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,1,1,3,3,1,1,3,3,1,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,1,100,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,3,1,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,100,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,1,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,2,2,2,2,2,2,2,-1073741824,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,-10,-10,-10,100,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,3,2,2,2,2,3,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,2,2,2,2,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,2,2,2,2,2,2,2,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,2,3,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,2,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,2,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,2,2,2,2,2,2,2,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,2,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,100,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,100,100,100,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,100,3,3,3,3,3,3,3,3,3],"height":64,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":56},{"id":1,"team":"B","type":"hq","x":58,"y":7},{"id":2,"team":"neutral","type":"cow","x":28,"y":14},{"id":3,"team":"neutral","type":"cow","x":35,"y":49},{"id":4,"team":"neutral","type":"cow","x":28,"y":27},{"id":5,"team":"neutral","type":"cow","x":35,"y":36},{"id":6,"team":"neutral","type":"cow","x":29,"y":38},{"id":7,"team":"neutral","type":"cow","x":34,"y":25}],"initial_water":0,"name":"Maze","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,150,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,150,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":64} diff --git a/data/maps/bc20/OmgThisIsProcedural.json b/data/maps/bc20/OmgThisIsProcedural.json new file mode 100644 index 0000000..5245fd1 --- /dev/null +++ b/data/maps/bc20/OmgThisIsProcedural.json @@ -0,0 +1 @@ +{"elevation":[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,19,3,3,3,3,3,3,3,3,3,3,3,18,19,19,19,11,11,19,19,19,18,3,3,3,3,3,3,3,3,3,3,3,19,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,18,17,10,13,18,3,3,3,3,3,3,3,3,3,3,18,12,17,15,16,10,10,16,15,17,12,18,3,3,3,3,3,3,3,3,3,3,18,13,10,17,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,17,16,12,16,12,3,3,3,3,3,3,3,3,3,3,3,3,13,15,19,13,3,3,13,19,15,13,3,3,3,3,3,3,3,3,3,3,3,3,12,16,12,16,17,13,3,3,3,3,3,3,3,3,3,3,13,13,18,19,17,16,12,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,12,16,17,19,18,13,13,3,3,3,3,3,3,3,13,13,13,12,17,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,17,12,13,13,13,3,3,3,3,3,3,3,3,3,13,17,10,11,12,19,3,3,3,3,3,3,3,3,3,3,3,16,3,3,3,3,3,3,3,3,3,3,3,3,3,3,16,3,3,3,3,3,3,3,3,3,3,3,19,12,11,10,17,13,3,3,3,3,3,3,3,3,3,3,15,19,10,17,19,17,13,15,3,3,3,3,3,3,3,3,19,11,11,3,3,3,3,3,3,3,3,3,3,3,3,11,11,19,3,3,3,3,3,3,3,3,15,13,17,19,17,10,19,15,3,3,3,3,3,3,3,3,3,3,3,12,12,16,15,10,13,11,14,3,3,3,3,3,3,3,3,10,10,3,3,3,10,19,3,3,19,10,3,3,3,10,10,3,3,3,3,3,3,3,3,14,11,13,10,15,16,12,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,18,16,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,16,18,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,14,16,3,3,3,3,3,3,3,3,3,3,3,3,15,3,3,3,17,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,18,17,3,3,3,15,3,3,3,3,3,3,3,3,3,3,3,3,16,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,17,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,14,17,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,19,3,3,3,3,14,3,3,3,3,3,3,3,3,3,3,3,13,10,13,13,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,13,10,13,3,3,3,3,3,3,3,3,3,3,3,14,3,3,3,3,19,13,3,3,3,3,3,3,17,15,12,13,3,3,3,3,3,3,3,3,3,3,3,19,14,11,3,3,3,11,16,19,3,3,19,16,11,3,3,3,11,14,19,3,3,3,3,3,3,3,3,3,3,3,13,12,15,17,3,3,3,3,3,3,3,3,3,3,19,18,11,10,3,3,3,3,3,3,3,3,3,3,3,3,10,12,16,3,3,17,10,10,11,11,10,10,17,3,3,16,12,10,3,3,3,3,3,3,3,3,3,3,3,3,10,11,18,19,3,3,3,3,3,3,3,3,3,3,3,11,16,15,12,16,10,18,13,3,3,3,14,3,3,3,14,11,3,3,3,3,15,17,3,3,17,15,3,3,3,3,11,14,3,3,3,14,3,3,3,13,18,10,16,12,15,16,11,3,3,3,3,3,3,3,3,3,3,3,3,3,18,15,17,19,11,17,17,11,3,18,10,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11,10,18,3,11,17,17,11,19,17,15,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,13,15,10,15,10,19,12,3,19,11,3,3,3,3,10,13,3,3,3,3,3,3,3,3,3,3,3,3,13,10,3,3,3,3,11,19,3,12,19,10,15,10,15,13,13,3,3,3,3,3,3,3,3,3,3,3,3,3,12,12,19,16,17,12,16,16,18,3,3,3,3,3,3,3,12,11,3,3,3,3,3,3,3,3,3,3,3,3,11,12,3,3,3,3,3,3,3,18,16,16,12,17,16,19,12,12,3,3,3,3,3,3,3,3,3,3,3,3,16,15,13,15,14,18,14,13,16,3,3,17,11,3,3,11,18,13,3,3,3,3,3,3,3,3,3,3,3,3,13,18,11,3,3,11,17,3,3,16,13,14,18,14,15,13,15,16,3,3,3,3,3,3,3,3,3,3,3,3,3,10,11,17,14,16,15,10,3,3,18,12,12,13,19,19,19,3,3,3,3,3,3,3,3,3,3,3,3,3,3,19,19,19,13,12,12,18,3,3,10,15,16,14,17,11,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,15,3,3,3,3,3,19,10,19,16,15,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11,15,16,19,10,19,3,3,3,3,3,15,17,3,3,3,3,3,3,3,3,3,3,3,3,3,12,3,3,3,3,3,3,3,3,3,3,3,11,14,10,14,10,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,10,14,10,14,11,3,3,3,3,3,3,3,3,3,3,3,12,3,3,3,3,3,3,3,3,11,3,3,3,3,3,3,3,3,3,3,14,3,3,3,17,18,3,3,3,3,10,18,18,3,3,3,3,18,18,10,3,3,3,3,18,17,3,3,3,14,3,3,3,3,3,3,3,3,3,3,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,12,3,3,3,3,3,3,3,3,3,12,11,19,3,3,3,3,19,11,12,3,3,3,3,3,3,3,3,3,12,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11,13,16,3,3,16,11,17,3,3,3,3,3,3,3,3,3,3,12,3,3,3,3,3,3,12,3,3,3,3,3,3,3,3,3,3,17,11,16,3,3,16,13,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,13,10,19,18,18,17,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,10,17,18,18,19,10,13,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,19,10,14,17,10,15,10,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,14,10,15,10,17,14,10,19,3,3,3,3,3,3,3,3,3,3,3,3,3,14,17,3,3,13,11,13,18,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,18,18,13,11,13,3,3,17,14,3,3,3,3,3,3,3,15,3,3,3,3,10,10,15,3,3,3,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,3,3,3,15,10,10,3,3,3,3,15,3,3,15,3,3,3,15,18,12,18,16,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,16,18,12,18,15,3,3,3,15,3,3,3,3,3,3,12,10,16,16,16,14,3,3,3,3,3,3,3,3,3,3,3,10,15,17,3,3,3,3,3,3,3,3,3,3,17,15,10,3,3,3,3,3,3,3,3,3,3,3,14,16,16,16,10,12,3,3,3,3,3,3,3,3,3,16,14,14,10,10,19,16,3,3,3,3,3,3,3,3,3,3,3,13,10,12,10,14,14,3,3,3,3,14,14,10,12,10,13,3,3,3,3,3,3,3,3,3,3,3,16,19,10,10,14,14,16,3,3,3,3,3,3,3,3,3,12,3,3,3,16,19,3,3,3,3,3,3,3,3,3,3,3,12,15,14,15,18,16,15,3,3,15,16,18,15,14,15,12,3,3,3,3,3,3,3,3,3,3,3,19,16,3,3,3,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,12,13,3,3,3,3,3,3,3,3,3,3,3,12,17,17,11,16,12,12,3,3,12,12,16,11,17,17,12,3,3,3,3,3,3,3,3,3,3,3,13,12,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,18,3,3,3,3,3,3,3,3,3,3,3,14,15,18,13,15,13,12,3,3,12,13,15,13,18,15,14,3,3,3,3,3,3,3,3,3,3,3,18,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,18,3,3,3,3,3,3,3,3,3,3,3,13,12,14,3,3,13,19,-100,-100,19,13,3,3,14,12,13,3,3,3,3,3,3,3,3,3,3,3,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,11,3,3,3,3,3,14,14,3,-100,-100,-100,-100,-100,-100,-100,-100,3,14,14,3,3,3,3,3,11,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,19,3,3,3,3,3,3,3,3,3,3,12,11,10,3,3,3,3,3,3,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,3,3,3,3,3,3,10,11,12,3,3,3,3,3,3,3,3,3,3,19,3,3,3,3,3,3,3,3,3,16,19,18,3,3,3,3,3,3,3,3,3,17,16,3,3,3,3,3,3,3,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,3,3,3,3,3,3,3,16,17,3,3,3,3,3,3,3,3,3,18,19,16,3,3,3,3,3,3,3,3,3,18,14,3,3,3,18,3,3,3,3,3,3,19,3,3,3,3,3,3,3,-100,-100,-100,-1073741824,-100,-100,-1073741824,-100,-100,-100,3,3,3,3,3,3,3,19,3,3,3,3,3,3,18,3,3,3,14,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,18,19,18,3,3,3,3,15,3,3,3,3,3,3,3,-100,-100,-100,-100,-100,-1073741824,-1073741824,-100,-100,-100,-100,-100,3,3,3,3,3,3,3,15,3,3,3,3,18,19,18,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11,10,13,3,3,10,19,16,10,3,3,3,3,3,3,3,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,3,3,3,3,3,3,3,10,16,19,10,3,3,13,10,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,10,15,13,17,14,16,16,3,3,3,3,3,3,3,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,3,3,3,3,3,3,3,16,16,14,17,13,15,10,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,18,11,12,12,18,3,3,3,11,3,3,3,15,-100,-100,-100,-100,-100,-100,-100,-100,-100,-100,15,3,3,3,11,3,3,3,18,12,12,11,18,15,3,3,3,3,3,3,3,3,3,3,3,3,3,19,15,3,3,3,3,3,3,3,14,13,19,17,11,11,3,3,18,18,3,3,3,3,17,-100,-100,-100,-100,-100,-100,-100,-100,17,3,3,3,3,18,18,3,3,11,11,17,19,13,14,3,3,3,3,3,3,3,15,19,3,3,3,15,12,14,16,18,3,3,3,3,3,18,18,15,18,18,19,12,14,19,16,3,3,3,3,3,3,3,3,-100,-100,3,3,3,3,3,3,3,3,16,19,14,12,19,18,18,15,18,18,3,3,3,3,3,18,16,14,12,15,3,3,13,12,19,12,19,19,14,3,3,3,18,13,15,3,17,17,15,19,13,3,3,3,3,3,3,3,11,3,3,3,3,11,3,3,3,3,3,3,3,13,19,15,17,17,3,15,13,18,3,3,3,14,19,19,12,19,12,13,3,3,3,16,16,3,3,3,13,3,3,3,11,12,3,3,3,18,11,19,3,3,3,3,3,3,3,10,10,15,3,3,15,10,10,3,3,3,3,3,3,3,19,11,18,3,3,3,12,11,3,3,3,13,3,3,3,16,16,3,3,3,3,3,3,3,3,3,12,3,3,3,3,3,3,3,3,18,19,16,3,3,3,3,3,3,3,11,12,14,3,3,14,12,11,3,3,3,3,3,3,3,16,19,18,3,3,3,3,3,3,3,3,12,3,3,3,3,3,3,3,3,3,3,3,3,17,3,3,3,3,3,3,3,3,3,3,12,14,3,3,3,3,3,3,3,3,12,12,3,3,3,3,12,12,3,3,3,3,3,3,3,3,14,12,3,3,3,3,3,3,3,3,3,3,17,3,3,3,3,3,3,3,3,3,19,11,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,15,11,3,3,11,15,13,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,11,19,3,3,3,3,3,3,3,3,16,16,11,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,15,10,3,3,10,15,13,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,11,16,16,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,14,16,12,3,3,12,16,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,15,12,16,3,3,16,12,15,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,19,3,3,19,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,13,17,3,3,3,3,3,3,15,19,3,3,3,3,3,3,16,13,17,17,10,19,16,16,19,10,17,17,13,16,3,3,3,3,3,3,19,15,3,3,3,3,3,3,17,13,3,3,3,3,3,3,3,3,3,3,3,3,3,3,16,3,3,3,3,3,3,17,14,17,18,3,3,3,3,19,11,13,10,13,14,11,10,10,11,14,13,10,13,11,19,3,3,3,3,18,17,14,17,3,3,3,3,3,3,16,3,3,3,3,3,3,3,3,3,3,19,12,17,3,3,3,3,3,3,3,3,3,10,11,17,17,3,3,3,3,10,10,14,16,16,10,3,3,10,16,16,14,10,10,3,3,3,3,17,17,11,10,3,3,3,3,3,3,3,3,3,17,12,19,3,3,3,3,3,14,18,12,3,3,3,3,3,3,3,3,3,3,3,3,18,19,12,10,3,3,3,3,3,3,10,10,3,3,10,10,3,3,3,3,3,3,10,12,19,18,3,3,3,3,3,3,3,3,3,3,3,3,12,18,14,3,3,3,3,10,18,3,3,3,3,3,3,3,3,3,3,3,3,3,11,13,19,19,10,3,3,3,3,3,17,13,3,3,13,17,3,3,3,3,3,10,19,19,13,11,3,3,3,3,3,3,3,3,3,3,3,3,3,18,10,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,14,3,3,3,15,10,17,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,17,10,15,3,3,3,14,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":64,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":27},{"id":1,"team":"B","type":"hq","x":54,"y":27},{"id":2,"team":"neutral","type":"cow","x":10,"y":14},{"id":3,"team":"neutral","type":"cow","x":49,"y":14},{"id":4,"team":"neutral","type":"cow","x":20,"y":57},{"id":5,"team":"neutral","type":"cow","x":39,"y":57},{"id":6,"team":"neutral","type":"cow","x":39,"y":39},{"id":7,"team":"neutral","type":"cow","x":20,"y":39}],"initial_water":0,"name":"OmgThisIsProcedural","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":4444,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,96,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,96,96,0,88,72,72,72,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,72,72,72,88,80,64,64,64,80,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,80,64,64,64,80,88,72,72,72,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,88,72,72,72,88,0,96,96,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,96,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,105,105,105,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,105,105,105,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,90,80,70,70,70,80,90,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,90,80,70,70,70,80,90,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,85,65,55,45,45,45,55,65,85,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,85,65,55,45,45,45,55,65,85,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,90,70,50,40,30,30,30,40,50,70,90,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,90,70,50,40,30,30,30,40,50,70,90,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,85,65,45,35,25,25,25,35,45,65,85,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,85,65,45,35,25,25,25,35,45,65,85,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,90,70,50,40,30,30,30,40,50,70,90,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,90,70,50,40,30,30,30,40,50,70,90,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,85,65,55,45,45,45,55,65,85,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,105,85,65,55,45,45,45,55,65,85,105,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,90,80,70,70,70,80,90,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,90,80,70,70,70,80,90,110,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,105,105,105,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,115,105,105,105,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"vertical","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":60} diff --git a/data/maps/bc20/Spiral.json b/data/maps/bc20/Spiral.json new file mode 100644 index 0000000..70fc0be --- /dev/null +++ b/data/maps/bc20/Spiral.json @@ -0,0 +1 @@ +{"elevation":[4,4,4,4,4,2,2,2,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-1073741824,2,2,4,5,4,4,4,4,4,4,2,2,2,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-1073741824,2,2,4,4,4,4,4,4,4,4,4,2,2,2,2,2,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-1073741824,-1073741824,2,2,2,4,4,4,4,4,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-12,-12,-12,-12,-12,-12,-12,-12,-12,-1073741824,-1073741824,2,2,4,4,4,4,4,2,2,2,-12,-12,2,4,4,4,4,4,4,4,4,4,2,2,2,-12,-12,-12,-12,-12,-12,-12,-1073741824,-1073741824,-1073741824,4,4,4,4,2,2,2,-12,-12,2,4,2,2,2,2,2,2,2,2,2,4,4,4,2,-12,-12,-12,-12,-12,-12,-12,-12,-12,4,4,4,2,2,2,-12,-12,2,4,2,-12,-12,-12,-12,-12,-12,-12,-12,2,2,2,2,4,2,-12,-12,-12,-12,-12,-12,-12,-12,2,4,2,2,2,-12,-12,2,4,2,-12,-12,2,2,2,2,2,2,-12,-12,2,2,2,2,4,2,-12,-12,-12,-12,-12,-12,-12,2,4,2,2,-12,-12,2,4,2,-12,-12,2,4,4,4,4,4,4,2,-12,-12,-12,-12,-12,2,4,2,-12,-12,-12,-12,-12,-12,2,4,2,2,-12,2,4,2,-12,-12,2,4,4,2,2,2,2,2,4,2,2,2,2,-12,-12,2,4,2,-12,-12,-12,-12,-12,2,4,2,2,-12,2,4,2,-12,2,4,4,2,-12,-12,-12,-12,-12,2,4,4,4,4,2,-12,-12,2,4,2,-12,-12,-12,-12,2,4,2,2,-12,2,4,2,-12,2,4,2,-12,-12,2,2,2,-12,-12,2,2,2,2,4,2,-12,-12,2,4,2,-12,-12,-12,2,4,2,2,-12,2,4,2,-12,2,4,2,-12,2,4,4,4,2,-12,-12,-12,-12,-12,2,4,2,-12,2,2,4,2,-12,-12,2,4,2,2,-12,2,4,2,-12,2,4,2,-12,2,4,4,4,4,2,2,2,2,-12,-12,2,4,-12,-12,2,2,4,2,2,2,4,2,2,-12,2,4,2,-12,2,4,2,-12,2,4,4,4,4,4,4,4,4,2,-12,2,4,2,-12,-12,2,2,4,2,2,4,2,2,-12,2,4,2,-12,2,4,2,-12,-12,2,2,2,2,2,2,2,4,2,-12,-12,2,4,2,-12,2,2,4,2,2,4,2,2,-12,2,4,2,-12,2,4,4,2,-12,-12,-12,-1073741824,-12,-12,-12,2,4,4,2,-12,2,4,2,-12,2,2,4,2,2,4,2,2,-12,2,4,2,-12,-12,2,4,2,2,2,2,2,2,2,-12,-12,2,4,2,-12,2,4,2,-12,2,2,4,2,2,4,2,2,-12,-12,2,4,2,-12,2,4,4,4,4,4,4,4,4,2,-12,2,4,2,-12,2,4,2,-12,2,2,4,2,2,2,4,2,2,-12,-12,4,2,-12,-12,2,2,2,2,4,4,4,4,2,-12,2,4,2,-12,2,4,2,-12,2,2,4,2,-12,-12,2,4,2,2,-12,2,4,2,-12,-12,-12,-12,-12,2,4,4,4,2,-12,2,4,2,-12,2,4,2,-12,2,2,4,2,-12,-12,-12,2,4,2,-12,-12,2,4,2,2,2,2,-12,-12,2,2,2,-12,-12,2,4,2,-12,2,4,2,-12,2,2,4,2,-12,-12,-12,-12,2,4,2,-12,-12,2,4,4,4,4,2,-12,-12,-12,-12,-12,2,4,4,2,-12,2,4,2,-12,2,2,4,2,-12,-12,-12,-12,-12,2,4,2,-12,-12,2,2,2,2,4,2,2,2,2,2,4,4,2,-12,-12,2,4,2,-12,2,2,4,2,-12,-12,-12,-12,-12,-12,2,4,2,-12,-12,-12,-12,-12,2,4,4,4,4,4,4,2,-12,-12,2,4,2,-12,-12,2,2,4,2,-12,-12,-12,-12,-12,-12,-12,2,4,2,2,2,2,-12,-12,2,2,2,2,2,2,-12,-12,2,4,2,-12,-12,2,2,2,4,2,-12,-12,-12,-12,-12,-12,-12,-12,2,4,2,2,2,2,-12,-12,-12,-12,-12,-12,-12,-12,2,4,2,-12,-12,2,2,2,4,4,4,-12,-12,-12,-12,-12,-12,-12,-12,-12,2,4,4,4,2,2,2,2,2,2,2,2,2,4,2,-12,-12,2,2,2,4,4,4,4,-1073741824,-1073741824,-1073741824,-12,-12,-12,-12,-12,-12,-12,2,2,2,4,4,4,4,4,4,4,4,4,2,-12,-12,2,2,2,4,4,4,4,4,2,2,-1073741824,-1073741824,-12,-12,-12,-12,-12,-12,-12,-12,-12,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,4,4,4,4,4,2,2,2,-1073741824,-1073741824,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,2,2,2,2,2,4,4,4,4,4,4,4,4,4,2,2,-1073741824,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,2,2,2,4,4,4,4,4,4,5,4,2,2,-1073741824,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,-12,2,2,2,4,4,4,4,4],"height":33,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":15,"y":13},{"id":1,"team":"B","type":"hq","x":17,"y":19}],"initial_water":0,"name":"Spiral","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4000,0,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,100,100,100,100,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,100,100,100,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,100,0,0,0,0,100,100,100,100,100,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,0,100,100,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,100,100,0,0,0,0,0,0,0,100,100,100,100,0,0,0,0,100,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,100,0,100,100,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,100,100,100,100,0,0,0,100,0,0,0,0,0,100,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,100,0,0,0,0,100,0,0,0,100,100,0,0,0,0,0,0,0,0,0,100,100,0,0,0,100,0,0,0,0,100,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,100,0,0,0,0,0,100,0,0,0,100,100,100,100,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,100,100,0,100,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,100,0,0,0,0,100,100,100,100,0,0,0,0,0,0,0,100,100,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,100,100,0,0,0,0,100,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,100,100,100,100,100,100,0,0,0,0,100,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,100,100,100,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,100,100,100,100,100,100,0,0,0,0,0,0,0,0,0,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,100,100,0,4000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,true,true,false,false,false,true,true,false,false,false,false,false,false,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,true,true,false,false,false,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,true,true,true,true,true,true,false,false,false,false,true,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,true,true,true,true,true,false,false,false,false,true,false,false,false,true,false,false,false,false,true,true,true,true,true,false,false,false,false,false,false,true,true,false,false,false,true,true,true,true,false,false,false,false,true,false,false,false,true,false,false,false,true,true,false,false,false,true,true,false,false,false,false,false,false,true,true,false,false,false,true,true,true,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,false,true,true,true,true,true,false,false,false,true,false,false,false,false,true,true,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,true,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,true,false,false,false,true,true,false,false,false,false,false,false,false,false,false,true,true,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,true,true,false,false,false,false,true,false,false,false,true,true,true,true,true,false,false,false,false,false,true,false,false,false,true,false,false,false,true,false,false,false,false,true,true,true,false,false,false,true,true,false,false,false,false,false,false,true,true,false,false,false,true,true,false,false,false,true,false,false,false,true,false,false,false,false,true,true,true,true,false,false,false,true,true,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,true,false,false,false,true,false,false,false,false,true,true,true,true,true,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,true,false,false,false,false,true,true,true,true,true,true,false,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,false,false,false,true,true,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,false,false,false,false,false,false,true,true,false,false,false,true,true,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,true,true,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false],"width":33} diff --git a/data/maps/bc20/Squares.json b/data/maps/bc20/Squares.json new file mode 100644 index 0000000..56b2a77 --- /dev/null +++ b/data/maps/bc20/Squares.json @@ -0,0 +1 @@ +{"elevation":[-1073741824,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1073741824,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,3000,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,500,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,120,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1073741824,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,-1073741824],"height":63,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":10,"y":31},{"id":1,"team":"B","type":"hq","x":52,"y":31},{"id":2,"team":"neutral","type":"cow","x":24,"y":24},{"id":3,"team":"neutral","type":"cow","x":38,"y":24},{"id":4,"team":"neutral","type":"cow","x":24,"y":38},{"id":5,"team":"neutral","type":"cow","x":38,"y":38}],"initial_water":0,"name":"Squares","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,100,100,100,100,100,100,100,100,0,100,100,100,100,1000,1000,1000,1000,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,1000,1000,1000,1000,1000,100,100,100,100,0,100,100,100,100,100,100,100,100,1000,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"vertical","water":[true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true],"width":63} diff --git a/data/maps/bc20/TheHighGround.json b/data/maps/bc20/TheHighGround.json new file mode 100644 index 0000000..4352db3 --- /dev/null +++ b/data/maps/bc20/TheHighGround.json @@ -0,0 +1 @@ +{"elevation":[3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,20,20,0,20,3,3,3,3,3,3,3,3,3,3,3,3,7,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,3,3,3,3,3,3,3,4,3,3,3,3,7,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,3,3,3,3,3,3,3,3,3,3,20,20,20,20,3,3,3,3,3,3,4,5,4,3,3,3,7,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,3,3,3,3,3,3,3,3,3,3,20,0,20,20,3,3,3,3,3,3,3,4,3,3,3,3,7,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,40,40,0,40,25,25,25,25,25,-30,-30,-30,-30,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,7,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,40,20,20,40,25,25,25,25,25,-30,-30,-30,-30,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,40,20,20,40,25,25,25,25,25,-30,-30,-30,-30,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30,30,30,30,30,30,30,30,30,30,30,30,25,25,25,25,25,40,0,40,40,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30,30,30,30,30,30,30,30,30,30,30,30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30,30,40,40,40,40,40,40,40,40,30,30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30,30,40,40,40,40,40,40,40,40,30,30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,30,30,40,40,60,60,60,60,40,40,30,30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,5,5,3,3,3,3,3,3,3,3,30,30,40,40,40,80,80,80,80,40,30,30,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,5,5,3,3,3,3,3,3,3,3,3,3,8,8,3,3,3,3,3,3,3,3,30,30,40,40,40,40,100,100,100,100,30,30,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,8,8,3,3,3,3,3,3,3,3,3,3,11,11,3,3,-30,-30,-30,-30,3,3,30,30,30,30,30,30,30,120,120,120,120,30,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,11,11,3,3,3,3,3,3,3,3,3,3,14,14,3,3,-30,-30,-30,-30,3,3,30,30,30,30,30,30,30,30,140,140,140,140,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,14,14,3,3,3,3,3,3,3,3,3,3,17,17,3,3,-30,-30,-30,-30,3,3,3,25,25,25,25,25,25,25,25,160,160,160,160,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,17,17,3,3,3,3,3,3,3,3,3,3,20,20,3,3,-30,-30,-30,-30,3,3,3,3,25,25,25,25,25,25,25,25,180,180,180,180,-4,-4,-4,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,20,20,3,3,3,3,3,3,3,3,3,3,23,23,3,3,-30,-30,-30,-30,3,3,3,3,3,25,25,25,25,25,25,25,25,200,200,200,200,-1073741824,-1073741824,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,23,23,3,3,3,3,3,3,3,3,3,3,26,26,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,220,220,220,220,-1073741824,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,26,26,3,3,3,3,3,3,3,3,3,3,29,29,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,240,240,240,240,240,240,240,25,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29,29,3,3,3,3,3,3,3,3,3,3,32,32,3,3,-30,-30,-30,-30,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,260,260,260,260,260,260,260,260,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,32,32,3,3,3,3,3,3,3,3,3,3,35,35,3,3,-30,-30,-30,-30,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,280,280,280,280,280,280,280,280,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,35,35,3,3,3,3,3,3,3,3,3,3,38,38,3,3,-30,-30,-30,-30,3,3,3,3,3,3,3,3,3,-4,-1073741824,201,202,-1073741824,-1073741824,300,300,300,300,300,300,300,300,-1073741824,-1073741824,201,200,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,38,38,3,3,3,3,3,3,3,3,3,3,41,41,3,3,-30,-30,-30,-30,3,3,3,3,3,3,3,3,3,-4,-1073741824,200,201,-1073741824,-1073741824,320,320,320,320,320,320,320,320,-1073741824,-1073741824,202,201,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,41,41,3,3,3,3,3,3,3,3,3,3,44,44,3,3,-30,-30,-30,-30,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,340,340,340,340,340,340,340,340,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,44,44,3,3,3,3,3,3,3,3,3,3,47,47,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,360,360,360,360,360,360,360,360,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,47,47,3,3,3,3,3,3,3,3,3,3,50,50,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,380,380,380,380,380,380,380,380,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,50,50,3,3,3,3,3,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,-1073741824,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-1073741824,-1073741824,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-1073741824,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-1073741824,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,400,-1073741824,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,-4,50,50,-4,-4,-4,-4,-4,3,3,3,3,3,50,50,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,380,380,380,380,380,380,380,380,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,50,50,3,3,3,3,3,3,3,3,3,3,47,47,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,360,360,360,360,360,360,360,360,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,47,47,3,3,3,3,3,3,3,3,3,3,44,44,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,340,340,340,340,340,340,340,340,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,-30,-30,-30,-30,3,3,44,44,3,3,3,3,3,3,3,3,3,3,41,41,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,201,202,-1073741824,-1073741824,320,320,320,320,320,320,320,320,-1073741824,-1073741824,201,200,-1073741824,-4,3,3,3,3,3,3,3,3,3,-30,-30,-30,-30,3,3,41,41,3,3,3,3,3,3,3,3,3,3,38,38,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,200,201,-1073741824,-1073741824,300,300,300,300,300,300,300,300,-1073741824,-1073741824,202,201,-1073741824,-4,3,3,3,3,3,3,3,3,3,-30,-30,-30,-30,3,3,38,38,3,3,3,3,3,3,3,3,3,3,35,35,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,280,280,280,280,280,280,280,280,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,-30,-30,-30,-30,3,3,35,35,3,3,3,3,3,3,3,3,3,3,32,32,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,260,260,260,260,260,260,260,260,-1073741824,-1073741824,-1073741824,-1073741824,-1073741824,-4,3,3,3,3,3,3,3,3,3,-30,-30,-30,-30,3,3,32,32,3,3,3,3,3,3,3,3,3,3,29,29,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,-4,-4,-1073741824,-1073741824,-1073741824,-1073741824,25,240,240,240,240,240,240,240,-1073741824,-1073741824,-1073741824,-1073741824,-4,-4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,29,29,3,3,3,3,3,3,3,3,3,3,26,26,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,-1073741824,220,220,220,220,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,26,26,3,3,3,3,3,3,3,3,3,3,23,23,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,-1073741824,-1073741824,200,200,200,200,25,25,25,25,25,25,25,25,3,3,3,3,3,-30,-30,-30,-30,3,3,23,23,3,3,3,3,3,3,3,3,3,3,20,20,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,-4,-4,-4,180,180,180,180,25,25,25,25,25,25,25,25,3,3,3,3,-30,-30,-30,-30,3,3,20,20,3,3,3,3,3,3,3,3,3,3,17,17,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,160,160,160,160,25,25,25,25,25,25,25,25,3,3,3,-30,-30,-30,-30,3,3,17,17,3,3,3,3,3,3,3,3,3,3,14,14,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,140,140,140,140,30,30,30,30,30,30,30,30,3,3,-30,-30,-30,-30,3,3,14,14,3,3,3,3,3,3,3,3,3,3,11,11,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,30,120,120,120,120,30,30,30,30,30,30,30,3,3,-30,-30,-30,-30,3,3,11,11,3,3,3,3,3,3,3,3,3,3,8,8,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,30,30,100,100,100,100,40,40,40,40,30,30,3,3,3,3,3,3,3,3,8,8,3,3,3,3,3,3,3,3,3,3,5,5,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,30,30,40,80,80,80,80,40,40,40,30,30,3,3,3,3,3,3,3,3,5,5,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,30,30,40,40,60,60,60,60,40,40,30,30,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,30,30,40,40,40,40,40,40,40,40,30,30,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,30,30,40,40,40,40,40,40,40,40,30,30,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,30,30,30,30,30,30,30,30,30,30,30,30,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,40,40,0,40,25,25,25,25,25,30,30,30,30,30,30,30,30,30,30,30,30,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,-30,-30,-30,-30,25,25,25,25,25,40,20,20,40,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,-30,-30,-30,-30,25,25,25,25,25,40,20,20,40,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,7,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,-30,-30,-30,-30,25,25,25,25,25,40,0,40,40,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,7,3,3,3,3,4,3,3,3,3,3,3,3,20,20,0,20,3,3,3,3,3,3,3,3,3,3,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,7,3,3,3,4,5,4,3,3,3,3,3,3,20,20,20,20,3,3,3,3,3,3,3,3,3,3,25,25,25,-30,-30,-30,-30,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,7,3,3,3,3,4,3,3,3,3,3,3,3,20,20,20,20,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,7,3,3,3,3,3,3,3,3,3,3,3,3,20,0,20,20,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":64,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":5},{"id":1,"team":"B","type":"hq","x":58,"y":58},{"id":2,"team":"neutral","type":"cow","x":18,"y":49},{"id":3,"team":"neutral","type":"cow","x":45,"y":14},{"id":4,"team":"neutral","type":"cow","x":21,"y":46},{"id":5,"team":"neutral","type":"cow","x":42,"y":17},{"id":6,"team":"neutral","type":"cow","x":24,"y":25},{"id":7,"team":"neutral","type":"cow","x":39,"y":38},{"id":8,"team":"neutral","type":"cow","x":24,"y":26},{"id":9,"team":"neutral","type":"cow","x":39,"y":37},{"id":10,"team":"neutral","type":"cow","x":24,"y":37},{"id":11,"team":"neutral","type":"cow","x":39,"y":26},{"id":12,"team":"neutral","type":"cow","x":24,"y":38},{"id":13,"team":"neutral","type":"cow","x":39,"y":25},{"id":14,"team":"neutral","type":"cow","x":25,"y":25},{"id":15,"team":"neutral","type":"cow","x":38,"y":38},{"id":16,"team":"neutral","type":"cow","x":25,"y":26},{"id":17,"team":"neutral","type":"cow","x":38,"y":37},{"id":18,"team":"neutral","type":"cow","x":25,"y":37},{"id":19,"team":"neutral","type":"cow","x":38,"y":26},{"id":20,"team":"neutral","type":"cow","x":25,"y":38},{"id":21,"team":"neutral","type":"cow","x":38,"y":25},{"id":22,"team":"neutral","type":"cow","x":27,"y":6},{"id":23,"team":"neutral","type":"cow","x":36,"y":57},{"id":24,"team":"neutral","type":"cow","x":28,"y":59},{"id":25,"team":"neutral","type":"cow","x":35,"y":4}],"initial_water":0,"name":"TheHighGround","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,150,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,150,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,150,150,150,150,150,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,150,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,150,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,false,false,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,true,true,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":64} diff --git a/data/maps/bc20/Toothpaste.json b/data/maps/bc20/Toothpaste.json new file mode 100644 index 0000000..2810fdb --- /dev/null +++ b/data/maps/bc20/Toothpaste.json @@ -0,0 +1 @@ +{"elevation":[-1073741824,100,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,2,2,2,2,2,100,100,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,10,10,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,10,10,2,3,2,2,2,2,2,3,3,3,3,3,3,3,2,100,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,10,10,2,3,2,2,2,2,2,3,3,2,2,2,2,2,2,100,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,3,3,2,3,3,3,3,3,100,2,2,5,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,1,2,3,2,2,2,2,2,2,100,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,2,2,2,2,2,3,2,2,2,1,1,2,3,3,3,3,3,3,2,100,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,7,7,2,2,2,3,3,2,2,1,1,1,99,3,3,3,2,2,2,2,100,100,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,7,12,9,9,7,7,2,2,2,3,3,2,2,1,1,1,99,100,3,3,2,3,3,3,3,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,12,9,9,7,7,2,2,2,3,3,2,2,1,1,1,99,100,2,3,2,2,2,3,2,2,2,2,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,9,7,7,2,2,2,3,3,2,1,1,1,1,99,79,2,3,3,3,2,3,2,2,2,7,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,7,7,7,7,7,2,2,2,2,3,3,2,1,1,-4,-4,99,59,2,2,2,3,3,3,2,2,2,7,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,7,2,2,2,2,2,2,2,2,2,3,3,2,1,1,-4,29,39,2,2,2,2,2,3,3,2,2,2,7,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1000,3,3,1,1,-4,-4,29,29,2,2,2,2,2,2,3,3,2,2,7,8,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,1000,1000,3,1,-4,-4,-4,29,2,2,2,2,2,2,2,2,3,3,6,7,8,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,1000,2,3,1,-4,29,29,29,2,2,2,2,2,2,2,2,2,3,6,7,2,2,2,2,2,2,2,2,2,2,2,6,4,2,2,2,2,2,2,2,2,2,2,100,100,1000,2,3,1,-4,2,3,2,2,2,2,2,2,2,2,2,2,2,6,7,2,2,2,2,2,2,2,2,2,2,2,6,4,2,2,2,2,2,2,2,2,2,2,100,2,2,2,3,1,2,2,100,2,2,2,2,2,2,2,2,2,2,4,6,2,2,2,2,2,2,2,2,2,2,2,2,6,4,2,2,2,2,2,2,2,2,2,2,1001,2,2,2,3,2,2,2,1001,2,2,2,2,2,2,2,2,2,2,4,6,2,2,2,2,2,2,2,2,2,2,2,2,6,4,2,2,2,2,2,2,2,2,2,2,100,2,2,1,3,2,2,2,100,2,2,2,2,2,2,2,2,2,2,4,6,2,2,2,2,2,2,2,2,2,2,2,7,6,2,2,2,2,2,2,2,2,2,2,2,3,2,-4,1,3,2,1000,100,100,2,2,2,2,2,2,2,2,2,2,4,6,2,2,2,2,2,2,2,2,2,2,2,7,6,3,2,2,2,2,2,2,2,2,2,29,29,29,-4,1,3,2,1000,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,8,7,6,3,3,2,2,2,2,2,2,2,2,29,-4,-4,-4,1,3,1000,1000,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,8,7,2,2,3,3,2,2,2,2,2,2,29,29,-4,-4,1,1,3,3,1000,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,7,2,2,2,3,3,2,2,2,2,2,39,29,-4,1,1,2,3,3,2,2,2,2,2,2,2,2,2,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,7,2,2,2,3,3,3,2,2,2,59,99,-4,-4,1,1,2,3,3,2,2,2,2,7,7,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,7,2,2,2,3,2,3,3,3,2,79,99,1,1,1,1,2,3,3,2,2,2,7,7,9,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,2,3,2,2,2,3,2,100,99,1,1,1,2,2,3,3,2,2,2,7,7,9,9,12,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,3,3,3,3,2,3,3,100,99,1,1,1,2,2,3,3,2,2,2,7,7,9,9,12,7,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,100,100,2,2,2,2,3,3,3,99,1,1,1,2,2,3,3,2,2,2,7,7,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,100,2,3,3,3,3,3,3,2,1,1,2,2,2,3,2,2,2,2,2,7,7,7,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,100,2,2,2,2,2,2,3,2,1,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,5,2,2,100,3,3,3,3,3,2,3,3,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,2,2,2,3,3,2,2,2,2,2,3,2,10,10,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,3,3,3,3,3,3,3,2,2,2,2,2,3,2,10,10,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,10,10,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,100,2,2,2,2,2,2,2,2,100,2,2,2,2,2,2,2,2,2,2,2,2,2,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,100,-1073741824],"height":37,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":32},{"id":1,"team":"B","type":"hq","x":39,"y":4},{"id":2,"team":"neutral","type":"cow","x":10,"y":18},{"id":3,"team":"neutral","type":"cow","x":34,"y":18},{"id":4,"team":"neutral","type":"cow","x":11,"y":19},{"id":5,"team":"neutral","type":"cow","x":33,"y":17},{"id":6,"team":"neutral","type":"cow","x":12,"y":20},{"id":7,"team":"neutral","type":"cow","x":32,"y":16},{"id":8,"team":"neutral","type":"cow","x":14,"y":8},{"id":9,"team":"neutral","type":"cow","x":30,"y":28},{"id":10,"team":"neutral","type":"cow","x":14,"y":22},{"id":11,"team":"neutral","type":"cow","x":30,"y":14}],"initial_water":0,"name":"Toothpaste","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,120,0,0,0,120,120,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,120,0,0,0,0,0,0,0,120,0,120,0,120,0,0,120,0,120,120,120,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,120,0,120,0,120,0,0,120,120,120,120,120,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,0,0,0,0,0,0,0,120,0,120,0,120,0,0,0,120,120,120,120,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,0,0,0,0,0,0,0,120,0,120,0,120,0,0,0,0,120,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,0,0,0,0,0,0,0,0,0,0,120,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,120,0,0,0,0,120,0,120,0,120,0,0,0,0,0,0,0,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,0,0,120,120,120,120,0,0,0,120,0,120,0,120,0,0,0,0,0,0,0,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,0,0,120,120,120,120,120,0,0,120,0,120,0,120,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,0,0,120,120,120,0,120,0,0,120,0,120,0,120,0,0,0,0,0,0,0,120,120,120,120,120,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,120,120,120,120,120,0,0,0,120,120,0,0,0,120,0,0,0,0,0,0,0,0],"symmetry":"rotational","water":[true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true],"width":45} diff --git a/data/maps/bc20/TwoLakeLand.json b/data/maps/bc20/TwoLakeLand.json new file mode 100644 index 0000000..af49193 --- /dev/null +++ b/data/maps/bc20/TwoLakeLand.json @@ -0,0 +1 @@ +{"elevation":[1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,1,1,-100,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,3,3,1,-100,-100,-100,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,4,4,1,1,1,3,3,3,1,3,1,-100,-100,-100,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,4,4,4,4,1,1,3,3,3,3,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,4,4,4,4,4,4,1,3,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,4,4,4,4,4,4,4,4,2,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,4,4,4,2,2,2,4,4,2,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,4,4,2,3,3,3,2,2,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,3,3,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,40,2,3,3,3,3,3,3,3,3,3,2,3,2,2,2,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,40,40,40,2,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,2,40,40,-1073741824,40,40,2,3,3,3,3,3,2,3,3,2,2,2,2,2,2,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,1,3,3,3,3,3,3,2,3,40,40,40,2,3,3,3,3,3,2,3,3,2,2,2,2,2,2,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,1,3,3,3,3,3,3,3,3,3,40,3,2,3,3,3,3,2,3,3,3,3,2,2,2,2,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,20,3,1,3,3,3,3,3,3,3,3,3,40,3,2,3,3,3,2,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,30,20,3,1,3,3,3,3,3,3,3,70,3,40,3,3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,40,30,10,10,10,3,3,3,3,3,3,90,70,3,40,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,30,20,3,1,3,20,3,3,3,120,90,60,60,40,3,70,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,20,3,1,3,20,30,3,3,3,90,70,3,40,3,70,90,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,10,10,10,30,40,3,3,3,70,3,40,60,60,90,120,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,20,3,1,3,20,30,3,3,3,90,70,3,40,3,70,90,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,30,20,3,1,3,20,3,3,3,120,90,60,60,40,3,70,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,40,30,10,10,10,3,3,3,3,3,3,90,70,3,40,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,30,20,3,1,3,3,3,3,3,3,3,70,3,40,3,3,2,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,20,3,1,3,3,3,3,3,3,3,3,3,40,3,2,3,3,3,2,3,3,3,3,3,3,2,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,1,3,3,3,3,3,3,3,3,3,40,3,2,3,3,3,3,2,3,3,3,3,2,2,2,2,3,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,1,3,3,3,3,3,3,2,3,40,40,40,2,3,3,3,3,3,2,3,3,2,2,2,2,2,2,3,3,3,3,3,3,3,3,1,1,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,2,40,40,-1073741824,40,40,2,3,3,3,3,3,2,3,3,2,2,2,2,2,2,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,40,40,40,2,3,3,3,3,3,3,3,2,3,3,2,2,2,2,3,3,3,3,3,3,3,3,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,2,3,2,40,2,3,3,3,3,3,3,3,3,3,2,3,2,2,2,3,3,3,3,3,3,3,3,3,1,1,1,2,3,3,3,3,3,3,3,3,3,3,3,3,2,2,3,3,2,3,3,3,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,3,3,1,1,1,1,2,3,3,3,3,3,3,2,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,4,4,2,3,3,3,2,2,3,3,3,3,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,4,4,4,2,2,2,4,4,2,3,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,4,4,4,4,4,4,4,4,2,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,4,4,4,4,4,4,1,3,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,4,4,4,4,1,1,3,3,3,3,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,4,4,1,1,1,3,3,3,1,3,1,-100,-100,-100,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,3,3,1,3,3,1,-100,-100,-100,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,1,1,-100,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":45,"initial_bodies":[{"id":0,"team":"neutral","type":"cow","x":10,"y":25},{"id":1,"team":"neutral","type":"cow","x":10,"y":19},{"id":2,"team":"A","type":"hq","x":10,"y":30},{"id":3,"team":"B","type":"hq","x":10,"y":14},{"id":4,"team":"neutral","type":"cow","x":12,"y":23},{"id":5,"team":"neutral","type":"cow","x":12,"y":21},{"id":6,"team":"neutral","type":"cow","x":15,"y":38},{"id":7,"team":"neutral","type":"cow","x":15,"y":6},{"id":8,"team":"neutral","type":"cow","x":41,"y":41},{"id":9,"team":"neutral","type":"cow","x":41,"y":3}],"initial_water":0,"name":"TwoLakeLand","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,200,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,200,500,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,500,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"horizontal","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":45} diff --git a/data/maps/bc20/WateredDown.json b/data/maps/bc20/WateredDown.json new file mode 100644 index 0000000..495a96f --- /dev/null +++ b/data/maps/bc20/WateredDown.json @@ -0,0 +1 @@ +{"elevation":[2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,3,5,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,3,2,2,2,2,2,1,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,-1,-1,-1,2,2,-1,-1,-1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-10,-10,-1073741824,-10,-10,-1,-1,-1,-1,-10,-1,-1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,-1,-1,-10,-10,-10,-10,-10,-1,-1,-10,-10,-10,-1,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,2,1,1,2,-1,-1,-1,-1,-1,-10,-10,-10,-10,-1,-1,-1,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,1,1,2,2,2,2,2,2,-1,-1,-1,-1,-1,-1,2,2,2,2,2,2,3,2,2,2,2,8,8,8,8,8,8,8,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,8,2,0,0,0,0,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,2,2,0,0,0,0,2,4,6,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,3,2,2,7,7,0,0,6,6,6,6,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,7,6,6,6,6,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,8,7,6,6,6,6,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,7,7,0,0,6,6,6,6,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,2,4,6,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,2,2,2,3,2,2,0,0,0,0,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,2,2,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,8,2,1,1,2,2,2,2,2,2,-1,-1,-1,-1,-1,-1,2,2,2,2,2,2,3,2,2,2,2,8,8,8,8,8,8,8,2,1,1,2,-1,-1,-1,-1,-1,-10,-10,-10,-10,-1,-1,-1,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,8,2,2,1,-1,-1,-10,-10,-10,-10,-10,-1,-1,-10,-10,-10,-1,2,2,2,2,2,2,2,2,2,2,2,2,8,8,8,8,2,2,2,-1,-10,-10,-1073741824,-10,-10,-1,-1,-1,-1,-10,-1,-1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-1,-1,-1,-1,-1,-1,-1,2,2,-1,-1,-1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,2,2,2,2,2,2,1,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,2,2,2,2,2,2,3,4,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,-4,-4,-4,2,2,3,2,2,2,2,2,3,5,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,4],"height":32,"initial_bodies":[{"id":0,"team":"neutral","type":"cow","x":0,"y":16},{"id":1,"team":"neutral","type":"cow","x":0,"y":15},{"id":2,"team":"neutral","type":"cow","x":3,"y":29},{"id":3,"team":"neutral","type":"cow","x":3,"y":2},{"id":4,"team":"neutral","type":"cow","x":20,"y":21},{"id":5,"team":"neutral","type":"cow","x":20,"y":10},{"id":6,"team":"A","type":"hq","x":20,"y":28},{"id":7,"team":"B","type":"hq","x":20,"y":3},{"id":8,"team":"neutral","type":"cow","x":25,"y":18},{"id":9,"team":"neutral","type":"cow","x":25,"y":13},{"id":10,"team":"neutral","type":"cow","x":28,"y":29},{"id":11,"team":"neutral","type":"cow","x":28,"y":2},{"id":12,"team":"neutral","type":"cow","x":29,"y":18},{"id":13,"team":"neutral","type":"cow","x":29,"y":13}],"initial_water":0,"name":"WateredDown","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":43223,"soup":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,300,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,300,300,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,300,0,0,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,0,0,0,0,0,0,0,0,0,0,300,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,0,0,0,0,0,300,300,300,300,300,0,0,300,300,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,300,300,0,300,300,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,300,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,200,200,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"symmetry":"horizontal","water":[false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,true,false,false,true,true,true,true,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,false,false,false,false,false,true,true,false,false,false,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,false,false,true,false,false,true,true,true,true,false,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":32} diff --git a/data/maps/bc20/maptestsmall.json b/data/maps/bc20/maptestsmall.json new file mode 100644 index 0000000..c51d2c2 --- /dev/null +++ b/data/maps/bc20/maptestsmall.json @@ -0,0 +1 @@ +{"elevation":[1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,-1073741824,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,-1073741824,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3],"height":32,"initial_bodies":[{"id":0,"team":"A","type":"hq","x":5,"y":5},{"id":1,"team":"B","type":"hq","x":26,"y":5},{"id":2,"team":"neutral","type":"cow","x":10,"y":10},{"id":3,"team":"neutral","type":"cow","x":21,"y":10},{"id":4,"team":"neutral","type":"cow","x":4,"y":18},{"id":5,"team":"neutral","type":"cow","x":27,"y":18}],"initial_water":0,"name":"maptestsmall","pollution":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"random_seed":30,"soup":[31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,63,61,59,57,55,53,51,49,47,45,43,41,39,37,35,33,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,95,92,89,86,83,80,77,74,71,68,65,62,59,56,53,50,50,53,56,59,62,65,68,71,74,77,80,83,86,89,92,95,127,123,119,115,111,107,103,99,95,91,87,83,79,75,71,67,67,71,75,79,83,87,91,95,99,103,107,111,115,119,123,127,159,154,149,144,139,134,129,124,119,114,109,104,99,94,89,84,84,89,94,99,104,109,114,119,124,129,134,139,144,149,154,159,191,185,179,173,167,161,155,149,143,137,131,125,119,113,107,101,101,107,113,119,125,131,137,143,149,155,161,167,173,179,185,191,223,216,209,202,195,188,181,174,167,160,153,146,139,132,125,118,118,125,132,139,146,153,160,167,174,181,188,195,202,209,216,223,255,247,239,231,223,215,207,199,191,183,175,167,159,151,143,135,135,143,151,159,167,175,183,191,199,207,215,223,231,239,247,255,287,278,269,260,251,242,233,224,215,206,197,188,179,170,161,152,152,161,170,179,188,197,206,215,224,233,242,251,260,269,278,287,319,309,299,289,279,269,259,249,239,229,219,209,199,189,179,169,169,179,189,199,209,219,229,239,249,259,269,279,289,299,309,319,351,340,329,318,307,296,285,274,263,252,241,230,219,208,197,186,186,197,208,219,230,241,252,263,274,285,296,307,318,329,340,351,383,371,359,347,335,323,311,299,287,275,263,251,239,227,215,203,203,215,227,239,251,263,275,287,299,311,323,335,347,359,371,383,415,402,389,376,363,350,337,324,311,298,285,272,259,246,233,220,220,233,246,259,272,285,298,311,324,337,350,363,376,389,402,415,447,433,419,405,391,377,363,349,335,321,307,293,279,265,251,237,237,251,265,279,293,307,321,335,349,363,377,391,405,419,433,447,479,464,449,434,419,404,389,374,359,344,329,314,299,284,269,254,254,269,284,299,314,329,344,359,374,389,404,419,434,449,464,479,511,495,479,463,447,431,415,399,383,367,351,335,319,303,287,271,271,287,303,319,335,351,367,383,399,415,431,447,463,479,495,511,543,526,509,492,475,458,441,424,407,390,373,356,339,322,305,288,288,305,322,339,356,373,390,407,424,441,458,475,492,509,526,543,575,557,539,521,503,485,467,449,431,413,395,377,359,341,323,305,305,323,341,359,377,395,413,431,449,467,485,503,521,539,557,575,607,588,569,550,531,512,493,474,455,436,417,398,379,360,341,322,322,341,360,379,398,417,436,455,474,493,512,531,550,569,588,607,639,619,599,579,559,539,519,499,479,459,439,419,399,379,359,339,339,359,379,399,419,439,459,479,499,519,539,559,579,599,619,639,671,650,629,608,587,566,545,524,503,482,461,440,419,398,377,356,356,377,398,419,440,461,482,503,524,545,566,587,608,629,650,671,703,681,659,637,615,593,571,549,527,505,483,461,439,417,395,373,373,395,417,439,461,483,505,527,549,571,593,615,637,659,681,703,735,712,689,666,643,620,597,574,551,528,505,482,459,436,413,390,390,413,436,459,482,505,528,551,574,597,620,643,666,689,712,735,767,743,719,695,671,647,623,599,575,551,527,503,479,455,431,407,407,431,455,479,503,527,551,575,599,623,647,671,695,719,743,767,799,774,749,724,699,674,649,624,599,574,549,524,499,474,449,424,424,449,474,499,524,549,574,599,624,649,674,699,724,749,774,799,831,805,779,753,727,701,675,649,623,597,571,545,519,493,467,441,441,467,493,519,545,571,597,623,649,675,701,727,753,779,805,831,863,836,809,782,755,728,701,674,647,620,593,566,539,512,485,458,458,485,512,539,566,593,620,647,674,701,728,755,782,809,836,863,895,867,839,811,783,755,727,699,671,643,615,587,559,531,503,475,475,503,531,559,587,615,643,671,699,727,755,783,811,839,867,895,927,898,869,840,811,782,753,724,695,666,637,608,579,550,521,492,492,521,550,579,608,637,666,695,724,753,782,811,840,869,898,927,959,929,899,869,839,809,779,749,719,689,659,629,599,569,539,509,509,539,569,599,629,659,689,719,749,779,809,839,869,899,929,959,991,960,929,898,867,836,805,774,743,712,681,650,619,588,557,526,526,557,588,619,650,681,712,743,774,805,836,867,898,929,960,991,1023,991,959,927,895,863,831,799,767,735,703,671,639,607,575,543,543,575,607,639,671,703,735,767,799,831,863,895,927,959,991,1023],"symmetry":"vertical","water":[true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,true,true,true,true,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false],"width":32} diff --git a/replay-viewer/bc_replay.nim b/replay-viewer/bc_replay.nim index 3ddba36..7a46977 100644 --- a/replay-viewer/bc_replay.nim +++ b/replay-viewer/bc_replay.nim @@ -14,7 +14,7 @@ import std/json import bitworld/spriteprotocol import battlecode/[broadcast, build_stamp, render, replay, sim_types] -import battlecode/years/bc26/world +import battlecode/years/dispatch var runtimeLoaded = false @@ -67,9 +67,12 @@ proc renderCurrent() = else: 0 let sideAslot = doc.plan.sideAslots[min(gameIndex, doc.plan.sideAslots.high)] let ended = deriver.frame >= deriver.totalFrames - 1 - let chrome = chromeJson(doc, deriver.world, viewer, max(0, deriver.frame), - deriver.totalFrames, gameIndex, sideAslot, beats, gameChips, ended) - packet = renderer.buildPacket(deriver.world, gameIndex, sideAslot, chrome) + ## The replay header's `year` selects the sim, the atlas and the chrome + ## record set; nothing else in this file knows which year it is. + let chrome = sessionChromeJson(doc, deriver.session, viewer, + max(0, deriver.frame), deriver.totalFrames, gameIndex, sideAslot, beats, + gameChips, ended) + packet = renderer.buildSessionPacket(deriver.session, chrome) proc bcLoadReplay(data: ptr uint8, length: cint): cint {.exportc: "bc_load_replay", cdecl.} = @@ -84,7 +87,7 @@ proc bcLoadReplay(data: ptr uint8, length: cint): cint stampStage("build the deriver") deriver = newDeriver(doc) stampStage("load the sprite atlas") - renderer = newRenderer() + renderer = newRenderer(yearSpec(doc.year).atlas) stampStage("collect the scrubber beats") viewer = initViewerState() beats = beatsFor(doc, frameOfGameRound) diff --git a/src/battlecode/baselines.nim b/src/battlecode/baselines.nim index 10bf96a..60a53ff 100644 --- a/src/battlecode/baselines.nim +++ b/src/battlecode/baselines.nim @@ -1,39 +1,77 @@ -## The two published scripted baselines. +## The published scripted baselines, YEAR-AWARE. ## -## Both answer the doctrine request from a TABLE — no model, no network — and -## both replies go through the SAME `sheet.validate()` the LLM path uses, -## which is what makes `tests/test_baselines.nim`'s bounded-orders check -## meaningful and an LLM doctrine and a scripted one strictly comparable. +## `PLAYER_SCRIPTED=` selects BOTH the reply sheet and the chassis. The +## chassis is never a sheet field (D1): a submitted `chassis` key is recorded +## as an unknown field and never honoured, so the filler path below is the ONLY +## way `scaffold` or `examplefuncsplayer` is ever driven. +## +## Every reply goes through the SAME `sheet.validate()` the LLM path uses, +## which is what makes `tests/test_baselines.nim`'s and +## `tests/test_bc20_baselines.nim`'s bounded-orders checks meaningful and an +## LLM doctrine and a scripted one strictly comparable — which is why a reply +## carries only keys the LLM surface also has, and never `chassis`. import std/strutils import sheet +import years/dispatch type Baseline* = enum blAwu = "awu" blScaffold = "scaffold" + blBowlOfChowder = "bowl-of-chowder" + blExamplefuncsplayer = "examplefuncsplayer" + +proc defaultBaselineFor*(year: string): Baseline = + ## A seat that says nothing useful plays the year's STRONG published + ## doctrine, not the deliberately weak floor. + case yearIdOf(year) + of yBc20: blBowlOfChowder + of yBc26: blAwu + +proc baselineFor*(year, name: string): Baseline = + ## `PLAYER_SCRIPTED` values, per year. An unrecognised name takes the year's + ## default. + let key = name.strip().toLowerAscii() + case yearIdOf(year) + of yBc20: + case key + of "examplefuncsplayer", "scaffold", "example": blExamplefuncsplayer + else: blBowlOfChowder + of yBc26: + case key + of "scaffold", "examplefuncsplayer", "example": blScaffold + else: blAwu proc parseBaseline*(text: string): Baseline = - ## `PLAYER_SCRIPTED` values. Anything unrecognised is `awu`: a seat that - ## says nothing useful still plays the published default rather than the - ## deliberately weak floor. - case text.strip().toLowerAscii() - of "scaffold", "examplefuncsplayer", "example": blScaffold - else: blAwu + ## Year-free reading, kept for the bc26 call sites that predate the year + ## module boundary. bc26's own two names are the only ones it can return. + baselineFor("bc26", text) proc chassisFor*(kind: Baseline): Chassis = - ## The filler path's chassis selection, and the ONLY one there is. It is not - ## a sheet key: an LLM doctrine cannot reach it (see `sheet.KnownKeys`), so - ## `scaffold` is selectable only by `PLAYER_SCRIPTED=scaffold`. + ## The bc26 filler path's chassis selection, and the ONLY one there is. It + ## is not a sheet key: an LLM doctrine cannot reach it (see + ## `sheet.KnownKeys`), so `scaffold` is selectable only by + ## `PLAYER_SCRIPTED=scaffold`. case kind - of blAwu: chAwu of blScaffold: chScaffold + else: chAwu + +proc baselineChassis*(kind: Baseline): ChassisKind = + ## The same selection for bc20, whose chassis is a `ChassisKind` on the seat + ## record rather than a field on the doctrine. + case kind + of blExamplefuncsplayer: parseChassisKind("examplefuncsplayer") + else: parseChassisKind("bowl-of-chowder") proc baselineReply*(kind: Baseline): string = ## The exact JSON a scripted seat "answers" with. Emitted as text and then ## parsed by the same tolerant validator, so a scripted seat is ## indistinguishable from an LLM seat downstream — which is why it carries ## only keys the LLM surface also has, and never `chassis`. + ## + ## The bc20 replies are the all-defaults sheet: the passive-lattice build, + ## which is also the fallback sheet §Decisions prints verbatim. case kind of blAwu: """{"sheet":{},"notes":"default awu doctrine", @@ -41,7 +79,29 @@ proc baselineReply*(kind: Baseline): string = of blScaffold: """{"sheet":{},"notes":"scaffold baseline", "motto":"Forward."}""" + of blBowlOfChowder: + """{"sheet":{"opening":"passive_lattice","terraform_start_round":300, + "lattice_radius":6,"landscaper_count_curve":"steady", + "miner_count_curve":"steady","vaporator_budget":2, + "drone_role":"harass","net_gun_ring":2,"rush_trigger":0, + "wall_hq_round":250}, + "notes":"default bowl-of-chowder doctrine","motto":"Soup first."}""" + of blExamplefuncsplayer: + """{"sheet":{"opening":"passive_lattice","terraform_start_round":300, + "lattice_radius":6,"landscaper_count_curve":"steady", + "miner_count_curve":"steady","vaporator_budget":2, + "drone_role":"harass","net_gun_ring":2,"rush_trigger":0, + "wall_hq_round":250}, + "notes":"scaffold baseline (2020)","motto":"Forward."}""" + +proc baselineSheet*(year: string, kind: Baseline): Sheet = + result = parseReply(baselineReply(kind), year) + ## bc26 carries its chassis ON the doctrine, so the filler path sets it here + ## rather than through the sheet. bc20 carries it on the seat record instead. + if yearIdOf(year) == yBc26: + result.doctrine.chassis = chassisFor(kind) proc baselineSheet*(kind: Baseline): Sheet = - result = parseReply(baselineReply(kind)) - result.doctrine.chassis = chassisFor(kind) + baselineSheet("bc26", kind) + +proc baselineName*(kind: Baseline): string = $kind diff --git a/src/battlecode/broadcast.nim b/src/battlecode/broadcast.nim index 9655e97..0c4f57e 100644 --- a/src/battlecode/broadcast.nim +++ b/src/battlecode/broadcast.nim @@ -11,7 +11,11 @@ import std/[json, strutils] import sim_types, sheet, match, replay +import years/dispatch import years/bc26/[constants, rules, world] +from years/bc20/world as w20 import nil +from years/bc20/constants as c20 import nil +from years/bc20/chassis/signals as sig20 import nil const PlaybackSpeeds* = [1, 2, 3, 4, 8, 16] @@ -124,6 +128,14 @@ proc beatsFor*(doc: ReplayDoc, frameOfGameRound: proc (g, r: int): int): JsonNod of "game_start": "game" of "game_end": "end" of "game_abandoned": "end" + of "flood_stage": "flood" + of "first_build": "build" + of "wall_closed": "wall" + of "rush_launched": "rush" + of "drone_water_drop": "drop" + of "hq_buried": "bury" + of "hq_drowned": "drown" + of "doctrine_received", "doctrine_fallback": "doctrine" else: "" if kind.len == 0: continue var label = "" @@ -145,6 +157,30 @@ proc beatsFor*(doc: ReplayDoc, frameOfGameRound: proc (g, r: int): int): JsonNod e.fields{"end_reason"}.getStr().replace("_", " ") & ")" of "game_abandoned": label = "Game " & $(e.game + 1) & " abandoned at the wall clock" + of "flood_stage": + label = "Water reaches elevation " & $e.fields{"level"}.getInt() & + " — game " & $(e.game + 1) & ", round " & $e.round + of "first_build": + label = e.fields{"alias"}.getStr() & " builds its first " & + e.fields{"unit"}.getStr().replace("_", " ") & " — game " & + $(e.game + 1) & ", round " & $e.round + of "wall_closed": + label = e.fields{"alias"}.getStr() & " closes its HQ wall at elevation " & + $e.fields{"min_ring_elevation"}.getInt() & " — game " & + $(e.game + 1) & ", round " & $e.round + of "rush_launched": + label = e.fields{"alias"}.getStr() & " launches the rush — game " & + $(e.game + 1) & ", round " & $e.round + of "drone_water_drop": + label = e.fields{"alias"}.getStr() & " drops a " & + e.fields{"victim_unit"}.getStr().replace("_", " ") & " in the water — game " & + $(e.game + 1) & ", round " & $e.round + of "hq_buried": + label = "HQ BURIED — " & e.fields{"alias"}.getStr() & ", game " & + $(e.game + 1) & ", round " & $e.round + of "hq_drowned": + label = "HQ DROWNED — " & e.fields{"alias"}.getStr() & ", game " & + $(e.game + 1) & ", round " & $e.round else: discard result.add(%*{ "t": frameOfGameRound(e.game, max(1, e.round)), @@ -170,6 +206,98 @@ proc doctrinesJson*(doc: ReplayDoc): JsonNode = "motto": doc.seats[slot].sheet.motto }) +proc bc20Flood(w: w20.World): JsonNode = + ## `#bc20-flood`: the water level to 2 dp, the flood ring gauge, and the + ## HQ-elevation-versus-water reading the panel flashes red on. + var hqElev = [-1, -1] + for t in 0 .. 1: + let id = w.hqId[t] + if id >= 0 and id in w.robotsById: + hqElev[t] = w20.getDirt(w, w.robotsById[id].loc) + %*{ + "water": float(w.waterLevel), + "flooded": w.floodedCount, + "tiles": w.width * w.height, + "hq_elevation": hqElev, + "global_pollution": w.globalPollution + } + +proc bc20Soup(w: w20.World, sideAslot: int): JsonNode = + result = newJArray() + for slot in 0 .. 1: + let t = if slot == sideAslot: 0 else: 1 + result.add(%*{ + "pool": w.stats.soup[t], + "mined": w.stats.soupMined[t], + "refined": w.stats.soupRefined[t] + }) + +proc bc20Units(w: w20.World, sideAslot: int): JsonNode = + ## Per clan, counts by type plus the HQ's dirt load as `dirt/50`. + result = newJArray() + for slot in 0 .. 1: + let t = if slot == sideAslot: 0 else: 1 + var hqDirt = 0 + let id = w.hqId[t] + if id >= 0 and id in w.robotsById: + hqDirt = w.robotsById[id].dirtCarrying + result.add(%*{ + "miner": w.typeCount[t][c20.rtMiner], + "landscaper": w.typeCount[t][c20.rtLandscaper], + "drone": w.typeCount[t][c20.rtDeliveryDrone], + "vaporator": w.typeCount[t][c20.rtVaporator], + "net_gun": w.typeCount[t][c20.rtNetGun], + "design_school": w.typeCount[t][c20.rtDesignSchool], + "fulfillment_center": w.typeCount[t][c20.rtFulfillmentCenter], + "refinery": w.typeCount[t][c20.rtRefinery], + "hq_dirt": hqDirt, + "hq_dirt_limit": c20.RobotSpecs[c20.rtHq].dirtLimit, + "dirt_moved": w.stats.dirtMoved[t], + "drone_drops": w.stats.droneWaterDrops[t], + "net_gun_kills": w.stats.netGunKills[t] + }) + +proc bc20Chain(w: w20.World, sideAslot: int): JsonNode = + ## `#bc20-chain`, the endcard's blockchain panel. NOTHING about the chain is + ## stored in the replay: the wasm sim re-derives every block, and this reads + ## the re-derived blocks. Messages whose first int is not `SIGNAL_KEY` are + ## shown as raw ints, which is what makes an opponent's private traffic look + ## private without hiding that it happened. + var minted = [0, 0] + var spent = [0, 0] + var topFee = [0, 0] + var topRound = [-1, -1] + var recent = newJArray() + for roundIndex, blk in w.blockchain: + for tx in blk: + let slot = if tx.team == 0: sideAslot else: 1 - sideAslot + minted[slot] += 1 + spent[slot] += tx.cost + if tx.cost > topFee[slot]: + topFee[slot] = tx.cost + topRound[slot] = roundIndex + 1 + var words = "" + if tx.message[0] == sig20.SignalKey: + words = sig20.signalName(tx.message[2]) + else: + for i, v in tx.message: + if i > 0: words.add("_") + words.add($v) + recent.add(%*{"alias": aliasFor(slot), "round": roundIndex + 1, + "fee": tx.cost, "words": words}) + ## The LAST FIVE minted messages, decoded to plain words. + var tail = newJArray() + let start = max(0, recent.len - 5) + for i in start ..< recent.len: + tail.add(recent[i]) + %*{ + "minted": minted, "soup_spent": spent, + "top_fee": topFee, "top_fee_round": topRound, + "recent": tail + } + +proc doctrineWords(doc: ReplayDoc): JsonNode = doctrinesJson(doc) + proc chromeJson*( doc: ReplayDoc, w: World, view: ViewerState, frame, totalFrames, gameIndex, sideAslot: int, @@ -215,3 +343,65 @@ proc chromeJson*( "result": doc.result } $node + +proc bc20ChromeJson*( + doc: ReplayDoc, w: w20.World, view: ViewerState, + frame, totalFrames, gameIndex, sideAslot: int, + beats: JsonNode, gameChips: JsonNode, ended: bool +): string = + ## One frame of bc20 chrome. `t` / `st` / `mx` / `mt` are the GENERIC + ## timeline keys `chrome_common.js` reads, unchanged, so the clock, the + ## transport and the scrubber are driven by the starter's own code; the + ## `bc20_*` keys are what the APPENDED bc20 game block draws. + let phase = if ended: "gameover" else: "playing" + let points = w20.gamePoints(w) + var node = %*{ + "t": frame, + "st": 0, + "mx": max(1, totalFrames - 1), + "mt": 0, + "sp": view.speed, + "pl": view.playing, + "lp": view.loop, + "sk": view.skipLulls, + "ff": false, + "en": true, + "ph": phase, + "lob": 0, + "pov": -1, + "nim": GameVersion, + "year": "bc20", + "beats": beats, + "game": gameIndex + 1, + "games": doc.games.len, + "map": doc.plan.maps[min(gameIndex, doc.plan.maps.high)], + "round": w.currentRound, + "rounds": doc.plan.maxRounds, + "aliases": [AliasA, AliasB], + "names": [doc.names[0], doc.names[1]], + "sides": [(if sideAslot == 0: "A" else: "B"), + (if sideAslot == 0: "B" else: "A")], + "points": [points[(if sideAslot == 0: 0 else: 1)], + points[(if sideAslot == 0: 1 else: 0)]], + "bc20_flood": bc20Flood(w), + "bc20_soup": bc20Soup(w, sideAslot), + "bc20_units": bc20Units(w, sideAslot), + "bc20_chain": bc20Chain(w, sideAslot), + "gamechips": gameChips, + "doctrines": doctrineWords(doc), + "result": doc.result + } + $node + +proc sessionChromeJson*( + doc: ReplayDoc, s: Session, view: ViewerState, + frame, totalFrames, gameIndex, sideAslot: int, + beats: JsonNode, gameChips: JsonNode, ended: bool +): string = + case s.year + of yBc26: + chromeJson(doc, s.w26, view, frame, totalFrames, gameIndex, sideAslot, + beats, gameChips, ended) + of yBc20: + bc20ChromeJson(doc, s.w20, view, frame, totalFrames, gameIndex, sideAslot, + beats, gameChips, ended) diff --git a/src/battlecode/decide.nim b/src/battlecode/decide.nim index fa639e1..72f4d17 100644 --- a/src/battlecode/decide.nim +++ b/src/battlecode/decide.nim @@ -14,7 +14,7 @@ import std/[json, monotimes, strutils, times] import curly import sim_types, sheet, baselines, llm, match -import years/bc26/maps +import years/dispatch type SeatPolicy* = object @@ -22,6 +22,10 @@ type ## — or never registers at all — is `awu`. isLlm*: bool prompt*: string + scripted*: string + ## The raw `PLAYER_SCRIPTED` value. Resolved to a `Baseline` PER YEAR at + ## episode time, because `bowl-of-chowder` means nothing to bc26 and + ## `awu` means nothing to bc20. baseline*: Baseline label*: string registered*: bool @@ -40,6 +44,24 @@ type policyKind*: array[2, string] events*: seq[MatchEvent] +proc baselineForSeat*(year: string, seat: SeatPolicy): Baseline = + if seat.scripted.len > 0: baselineFor(year, seat.scripted) + else: defaultBaselineFor(year) + +proc chassisForSeat*(year: string, seat: SeatPolicy): ChassisKind = + ## D1: the chassis is fixed by the OPERATOR. A scripted seat drives the + ## chassis its `PLAYER_SCRIPTED` names; an LLM seat drives the fixed champion + ## chassis, which for bc20 is `bowl-of-chowder`. + if seat.isLlm: parseChassisKind("bowl-of-chowder") + else: baselineChassis(baselineForSeat(year, seat)) + +proc chassisNameFor*(year: string, seat: SeatPolicy, sheet: Sheet): string = + case yearIdOf(year) + of yBc20: + (if seat.isLlm: "bowl-of-chowder" + else: baselineName(baselineForSeat(year, seat))) + of yBc26: $sheet.doctrine.chassis + const SystemPreamble* = """ You command a clan of robot rats in Battlecode 2026, "Uneasy Alliances": a two-clan grid war on a symmetric map, 2000 rounds a game, best of three. @@ -97,27 +119,83 @@ Your clan is driven by the `awu` chassis. That is not yours to choose: there is no `chassis` knob, and a reply that sends one has it ignored. """ +const Bc20Preamble* = """ +You command a clan in Battlecode 2020 "Soup": a two-clan grid war on a +symmetric map where THE WATER RISES EVERY ROUND, 1500 rounds a game, best of +three. + +You do not move a single robot. Before the war you write ONE DOCTRINE — a JSON +sheet of ten named knobs — and a deterministic simulation then plays the whole +match from it while you watch. + +THE CLOCK IS THE WATER +- The water level rises on a fixed curve and floods outward ONE RING PER ROUND + from every already-flooded tile whose neighbour sits below the level. + Anything that is not a delivery drone dies on a flooding tile. +- Your HQ starts at a low elevation and CANNOT BE RAISED: dirt dropped on a + building buries it. The only thing that keeps an HQ dry is a ring of eight + adjacent tiles the water can never cross. An HQ that is never walled drowns + on a schedule. + +THE WORLD +- MINERS (70 soup) mine SOUP (7 per action, carry 100) and deposit it at the HQ + or a REFINERY (200), which refines up to 20 per round into the team pool. +- DESIGN SCHOOLS (150) build LANDSCAPERS (150), which dig and dump dirt: raise + the ground into a lattice, wall the HQ in, or bury the enemy HQ under FIFTY + dirt. A landscaper carries 25. +- FULFILLMENT CENTERS (150) build DELIVERY DRONES (150), which pick up any unit + within r^2 <= 3 — including enemy landscapers — and drop them in the water. +- VAPORATORS (500) print 2 soup a round and scrub pollution. NET GUNS (250) + shoot drones within r^2 <= 15; so does the HQ. +- Pollution slows every action and shrinks every sensor. Cows pollute heavily. +- The only global channel is a blockchain: seven ints a message, seven messages + a round, paid for in soup, and BOTH TEAMS READ EVERY BLOCK. + +HOW A GAME ENDS +Round 1499, or when an HQ is buried or drowned. The ladder, first hit wins: +HQ destroyed; more robots alive (buildings included); greater net worth; more +transactions minted; highest living robot id; coin flip. + points = int(60 * HQ-survival share + 25 * unit share + 15 * net-worth share) +Winning a game is worth 100 and points are worth at most 100, so the game bonus +dominates: lose your HQ, lose the game. + +YOUR REPLY +Reply with ONE JSON object and NOTHING else. Your reply must begin with '{'. +{"sheet": {...knobs...}, "notes": "<=280 chars", "motto": "<=48 chars"} + +THE KNOBS (unknown key, wrong type or out-of-range value = that field's +default; you cannot forfeit by answering badly, only by answering weakly): + opening "rush" | "lattice" | "passive_lattice" | "turtle" + default "passive_lattice" + terraform_start_round 1..1500 default 300 + lattice_radius 2..12 default 6 + landscaper_count_curve "lean" | "steady" | "swarm" default "steady" + miner_count_curve "lean" | "steady" | "swarm" default "steady" + vaporator_budget 0..6 default 2 + drone_role "harass" | "wall" | "buster" | "carry_landscapers" + default "harass" + net_gun_ring 0..6 default 2 + rush_trigger 0..1500 (0 = never) default 0 + wall_hq_round 0..1500 (0 = never) default 250 +""" + +proc preambleFor*(year: string): string = + case yearIdOf(year) + of yBc20: Bc20Preamble + of yBc26: SystemPreamble + proc briefFor*( config: GameConfig, plan: MatchPlan, slot: int ): string = ## Everything this seat may legitimately know: its own alias and side, all - ## the map cards, the seed, both weight sets, the deadlines. NOT in here, + ## the map cards, the seed, the scoring weights, the deadlines. NOT in here, ## ever: the opponent's doctrine, sheet, notes, motto, real player name or ## fallback status. Sealed and simultaneous. var games = newJArray() for g in 0 ..< plan.maps.len: - let spec = loadMap(plan.maps[g]) - games.add(%*{ - "map": spec.name, - "width": spec.width, - "height": spec.height, - "symmetry": ($spec.symmetry).replace("sym", "").toLowerAscii(), - "cheese_mines": spec.cheeseMines.len, - "cats": spec.catWaypointIds.len, - "rounds": plan.maxRounds, - "you_are": (if plan.sideAslots[g] == slot: "A" else: "B") - }) - $(%*{ + games.add(mapCardFor(plan.year, plan.maps[g], slot, plan.sideAslots[g], + plan.maxRounds)) + var payload = %*{ "protocol": ProtocolId, "game_version": GameVersion, "year": plan.year, @@ -127,19 +205,30 @@ proc briefFor*( "team": (if plan.sideAslots[0] == slot: "A" else: "B"), "seed": plan.seed, "games": games, - "scoring": { + "budget": { + "attempt1_ms": config.attempt1Ms, + "retry_ms": config.retryMs, + "one_shot": true + } + } + case yearIdOf(plan.year) + of yBc20: + payload["flood_table"] = floodTableJson() + payload["scoring"] = %*{ + "weights": {"hq_survival": 60, "unit_share": 25, "net_worth_share": 15}, + "win_bonus_per_game": 100, + "games": plan.maps.len, + "note": "shares are float32; points truncate to an integer" + } + of yBc26: + payload["scoring"] = %*{ "cooperation": {"cat_damage": 0.5, "kings": 0.3, "cheese": 0.2}, "backstab": {"cat_damage": 0.3, "kings": 0.5, "cheese": 0.2}, "win_bonus_per_game": 100, "games": plan.maps.len, "note": "shares are float32; points truncate to an integer" - }, - "budget": { - "attempt1_ms": config.attempt1Ms, - "retry_ms": config.retryMs, - "one_shot": true } - }) + $payload proc decide*( config: GameConfig, plan: MatchPlan, seats: array[2, SeatPolicy] @@ -152,7 +241,8 @@ proc decide*( var open: seq[int] for slot in 0 .. 1: - result.sheets[slot] = baselineSheet(seats[slot].baseline) + result.sheets[slot] = baselineSheet(config.year, + baselineForSeat(config.year, seats[slot])) result.policyKind[slot] = if seats[slot].isLlm: "llm" else: "scripted" if seats[slot].isLlm and not client.disabled: open.add(slot) @@ -199,7 +289,7 @@ proc decide*( user.add("\n\nYour previous reply was not usable. Reply with ONLY " & "the JSON object described above, starting with '{'.") let request = client.requestFor( - SystemPreamble, userMessage(seats[slot].prompt, user)) + preambleFor(config.year), userMessage(seats[slot].prompt, user)) batch.post(request.url, request.headers, request.body, $slot) let batchStart = getMonoTime() ## ONE parallel batch. curly hands the deadline to CURLOPT_TIMEOUT, whose @@ -213,7 +303,7 @@ proc decide*( try: let text = client.textOf(responses[position].response, responses[position].error, batch[position].url) - result.sheets[slot] = parseReply(text) + result.sheets[slot] = parseReply(text, config.year) result.decisionMs[slot] = latency result.fallback[slot] = "" ## `chassis` is not a knob (sheet.KnownKeys). A reply that still sends @@ -251,7 +341,8 @@ proc decide*( break for slot in open: - result.sheets[slot] = baselineSheet(seats[slot].baseline) + result.sheets[slot] = baselineSheet(config.year, + baselineForSeat(config.year, seats[slot])) let cause = if client.disabled or client.transport == ltNone: "no_credentials" elif client.throttled: "throttled" diff --git a/src/battlecode/match.nim b/src/battlecode/match.nim index ab041f3..70a72fe 100644 --- a/src/battlecode/match.nim +++ b/src/battlecode/match.nim @@ -1,16 +1,21 @@ ## The match: three games, alternating sides, one sealed doctrine per seat. ## +## YEAR-NEUTRAL. The games are played through `years/dispatch.nim`, which is +## the only module that names a year's world; the beats collected below are +## mapped from the year's own event stream by kind, so a new year adds event +## kinds and nothing here changes shape. +## ## Everything wall-clock-driven is recorded as ONE load-bearing record and ## applied by the SAME proc on record and on playback (`abandonAfter`), which ## is the particle-worlds 2026-08-26 scar: a `deadline` stop derived from the ## recorder's clock and re-derived from the viewer's clock is not the same ## match. -import std/[json, monotimes, strutils, times] +import std/[json, monotimes, times] import sim_types, sheet -import years/bc26/[constants, maps, rules, world] +import years/dispatch -export rules, maps +export dispatch type MatchEvent* = object @@ -29,6 +34,9 @@ type maps*: seq[string] sideAslots*: seq[int] sheets*: array[2, Sheet] + chassis*: array[2, ChassisKind] + ## Which chassis each SEAT drives. Never a sheet field (D1): it comes + ## from `PLAYER_SCRIPTED`, or is the fixed champion chassis. maxRounds*: int ## The wall-clock stop, RECORDED: round `abandonAfter[g]` is the last ## round game `g` played. -1 means the game ran to its own end. @@ -61,38 +69,89 @@ proc buildPlan*(config: GameConfig, sheets: array[2, Sheet], result.year = config.year result.maxRounds = config.maxRounds result.sheets = sheets + result.chassis = [ckBowlOfChowder, ckBowlOfChowder] let count = max(1, config.gamesPerMatch) - result.maps = drawMaps(config.pool, seed, count) + result.maps = drawMapsFor(config.year, config.pool, seed, count) for g in 0 ..< result.maps.len: - result.sideAslots.add(sideAslotFor(seed, g)) + result.sideAslots.add(sideAslotFor(config.year, seed, g)) result.abandonAfter.add(-1) proc winsNeeded*(games: int): int = games div 2 + 1 -proc collectGameEvents(w: World, gameIndex: int, plan: MatchPlan, - events: var seq[MatchEvent]) = - ## The engine's own event stream, filtered to the beats the chrome draws. +proc aliasOfTeam(plan: MatchPlan, gameIndex, teamOrdinal: int): string = + ## `teamOrdinal` is 0 for A and 1 for B; which SEAT that is alternates per + ## game. + let slot = if teamOrdinal == 0: plan.sideAslots[gameIndex] + else: 1 - plan.sideAslots[gameIndex] + aliasFor(slot) + +proc collectGameEvents( + raw: seq[tuple[round: int, kind: string, a, b, c: int, s: string]], + gameIndex: int, plan: MatchPlan, events: var seq[MatchEvent] +) = + ## The year's own event stream, filtered to the beats the chrome draws. ## Everything else stays in the sim; the replay re-derives it. - for e in w.events: + var firstBuildSeen: seq[string] + for e in raw: case e.kind of "backstab": - let slot = if Team(e.a) == teamA: plan.sideAslots[gameIndex] - else: 1 - plan.sideAslots[gameIndex] events.add(ev("backstab", game = gameIndex, round = e.round, - fields = %*{"by_alias": aliasFor(slot), "by_slot": slot, + fields = %*{"by_alias": plan.aliasOfTeam(gameIndex, e.a), + "by_slot": (if e.a == 0: plan.sideAslots[gameIndex] + else: 1 - plan.sideAslots[gameIndex]), "trigger": e.s})) of "king_built": - let slot = if Team(e.b) == teamA: plan.sideAslots[gameIndex] - else: 1 - plan.sideAslots[gameIndex] events.add(ev("king_built", game = gameIndex, round = e.round, - fields = %*{"alias": aliasFor(slot), "kings_now": e.c})) + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.b), + "kings_now": e.c})) of "cat_fed": - let slot = if Team(e.c) == teamA: plan.sideAslots[gameIndex] - else: 1 - plan.sideAslots[gameIndex] events.add(ev("cat_fed", game = gameIndex, round = e.round, - fields = %*{"alias": aliasFor(slot)})) + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.c)})) + of "flood_stage": + events.add(ev("flood_stage", game = gameIndex, round = e.c, + fields = %*{"level": e.a, "flooded_tiles": e.b})) + of "first_build": + let key = $e.a & ":" & $e.b + if key in firstBuildSeen: continue + firstBuildSeen.add(key) + events.add(ev("first_build", game = gameIndex, round = e.c, + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.a), + "unit": Bc20UnitNames[e.b]})) + of "wall_closed": + events.add(ev("wall_closed", game = gameIndex, round = e.c, + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.a), + "min_ring_elevation": e.b})) + of "rush_launched": + events.add(ev("rush_launched", game = gameIndex, round = e.c, + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.a), + "units": e.b})) + of "drone_water_drop": + events.add(ev("drone_water_drop", game = gameIndex, round = e.round, + fields = %*{"alias": plan.aliasOfTeam(gameIndex, e.b), + "victim_alias": plan.aliasOfTeam(gameIndex, 1 - e.b), + "victim_unit": Bc20UnitNames[e.c]})) else: discard +proc bc20HqEvents(outcome: GameOutcome, gameIndex: int, + events: var seq[MatchEvent]) = + ## `hq_buried` / `hq_drowned` are CHAPTER MARKERS, derived from the recorded + ## per-game statistics rather than from a sim event, so the same two facts + ## drive the endcard, the scrubber and `results.games[]`. + if outcome.stats.isNil: return + if not outcome.stats.hasKey("hq_lost_round"): return + for slot in 0 .. 1: + let round = outcome.stats["hq_lost_round"][slot].getInt(-1) + if round < 0: continue + let cause = outcome.stats["hq_lost_cause"][slot].getStr("none") + if cause == "buried": + events.add(ev("hq_buried", game = gameIndex, round = round, + fields = %*{"alias": aliasFor(slot), "by_alias": aliasFor(1 - slot), + "dirt": 50})) + elif cause == "drowned": + events.add(ev("hq_drowned", game = gameIndex, round = round, + fields = %*{"alias": aliasFor(slot), + "water_level": outcome.stats{"water_level_end"}.getFloat()})) + proc playMatch*(config: GameConfig, plan: var MatchPlan, events: var seq[MatchEvent]): (seq[GameOutcome], EpisodeReason) = ## Plays the planned games in order, stopping early once a seat has taken @@ -113,15 +172,16 @@ proc playMatch*(config: GameConfig, plan: var MatchPlan, break let remaining = (matchBudget - elapsed).inSeconds.int let perGame = max(1, min(config.perGameBudgetSeconds, remaining)) - let spec = loadMap(plan.maps[g]) + let card = mapCardFor(config.year, plan.maps[g], plan.sideAslots[g], + plan.sideAslots[g], plan.maxRounds) events.add(ev("game_start", game = g, round = 0, fields = %*{ "map": plan.maps[g], - "width": spec.width, "height": spec.height, + "width": card{"width"}.getInt(), "height": card{"height"}.getInt(), "sides": [aliasFor(plan.sideAslots[g]), aliasFor(1 - plan.sideAslots[g])] })) - let (w, outcome) = playGame(spec, plan.sheets, g, plan.sideAslots[g], - plan.maxRounds, perGame) - collectGameEvents(w, g, plan, events) + let (outcome, raw) = playGameFor(config.year, plan.maps[g], plan.sheets, + plan.chassis, g, plan.sideAslots[g], plan.maxRounds, perGame) + collectGameEvents(raw, g, plan, events) if outcome.aborted: ## The unfinished game is DISCARDED, and the round it stopped at is ## recorded so the viewer's re-derivation stops in the same place. @@ -130,24 +190,28 @@ proc playMatch*(config: GameConfig, plan: var MatchPlan, events.add(ev("game_abandoned", game = g, round = outcome.roundsPlayed, fields = %*{"map": plan.maps[g]})) break + bc20HqEvents(outcome, g, events) outcomes.add(outcome) if outcome.winnerSlot >= 0: wins[outcome.winnerSlot] += 1 + var endFields = %*{ + "winner_alias": (if outcome.winnerSlot >= 0: + aliasFor(outcome.winnerSlot) else: "nobody"), + "winner_slot": outcome.winnerSlot, + "end_reason": outcome.endReason, + "points": [outcome.points[0], outcome.points[1]] + } + if outcome.stats != nil and outcome.stats.hasKey("cooperation_at_end"): + endFields["cooperation_at_end"] = outcome.stats["cooperation_at_end"] events.add(ev("game_end", game = g, round = outcome.roundsPlayed, - fields = %*{ - "winner_alias": (if outcome.winnerSlot >= 0: - aliasFor(outcome.winnerSlot) else: "nobody"), - "winner_slot": outcome.winnerSlot, - "end_reason": $outcome.endReason, - "points": [outcome.points[0], outcome.points[1]], - "cooperation_at_end": outcome.cooperationAtEnd - })) + fields = endFields)) (outcomes, reason) proc scoresFor*(games: seq[GameOutcome]): array[2, float] = ## `100 * gamesWon + mean(gamePoints over games actually played)`. ## Higher is better; the 100-per-game win bonus dominates, which is what - ## makes "losing every rat king loses the game outright" true. + ## makes "lose your HQ, lose the game" true in the ranking as well as in the + ## rules. if games.len == 0: return [0.0, 0.0] var wins: array[2, int] diff --git a/src/battlecode/render.nim b/src/battlecode/render.nim index ef4f92b..478db54 100644 --- a/src/battlecode/render.nim +++ b/src/battlecode/render.nim @@ -12,11 +12,15 @@ ## round. Re-sending the board every round would be a megabyte a frame; the ## diff is a few dozen bytes. -import std/[json, os, sequtils, sets, tables] +import std/[json, math, os, sequtils, sets, tables] import pixie import bitworld/spriteprotocol import sheet +import years/dispatch import years/bc26/[constants, maps, world] +from years/bc20/world as w20 import nil +from years/bc20/flood as f20 import nil +from years/bc20/constants as c20 import nil const TileSize* = 16 @@ -35,11 +39,24 @@ const TrapAObjectBase = 24000 TrapBObjectBase = 32000 RobotObjectBase = 40000 + SoupObjectBase = 48000 FloorColor = rgba(38, 32, 28, 255) WallColor = rgba(88, 74, 60, 255) GridColor = rgba(46, 39, 34, 255) + ## bc20's board is a HEIGHTMAP under a rising sea, so the terrain sprite is + ## re-cut whenever the water crosses an integer level and the nine-step + ## elevation ramp is keyed to the CURRENT water line — the lattice then reads + ## as terrain rising out of the sea rather than as a static heightmap. + Bc20Ramp = [ + rgba(24, 30, 38, 255), rgba(34, 42, 46, 255), rgba(48, 56, 50, 255), + rgba(64, 70, 54, 255), rgba(84, 84, 58, 255), rgba(106, 98, 64, 255), + rgba(130, 114, 78, 255), rgba(158, 136, 98, 255), rgba(190, 166, 126, 255) + ] + Bc20WaterColor = rgba(28, 62, 104, 255) + Bc20DeepWaterColor = rgba(16, 38, 70, 255) + type Atlas = ref object image: Image @@ -54,31 +71,39 @@ type prevDirt: seq[bool] prevCheese: seq[bool] prevTrap: array[2, seq[int]] + prevSoup: seq[int] + prevRobotSprite: Table[int, int] terrainGame: int + terrainStage: int + atlasName: string -proc loadAtlas(): Atlas = +proc loadAtlas(name: string): Atlas = ## The atlas rides in the wasm bundle's preloaded `data/`. When that package ## has not mounted, say so plainly: the alternative is whatever error the ## image decoder happens to raise on a missing file, several frames from ## the real cause. let root = dataRoot() - for asset in ["atlas.png", "atlas.json"]: + for asset in [name & ".png", name & ".json"]: if not fileExists(root / asset): raise newException(IOError, "the sprite atlas is missing: no " & (root / asset) & " (in the wasm bundle this means the preloaded data package did " & "not mount)") - result = Atlas(image: readImage(root / "atlas.png"), + result = Atlas(image: readImage(root / (name & ".png")), cells: initTable[string, tuple[x, y, w, h: int]]()) - let doc = parseJson(readFile(root / "atlas.json")) + let doc = parseJson(readFile(root / (name & ".json"))) for name, cell in doc["sprites"]: result.cells[name] = (cell["x"].getInt(), cell["y"].getInt(), cell["w"].getInt(), cell["h"].getInt()) -proc newRenderer*(): Renderer = - Renderer(atlas: loadAtlas(), spriteIds: initTable[string, int](), +proc newRenderer*(atlasName = "atlas"): Renderer = + ## The atlas is per YEAR (`YearSpec.atlas`): `atlas` for bc26, `atlas_bc20` + ## for the 2020 sprite set cut from the official client (credited in NOTICE). + Renderer(atlas: loadAtlas(atlasName), spriteIds: initTable[string, int](), nextSpriteId: AtlasSpriteBase, sentSprites: initHashSet[int](), - liveObjects: initHashSet[int](), terrainGame: -1) + liveObjects: initHashSet[int](), + prevRobotSprite: initTable[int, int](), + terrainGame: -1, terrainStage: -1, atlasName: atlasName) proc straightPixels(image: Image): seq[uint8] = ## `broadcast_core.js` blends sprites with straight (non-premultiplied) @@ -256,3 +281,115 @@ proc buildPacket*(r: Renderer, w: World, gameIndex, sideAslot: int, ## never drawn; `broadcast_core.js` routes it straight to `onText`. packet.addSprite(BroadcastChromeSpriteId, 1, 1, [0'u8, 0, 0, 0], chrome) packet + +# --------------------------------------------------------------------------- +# bc20 — a heightmap under a rising sea +# --------------------------------------------------------------------------- + +proc bc20SpriteName(r: w20.Robot, sideAslot: int): string = + ## Palette follows the ENGINE SIDE, not the seat: red = side A, blue = side + ## B, exactly as the 2020 client draws it. Because sides alternate every game + ## the scorebug plate keeps the alias constant and recolours its swatch. + let tint = if r.team == w20.teamA: "_red" else: "_blue" + case r.kind + of c20.rtHq: "hq" & tint + of c20.rtMiner: "miner" & tint + of c20.rtLandscaper: "landscaper" & tint + of c20.rtDeliveryDrone: + (if r.holdingUnit: "drone" & tint & "_carry" else: "drone" & tint) + of c20.rtRefinery: "refinery" & tint + of c20.rtVaporator: "vaporator" & tint + of c20.rtDesignSchool: "design_school" & tint + of c20.rtFulfillmentCenter: "fulfillment_center" & tint + of c20.rtNetGun: "net_gun" & tint + of c20.rtCow: "cow" + +proc renderBc20Terrain(r: Renderer, w: w20.World): Image = + ## A nine-step elevation ramp keyed to the CURRENT water level, plus a water + ## overlay on flooded tiles. Re-cut only when the water crosses an integer + ## level, so a 1499-round game sends seven terrain sprites, not 1499. + result = newImage(w.width * TileSize, w.height * TileSize) + result.fill(FloorColor) + let ctx = newContext(result) + let level = w.waterLevel + for y in 0 ..< w.height: + for x in 0 ..< w.width: + let l = w20.loc(x, y) + let px = x * TileSize + ## The board's y axis grows NORTH; the canvas grows down. + let py = (w.height - 1 - y) * TileSize + let elevation = w20.getDirt(w, l) + var colour: ColorRGBA + if w20.isFlooded(w, l): + colour = if float32(elevation) < level - 2.0'f32: + Bc20DeepWaterColor else: Bc20WaterColor + else: + let step = clamp(int(floor(float(elevation) - float(level))) + 2, + 0, Bc20Ramp.high) + colour = Bc20Ramp[step] + ctx.fillStyle = colour + ctx.fillRect(rect(float32(px), float32(py), + float32(TileSize), float32(TileSize))) + +proc buildBc20Packet(r: Renderer, w: w20.World, gameIndex, sideAslot: int, + chrome: string): seq[uint8] = + var packet: seq[uint8] + let n = w.width * w.height + let stage = f20.floodStageFor(w.waterLevel) + let newGame = r.terrainGame != gameIndex + + if newGame: + r.terrainGame = gameIndex + r.terrainStage = -1 + r.liveObjects.clear() + r.prevRobotSprite.clear() + packet.addClearObjects() + packet.addLayer(MapLayerId, MapLayerKind, ZoomableFlag) + packet.addViewport(MapLayerId, w.width * TileSize, w.height * TileSize) + r.prevSoup = newSeq[int](n) + for i in 0 ..< n: r.prevSoup[i] = -1 + + if stage != r.terrainStage: + r.terrainStage = stage + let terrain = r.renderBc20Terrain(w) + packet.addSprite(TerrainSpriteId, terrain.width, terrain.height, + straightPixels(terrain), "terrain") + packet.addObject(1, 0, 0, -32768, MapLayerId, TerrainSpriteId) + + let soupSprite = r.spriteId(packet, "soup") + for i in 0 ..< n: + let here = if w.soup[i] > 0: 1 else: 0 + if here == r.prevSoup[i]: continue + r.prevSoup[i] = here + let l = w20.indexToLoc(w, i) + if here == 1: + r.addObj(packet, SoupObjectBase + i, l.x * TileSize, + (w.height - 1 - l.y) * TileSize, 2, soupSprite) + else: + r.dropObj(packet, SoupObjectBase + i) + + ## Robots. Object ids are stable for a robot's whole life, so the client's + ## motion interpolation can glide it between rounds instead of teleporting. + var seen = initHashSet[int]() + for id, robot in w.robotsById: + if robot.blocked: continue ## riding inside a drone + let objectId = RobotObjectBase + (id mod 20000) + seen.incl(objectId) + let sprite = r.spriteId(packet, bc20SpriteName(robot, sideAslot)) + r.addObj(packet, objectId, robot.loc.x * TileSize, + (w.height - 1 - robot.loc.y) * TileSize, 5, sprite) + + for objectId in toSeq(r.liveObjects): + if objectId >= RobotObjectBase and objectId < SoupObjectBase and + objectId notin seen: + r.dropObj(packet, objectId) + + packet.addSprite(BroadcastChromeSpriteId, 1, 1, [0'u8, 0, 0, 0], chrome) + packet + +proc buildSessionPacket*(r: Renderer, s: Session, chrome: string): seq[uint8] = + ## The ONE place the renderer branches on the year. `Session` is an object + ## variant, so the compiler checks that a new year gets an arm here. + case s.year + of yBc26: r.buildPacket(s.w26, s.gameIndex, s.sideAslot, chrome) + of yBc20: r.buildBc20Packet(s.w20, s.gameIndex, s.sideAslot, chrome) diff --git a/src/battlecode/replay.nim b/src/battlecode/replay.nim index d89796c..a3ba7f8 100644 --- a/src/battlecode/replay.nim +++ b/src/battlecode/replay.nim @@ -11,7 +11,7 @@ import std/[json, strutils] import crunchy/sha256 import sim_types, sheet, match, results -import years/bc26/maps +import years/dispatch export match @@ -53,9 +53,9 @@ proc sha256Hex*(data: string): string = for b in sha256(data): result.add(toHex(b, 2).toLowerAscii()) -proc mapSha*(name: string): string = +proc mapSha*(year, name: string): string = try: - sha256Hex(readFile(mapPath(name))) + sha256Hex(readFile(mapPathFor(year, name))) except CatchableError: "" @@ -69,6 +69,7 @@ proc seatJson(seat: SeatReport, slot: int): JsonNode = "alias": seat.alias, "name": seat.name, "policy": seat.policyKind, + "chassis": seat.chassis, "sheet": seat.sheet.toJson(), "sheet_submitted": seat.sheet.submitted, "sheet_defaults_applied": applied, @@ -152,13 +153,12 @@ type roundInGame*: int frame*: int totalFrames*: int - world*: World - clans*: array[2, Clan] + session*: Session mismatchRound*: int frameGame*: seq[int] frameRound*: seq[int] -proc parseSeat(node: JsonNode): SeatReport = +proc parseSeat(node: JsonNode, year: string): SeatReport = result.name = node{"name"}.getStr() result.alias = node{"alias"}.getStr() result.policyKind = node{"policy"}.getStr("scripted") @@ -173,12 +173,15 @@ proc parseSeat(node: JsonNode): SeatReport = wrapper["sheet"] = node{"sheet"} wrapper["notes"] = %node{"notes"}.getStr() wrapper["motto"] = %node{"motto"}.getStr() - result.sheet = validate(wrapper) - ## `chassis` is not a knob, so `validate` does not read it — but the - ## deriver has to run the bot the recording ran or every round mismatches. - ## It rides in the applied sheet and is restored here. - result.sheet.doctrine.chassis = - parseChassis(node{"sheet"}{"chassis"}.getStr("awu")) + result.sheet = validate(wrapper, year) + ## `chassis` is not a knob, so `validate` does not read it — but the deriver + ## has to run the bot the recording ran or every round mismatches. bc26 rides + ## it in the applied sheet; bc20 records it on the SEAT, beside the policy + ## kind. Both are restored here. + result.chassis = node{"chassis"}.getStr("") + if year != "bc20": + result.sheet.doctrine.chassis = + parseChassis(node{"sheet"}{"chassis"}.getStr("awu")) result.sheet.notes = node{"notes"}.getStr() result.sheet.motto = node{"motto"}.getStr() result.sheet.submitted = node{"sheet_submitted"}.getStr("{}") @@ -200,7 +203,7 @@ proc parseReplay*(text: string): ReplayDoc = result.promptPreamble = doc{"prompt_preamble"}.getStr() for slot in 0 .. 1: result.names[slot] = doc["names"][slot].getStr() - result.seats[slot] = parseSeat(doc["seats"][slot]) + result.seats[slot] = parseSeat(doc["seats"][slot], result.year) for g in doc["games"]: result.games.add(GameHeader( index: g{"index"}.getInt(), @@ -231,14 +234,17 @@ proc parseReplay*(text: string): ReplayDoc = for v in plan["abandon_after"]: result.plan.abandonAfter.add(v.getInt()) result.plan.sheets[0] = result.seats[0].sheet result.plan.sheets[1] = result.seats[1].sheet + for slot in 0 .. 1: + result.plan.chassis[slot] = + parseChassisKind(doc["seats"][slot]{"chassis"}.getStr("bowl-of-chowder")) proc startGame(d: Deriver, index: int) = d.gameIndex = index d.roundInGame = 0 if index < d.doc.plan.maps.len: - let spec = loadMap(d.doc.plan.maps[index]) - d.world = newWorld(spec, d.doc.plan.maxRounds) - d.clans = newClans(d.doc.plan.sheets, d.doc.plan.sideAslots[index]) + d.session = newSession(d.doc.year, d.doc.plan.maps[index], + d.doc.plan.sheets, d.doc.plan.sideAslots[index], d.doc.plan.maxRounds, + d.doc.plan.chassis, index) proc gameRecord*(doc: ReplayDoc, index: int): tuple[rounds: int, chain: string, rounds_chains: string] = @@ -281,10 +287,10 @@ proc advance*(d: Deriver): bool {.discardable.} = return false let nextFrame = d.frame + 1 let wantGame = d.frameGame[nextFrame] - if wantGame != d.gameIndex or d.world == nil: + if wantGame != d.gameIndex or d.session == nil: d.startGame(wantGame) - runRound(d.world, d.clans) - d.roundInGame = d.world.currentRound + d.session.stepRound() + d.roundInGame = d.session.currentRound d.frame = nextFrame ## The recorded hash chain proves the re-derivation matches the recording. ## EVERY round is compared, against the chain the recorder was on at the end @@ -294,7 +300,7 @@ proc advance*(d: Deriver): bool {.discardable.} = if d.mismatchRound < 0: let record = d.doc.gameRecord(wantGame) let at = (d.roundInGame - 1) * ChainHexLen - let here = toHex(d.world.hashChain) + let here = d.session.hashChainHex() if record.rounds_chains.len >= at + ChainHexLen and here != record.rounds_chains[at ..< at + ChainHexLen]: d.mismatchRound = d.roundInGame diff --git a/src/battlecode/results.nim b/src/battlecode/results.nim index 2f8615a..b806080 100644 --- a/src/battlecode/results.nim +++ b/src/battlecode/results.nim @@ -4,9 +4,8 @@ ## key set `tools/ci/docker_smoke.sh` asserts are the same set, and ## `tests/test_manifest.nim` fails when any one of them drifts. -import std/[json, strutils] +import std/json import sim_types, sheet, match -import years/bc26/rules type SeatReport* = object @@ -18,29 +17,28 @@ type fallback*: string ## "" when the seat's own doctrine was used fallbackDetail*: string ## the provider's own words, <= 200 runes brief*: string ## the prompt payload composed for this seat + chassis*: string ## D1: fixed by the operator, never a sheet field proc gamesJson(games: seq[GameOutcome]): JsonNode = + ## The five YEAR-NEUTRAL keys are always present and always required; the + ## year's own statistics ride beside them as optional siblings. Deliberately + ## NOT a nested `stats` object: nesting would change the bytes every shipped + ## bc26 replay's `result` block carries and force a compatibility shim in the + ## endcard. Relaxing `required` changes nothing that already exists. result = newJArray() for g in games: - result.add(%*{ + var entry = %*{ "map": g.mapName, "side": [(if g.sideAslot == 0: "A" else: "B"), (if g.sideAslot == 0: "B" else: "A")], "rounds_played": g.roundsPlayed, "winner": g.winnerSlot, - "end_reason": $g.endReason, - "cooperation_at_end": g.cooperationAtEnd, - "backstab_round": g.backstabRound, - "backstab_by": g.backstabBySlot, - "cat_damage": [g.catDamage[0], g.catDamage[1]], - "cheese_transferred": [g.cheeseTransferred[0], g.cheeseTransferred[1]], - "kings_alive": [g.kingsAlive[0], g.kingsAlive[1]], - "kings_built": [g.kingsBuilt[0], g.kingsBuilt[1]], - "rats_built": [g.ratsBuilt[0], g.ratsBuilt[1]], - "rats_alive": [g.ratsAlive[0], g.ratsAlive[1]], - "traps_placed": [g.trapsPlaced[0], g.trapsPlaced[1]], - "dirt_placed": [g.dirtPlaced[0], g.dirtPlaced[1]] - }) + "end_reason": g.endReason + } + if g.stats != nil and g.stats.kind == JObject: + for key, value in g.stats: + entry[key] = value + result.add(entry) proc resultsJson*( seats: array[2, SeatReport], @@ -96,6 +94,34 @@ proc resultsJson*( "game_version": GameVersion } +const RequiredGameKeys* = [ + "map", "side", "rounds_played", "winner", "end_reason" +] + ## The year-neutral keys `results.games[].required` names. + +const Bc26GameKeys* = [ + "cooperation_at_end", "backstab_round", "backstab_by", "cat_damage", + "cheese_transferred", "kings_alive", "kings_built", "rats_built", + "rats_alive", "traps_placed", "dirt_placed" +] + +const Bc20GameKeys* = [ + "hq_alive", "hq_lost_round", "hq_lost_cause", "soup_mined", "soup_refined", + "net_worth", "units_alive", "units_built", "miners_built", + "landscapers_built", "drones_built", "vaporators_built", "net_guns_built", + "dirt_moved", "drone_pickups", "drone_water_drops", "net_gun_kills", + "transactions_sent", "transactions_minted", "blockchain_soup_spent", + "global_pollution_peak", "flooded_tiles_end", "water_level_end" +] + +const EndReasons* = [ + "kings_destroyed", "cats_cleared", "round_limit", "abandoned", + "hq_destroyed", "quantity", "quality", "broadcasts", "highest_id", + "coin_flip" +] + ## The union of both years' `DominationFactor` renderings plus our own + ## wall-clock `abandoned`. + const ResultsKeys* = [ "names", "aliases", "scores", "wins", "points", "games", "seed", "year", "policy_kind", "sheet_defaults_applied", "fallbacks", "decision_ms", @@ -105,11 +131,9 @@ const ResultsKeys* = [ ## manifest's `results_schema.required` and the list `docker_smoke.sh` ## checks. -const GameKeys* = [ - "map", "side", "rounds_played", "winner", "end_reason", - "cooperation_at_end", "backstab_round", "backstab_by", "cat_damage", - "cheese_transferred", "kings_alive", "kings_built", "rats_built", - "rats_alive", "traps_placed", "dirt_placed" -] +const GameKeys* = @RequiredGameKeys & @Bc26GameKeys + ## The key set a bc26 game emits, unchanged: the five required keys plus + ## bc26's eleven. `tests/test_manifest.nim` checks both years against the + ## manifest's `results_schema`. const EpisodeReasons* = ["complete", "deadline", "fault"] diff --git a/src/battlecode/server.nim b/src/battlecode/server.nim index d73c911..1c36052 100644 --- a/src/battlecode/server.nim +++ b/src/battlecode/server.nim @@ -17,8 +17,7 @@ import std/[json, locks, monotimes, os, strutils, times] import mummy, mummy/routers import bitworld/[runtime, spriteprotocol] import seats, sim_types, sheet, baselines, decide, match, replay, results -import years/[registry] -import years/bc26/maps +import years/[registry, dispatch] type AppState = object @@ -114,7 +113,7 @@ proc applyRegistration(slot: int, payload: string) = app.policy[slot].registered = true app.policy[slot].prompt = prompt app.policy[slot].isLlm = prompt.len > 0 - app.policy[slot].baseline = parseBaseline(scripted) + app.policy[slot].scripted = scripted app.policy[slot].label = block: let explicit = node{"policy"}.getStr().strip() if explicit.len > 0: explicit @@ -309,7 +308,7 @@ proc parseConfig*(text: string): GameConfig = if result.numAgents != 2: raise newException(ConfigError, "battlecode is a two-seat game; num_agents was " & $result.numAgents) - if poolNames(result.pool).len == 0: + if poolNamesFor(result.year, result.pool).len == 0: raise newException(ConfigError, "unknown pool " & result.pool) result.maxRounds = min(result.maxRounds, yearSpec(result.year).maxRounds) @@ -344,7 +343,13 @@ proc runEpisode*(runtimeConfig: RuntimeConfig, config: GameConfig) = policy = app.policy let seed = if config.seed != 0: config.seed else: randomSeed() - plan = buildPlan(config, [defaultSheet(), defaultSheet()], seed) + plan = buildPlan(config, [defaultSheet(config.year), + defaultSheet(config.year)], seed) + ## D1: the chassis is fixed by the OPERATOR, never by the sheet. A scripted + ## seat drives the chassis its `PLAYER_SCRIPTED` names; an LLM seat drives + ## the fixed champion chassis. + for slot in 0 .. 1: + plan.chassis[slot] = chassisForSeat(config.year, policy[slot]) events.add(ev("episode_start", ms = 0, fields = %*{ "seed": seed, "year": config.year, "maps": plan.maps, "aliases": [AliasA, AliasB]})) @@ -378,7 +383,9 @@ proc runEpisode*(runtimeConfig: RuntimeConfig, config: GameConfig) = decisionMs: decision.decisionMs[slot], fallback: decision.fallback[slot], fallbackDetail: decision.fallbackDetail[slot], - brief: decision.briefs[slot]) + brief: decision.briefs[slot], + chassis: chassisNameFor(config.year, policy[slot], + decision.sheets[slot])) events.add(ev("episode_end", ms = 0, fields = %*{"reason": $reason})) let wallClock = (getMonoTime() - episodeStart).inMilliseconds.float / 1000.0 @@ -392,12 +399,13 @@ proc runEpisode*(runtimeConfig: RuntimeConfig, config: GameConfig) = "num_agents": config.numAgents}, seed: seed, seats: seats, events: events, result: resultsDoc, plan: plan, promptPreamble: (if decision.briefs[0].len > 0 or - decision.briefs[1].len > 0: SystemPreamble else: "")) + decision.briefs[1].len > 0: preambleFor(config.year) + else: "")) for slot in 0 .. 1: doc.names[slot] = seats[slot].name for g in games: doc.games.add(GameHeader(index: g.index, map: g.mapName, - mapSha: mapSha(g.mapName), sideAslot: g.sideAslot, + mapSha: mapSha(config.year, g.mapName), sideAslot: g.sideAslot, rounds: g.roundsPlayed, hashChain: g.hashChain, roundChains: g.roundChains)) diff --git a/src/battlecode/sheet.nim b/src/battlecode/sheet.nim index 275d490..dba1477 100644 --- a/src/battlecode/sheet.nim +++ b/src/battlecode/sheet.nim @@ -1,5 +1,11 @@ -## The doctrine sheet: what a cog may say, how a reply is parsed TOLERANTLY, -## and how an illegal field is repaired instead of rejected. +## The doctrine sheet ENVELOPE: what a cog may say, how a reply is parsed +## TOLERANTLY, and how an illegal field is repaired instead of rejected. +## +## Year-neutral. The knob TABLE for a year — its types, its defaults, its +## ranges, its repair order and its plain-words readout — lives in +## `years//knobs.nim`; this file owns `notes`, `motto`, +## `defaultsApplied`, `unknownFields`, `submitted` and the dispatch into the +## year. The tolerant-parsing primitives live in `sheet_common.nim`. ## ## Both policy kinds — an LLM reply and a scripted baseline — go through this ## same `validate`, which is what makes `tests/test_baselines.nim`'s @@ -9,192 +15,53 @@ ## A sheet can never be REJECTED. An unknown key is recorded and ignored; a ## mistyped or out-of-range value takes that field's default. A cog therefore ## cannot forfeit a match by answering badly, only by answering weakly. +## +## `chassis` is NOT a knob in either year (D1). bc26 keeps the field on its +## `Doctrine` because the scripted filler path sets it directly and the replay +## records it; bc20 has no such field at all — its chassis is a `ChassisKind` +## the operator picks, carried on the seat record. -import std/[json, strutils, tables, unicode] -import sim_types - -type - Chassis* = enum - chAwu = "awu" - chScaffold = "scaffold" - - BackstabPolicy* = enum - bpNever = "never" - bpWhenAhead = "when_ahead" - bpAtRoundN = "at_round_N" - bpOnFirstContact = "on_first_contact" - bpRetaliateOnly = "retaliate_only" - - CatEngagement* = enum - ceAvoid = "avoid" - ceOpportunistic = "opportunistic" - ceHunt = "hunt" - ceFeed = "feed" - - SpawnCurve* = enum - scLean = "lean" - scSteady = "steady" - scSwarm = "swarm" +import std/[json, tables] +import sim_types, sheet_common +import years/bc26/knobs as knobs26 +import years/bc20/knobs as knobs20 - DirtWallPolicy* = enum - dwNone = "none" - dwKingShell = "king_shell" - dwChoke = "choke" +export sim_types, sheet_common, knobs26, knobs20 - Doctrine* = object - ## The 10 doctrine knobs, each with a named site in the chassis - ## (docs/RULES.md §The doctrine sheet lists the site per knob), plus the - ## chassis selector, which is NOT one of them. - chassis*: Chassis - ## NOT A KNOB. An LLM doctrine always runs `awu`: `chassis` is absent - ## from `KnownKeys` and from the prompt's knob list, so a reply that - ## names it is recorded in `unknownFields` and ignored. The only way to - ## select `scaffold` is the scripted filler path - ## (`PLAYER_SCRIPTED=scaffold` → `baselines.baselineSheet`), which sets - ## this field directly rather than through the sheet. - backstabPolicy*: BackstabPolicy - backstabRound*: int - catEngagement*: CatEngagement - catTrapBudget*: int - ratTrapBudget*: int - spawnCurve*: SpawnCurve - cheeseFerryRatio*: float - kingCountTarget*: int - dirtWallPolicy*: DirtWallPolicy - throwRatsToFeedCats*: bool +const + YearBc26* = "bc26" + YearBc20* = "bc20" +type Sheet* = object - doctrine*: Doctrine + year*: string + doctrine*: knobs26.Doctrine ## the bc26 knobs; defaults on a bc20 sheet + doctrine20*: knobs20.Doctrine20 ## the bc20 knobs; defaults on a bc26 sheet notes*: string motto*: string defaultsApplied*: seq[string] unknownFields*: seq[string] submitted*: string ## the raw sheet object as received, for the replay -const - KnownKeys* = [ - "backstab_policy", "backstab_round", "cat_engagement", - "cat_trap_budget", "rat_trap_budget", "spawn_curve", "cheese_ferry_ratio", - "king_count_target", "dirt_wall_policy", "throw_rats_to_feed_cats" - ] - ## THE LLM-VISIBLE KNOB SURFACE, and the same list the prompt preamble - ## prints. `chassis` is deliberately not in it: which bot drives the clan - ## is not a strategic choice a cog gets to make, it is which policy the - ## operator ran. A reply that sends `chassis` is treated as any other - ## unknown key — recorded, ignored, and the clan plays `awu`. - -proc defaultDoctrine*(): Doctrine = - Doctrine( - chassis: chAwu, - backstabPolicy: bpRetaliateOnly, - backstabRound: 600, - catEngagement: ceOpportunistic, - catTrapBudget: 40, - ratTrapBudget: 60, - spawnCurve: scSteady, - cheeseFerryRatio: 0.5, - kingCountTarget: 3, - dirtWallPolicy: dwKingShell, - throwRatsToFeedCats: false - ) - -proc defaultSheet*(): Sheet = - Sheet(doctrine: defaultDoctrine(), notes: "", motto: "", submitted: "{}") - -proc normalizeKey(text: string): string = - text.strip().toLowerAscii().replace("-", "_").replace(" ", "_") - -proc enumFromText[T: enum](text: string, fallback: T): T = - let key = normalizeKey(text) - for value in T: - if normalizeKey($value) == key: - return value - fallback - -proc readNumber(node: JsonNode): tuple[ok: bool, value: float] = - ## An int, a float, or a numeric string. Anything non-finite reports - ## `ok = false` so the caller applies the field's default rather than - ## inventing a value. - if node.isNil: return (false, 0.0) - case node.kind - of JInt: (true, float(node.getBiggestInt())) - of JFloat: - let f = node.getFloat() - if f != f or f > 1.0e9 or f < -1.0e9: (false, 0.0) else: (true, f) - of JString: - try: (true, parseFloat(node.getStr().strip())) - except CatchableError: (false, 0.0) - of JBool: (true, (if node.getBool(): 1.0 else: 0.0)) - else: (false, 0.0) - -proc readBool(node: JsonNode): tuple[ok: bool, value: bool] = - if node.isNil: return (false, false) - case node.kind - of JBool: (true, node.getBool()) - of JInt: (true, node.getBiggestInt() != 0) - of JString: - case normalizeKey(node.getStr()) - of "true", "yes", "1", "on": (true, true) - of "false", "no", "0", "off": (true, false) - else: (false, false) - else: (false, false) - -proc extractJsonObject*(text: string): JsonNode = - ## The outermost balanced `{...}` in a model reply, tolerating markdown - ## fences and any prose around it. Falls back to first-brace..last-brace, - ## which recovers a reply whose braces sit inside a quoted string. - var - depth = 0 - start = -1 - inString = false - escaped = false - for i, ch in text: - if inString: - if escaped: escaped = false - elif ch == '\\': escaped = true - elif ch == '"': inString = false - continue - case ch - of '"': inString = true - of '{': - if depth == 0: start = i - inc depth - of '}': - if depth > 0: - dec depth - if depth == 0 and start >= 0: - try: - return parseJson(text[start .. i]) - except CatchableError: - start = -1 - else: discard - let - first = text.find('{') - last = text.rfind('}') - if first < 0 or last <= first: - raise newException(BattlecodeError, - "no JSON object in reply: " & text.strip().truncateRunes(160). - replace("\n", " ")) - parseJson(text[first .. last]) +proc knownKeysFor*(year: string): seq[string] = + if year == YearBc20: + @(knobs20.KnownKeys20) + else: + @(knobs26.KnownKeys) -proc parseChassis*(text: string): Chassis = - ## The RECORDED chassis, read back on playback. `validate` never reads - ## `chassis` — it is not a knob — so the replay's applied sheet is the only - ## place the deriver can learn which bot actually drove a clan. Anything - ## unrecognised is `awu`, the chassis every LLM doctrine runs. - let key = normalizeKey(text) - for value in Chassis: - if $value == key: - return value - chAwu +proc defaultSheet*(year = YearBc26): Sheet = + Sheet(year: year, doctrine: knobs26.defaultDoctrine(), + doctrine20: knobs20.defaultDoctrine20(), + notes: "", motto: "", submitted: "{}") -proc validate*(payload: JsonNode): Sheet = +proc validate*(payload: JsonNode, year = YearBc26): Sheet = ## Turn one parsed reply into a legal `Sheet`. Never raises: every bounded ## field is repaired to its default and the repair is recorded, so a cog ## always ends the doctrine phase with a playable doctrine. - result = defaultSheet() + result = defaultSheet(year) + let known = knownKeysFor(year) if payload.isNil or payload.kind != JObject: - result.defaultsApplied = @KnownKeys + result.defaultsApplied = known return let sheetNode = @@ -211,156 +78,41 @@ proc validate*(payload: JsonNode): Sheet = if keyCount > MaxSheetKeys: break let norm = normalizeKey(key) - if norm in KnownKeys: + if norm in known: seen[norm] = value elif result.unknownFields.len < MaxUnknownFields: + ## This is where a submitted `chassis` lands, in EITHER year (D1): + ## recorded, never honoured. result.unknownFields.add(key.truncateRunes(MaxUnknownFieldRunes)) - template repair(name: string) = - result.defaultsApplied.add(name) - - # --- chassis is NOT read here -------------------------------------------- - # It is not in `KnownKeys`, so the loop above has already recorded it in - # `unknownFields`; `result.doctrine.chassis` keeps `defaultDoctrine()`'s - # `awu`. `decide.nim` logs the seat that tried. - - # --- backstab_policy / backstab_round ------------------------------------ - if "backstab_policy" in seen: - if seen["backstab_policy"].kind == JString: - let text = normalizeKey(seen["backstab_policy"].getStr()) - var found = false - for value in BackstabPolicy: - if normalizeKey($value) == text: - result.doctrine.backstabPolicy = value - found = true - if not found: repair("backstab_policy") - else: - repair("backstab_policy") - if "backstab_round" in seen: - let n = readNumber(seen["backstab_round"]) - if n.ok and n.value >= 1.0 and n.value <= 2000.0: - result.doctrine.backstabRound = int(n.value) - else: - repair("backstab_round") - - # --- cat_engagement ------------------------------------------------------ - if "cat_engagement" in seen: - if seen["cat_engagement"].kind == JString: - let text = normalizeKey(seen["cat_engagement"].getStr()) - var found = false - for value in CatEngagement: - if normalizeKey($value) == text: - result.doctrine.catEngagement = value - found = true - if not found: repair("cat_engagement") - else: - repair("cat_engagement") - - # --- integer budgets ----------------------------------------------------- - template intKnob(name: string, field: untyped, lo, hi: int) = - if name in seen: - let n = readNumber(seen[name]) - if n.ok and n.value >= float(lo) and n.value <= float(hi): - field = int(n.value) - else: - repair(name) - - intKnob("cat_trap_budget", result.doctrine.catTrapBudget, 0, 200) - intKnob("rat_trap_budget", result.doctrine.ratTrapBudget, 0, 200) - intKnob("king_count_target", result.doctrine.kingCountTarget, 1, 5) - - # --- spawn_curve --------------------------------------------------------- - if "spawn_curve" in seen: - if seen["spawn_curve"].kind == JString: - let text = normalizeKey(seen["spawn_curve"].getStr()) - var found = false - for value in SpawnCurve: - if normalizeKey($value) == text: - result.doctrine.spawnCurve = value - found = true - if not found: repair("spawn_curve") - else: - repair("spawn_curve") - - # --- cheese_ferry_ratio -------------------------------------------------- - if "cheese_ferry_ratio" in seen: - let n = readNumber(seen["cheese_ferry_ratio"]) - if n.ok and n.value >= 0.0 and n.value <= 1.0: - result.doctrine.cheeseFerryRatio = n.value - else: - repair("cheese_ferry_ratio") - - # --- dirt_wall_policy ---------------------------------------------------- - if "dirt_wall_policy" in seen: - if seen["dirt_wall_policy"].kind == JString: - let text = normalizeKey(seen["dirt_wall_policy"].getStr()) - var found = false - for value in DirtWallPolicy: - if normalizeKey($value) == text: - result.doctrine.dirtWallPolicy = value - found = true - if not found: repair("dirt_wall_policy") - else: - repair("dirt_wall_policy") - - # --- throw_rats_to_feed_cats --------------------------------------------- - if "throw_rats_to_feed_cats" in seen: - let b = readBool(seen["throw_rats_to_feed_cats"]) - if b.ok: - result.doctrine.throwRatsToFeedCats = b.value - else: - repair("throw_rats_to_feed_cats") + if year == YearBc20: + result.doctrine20 = knobs20.applyKnobs20(seen, result.defaultsApplied) + else: + result.doctrine = knobs26.applyKnobs(seen, result.defaultsApplied) # --- free text ----------------------------------------------------------- result.notes = sanitizeLine(payload{"notes"}.getStr(), MaxNoteRunes) result.motto = sanitizeLine(payload{"motto"}.getStr(), MaxMottoRunes) -proc parseReply*(text: string): Sheet = +proc parseReply*(text: string, year = YearBc26): Sheet = ## Tolerant end-to-end parse of one model reply. Raises only when there is ## no JSON object at all — the one condition the retry and then the ## scripted fallback exist for. ## MaxReplyBytes is BYTES (the note's cap table says 16 KB), cut on a rune ## boundary — `truncateRunes` here kept 16384 runes, i.e. up to 64 KB. let capped = text.truncateBytes(MaxReplyBytes) - validate(extractJsonObject(capped)) + validate(extractJsonObject(capped), year) proc toJson*(sheet: Sheet): JsonNode = - %*{ - "chassis": $sheet.doctrine.chassis, - "backstab_policy": $sheet.doctrine.backstabPolicy, - "backstab_round": sheet.doctrine.backstabRound, - "cat_engagement": $sheet.doctrine.catEngagement, - "cat_trap_budget": sheet.doctrine.catTrapBudget, - "rat_trap_budget": sheet.doctrine.ratTrapBudget, - "spawn_curve": $sheet.doctrine.spawnCurve, - "cheese_ferry_ratio": sheet.doctrine.cheeseFerryRatio, - "king_count_target": sheet.doctrine.kingCountTarget, - "dirt_wall_policy": $sheet.doctrine.dirtWallPolicy, - "throw_rats_to_feed_cats": sheet.doctrine.throwRatsToFeedCats - } + if sheet.year == YearBc20: + knobs20.toJson20(sheet.doctrine20) + else: + knobs26.toJson(sheet.doctrine) proc plainWords*(sheet: Sheet): seq[string] = - ## The endcard/`#doctrines` readout: the sheet in words a spectator can + ## The endcard/doctrine-overlay readout: the sheet in words a spectator can ## read without knowing the schema. - case sheet.doctrine.backstabPolicy - of bpNever: result.add("never betrays") - of bpRetaliateOnly: result.add("retaliates only") - of bpWhenAhead: result.add("betrays when ahead") - of bpAtRoundN: result.add("betrays at round " & $sheet.doctrine.backstabRound) - of bpOnFirstContact: result.add("betrays on first contact") - case sheet.doctrine.catEngagement - of ceAvoid: result.add("avoids cats") - of ceOpportunistic: result.add("fights cats when handy") - of ceHunt: result.add("hunts cats") - of ceFeed: result.add("feeds rats to cats") - result.add($sheet.doctrine.spawnCurve & " spawning") - result.add("keeps " & $sheet.doctrine.kingCountTarget & " kings") - result.add($sheet.doctrine.ratTrapBudget & " rat traps") - result.add($sheet.doctrine.catTrapBudget & " cat traps") - result.add($int(sheet.doctrine.cheeseFerryRatio * 100) & "% ferrying") - case sheet.doctrine.dirtWallPolicy - of dwNone: result.add("no dirt work") - of dwKingShell: result.add("walls its kings in") - of dwChoke: result.add("walls the chokepoints") - if sheet.doctrine.throwRatsToFeedCats: - result.add("throws rats to cats") + if sheet.year == YearBc20: + knobs20.plainWords20(sheet.doctrine20) + else: + knobs26.plainWords(sheet.doctrine) diff --git a/src/battlecode/sheet_common.nim b/src/battlecode/sheet_common.nim new file mode 100644 index 0000000..bbe2ab8 --- /dev/null +++ b/src/battlecode/sheet_common.nim @@ -0,0 +1,92 @@ +## Year-neutral doctrine-sheet plumbing, extracted UNCHANGED from `sheet.nim` +## when the second year module landed. +## +## `normalizeKey`, `readNumber`, `readBool` and `extractJsonObject` are the +## tolerant-parsing primitives every year's knob table repairs values with; +## the rune and byte caps and `sanitizeLine` live in `sim_types` and are +## re-exported here so a `years//knobs.nim` needs exactly one import. +## +## Nothing here knows what a knob is. A year's knob table lives in +## `years//knobs.nim`, and `sheet.nim` is the envelope that dispatches +## into it. + +import std/[json, strutils] +import sim_types + +export sim_types + +proc normalizeKey*(text: string): string = + text.strip().toLowerAscii().replace("-", "_").replace(" ", "_") + +proc enumFromText*[T: enum](text: string, fallback: T): T = + let key = normalizeKey(text) + for value in T: + if normalizeKey($value) == key: + return value + fallback + +proc readNumber*(node: JsonNode): tuple[ok: bool, value: float] = + ## An int, a float, or a numeric string. Anything non-finite reports + ## `ok = false` so the caller applies the field's default rather than + ## inventing a value. + if node.isNil: return (false, 0.0) + case node.kind + of JInt: (true, float(node.getBiggestInt())) + of JFloat: + let f = node.getFloat() + if f != f or f > 1.0e9 or f < -1.0e9: (false, 0.0) else: (true, f) + of JString: + try: (true, parseFloat(node.getStr().strip())) + except CatchableError: (false, 0.0) + of JBool: (true, (if node.getBool(): 1.0 else: 0.0)) + else: (false, 0.0) + +proc readBool*(node: JsonNode): tuple[ok: bool, value: bool] = + if node.isNil: return (false, false) + case node.kind + of JBool: (true, node.getBool()) + of JInt: (true, node.getBiggestInt() != 0) + of JString: + case normalizeKey(node.getStr()) + of "true", "yes", "1", "on": (true, true) + of "false", "no", "0", "off": (true, false) + else: (false, false) + else: (false, false) + +proc extractJsonObject*(text: string): JsonNode = + ## The outermost balanced `{...}` in a model reply, tolerating markdown + ## fences and any prose around it. Falls back to first-brace..last-brace, + ## which recovers a reply whose braces sit inside a quoted string. + var + depth = 0 + start = -1 + inString = false + escaped = false + for i, ch in text: + if inString: + if escaped: escaped = false + elif ch == '\\': escaped = true + elif ch == '"': inString = false + continue + case ch + of '"': inString = true + of '{': + if depth == 0: start = i + inc depth + of '}': + if depth > 0: + dec depth + if depth == 0 and start >= 0: + try: + return parseJson(text[start .. i]) + except CatchableError: + start = -1 + else: discard + let + first = text.find('{') + last = text.rfind('}') + if first < 0 or last <= first: + raise newException(BattlecodeError, + "no JSON object in reply: " & text.strip().truncateRunes(160). + replace("\n", " ")) + parseJson(text[first .. last]) diff --git a/src/battlecode/years/bc20/blockchain.nim b/src/battlecode/years/bc20/blockchain.nim new file mode 100644 index 0000000..c216e00 --- /dev/null +++ b/src/battlecode/years/bc20/blockchain.nim @@ -0,0 +1,62 @@ +## The 2020 blockchain: the transaction pool, `Transaction.compareTo`, and the +## per-round mint of at most `NUMBER_OF_TRANSACTIONS_PER_BLOCK = 7`. +## +## Pure: no world state, so `world.nim` can import it. The world owns the pool +## and the minted chain; this module owns the record and the ORDER. +## +## `common/Transaction.java`: +## +## if (other.cost != this.cost) return other.cost - this.cost; // higher cost first +## if (other.id != this.id) return other.id - this.id; // higher id first +## return serializedMessage.compareTo(other.serializedMessage); // lexicographically earlier first +## +## and the serialized message is the seven ints rendered with +## `Integer.toString` and joined by `_`. +## +## The engine drains a `PriorityQueue` with `poll()`; a full tie in all three +## keys is the one case a binary heap and a sorted extraction can disagree on, +## and it needs two transactions with the same cost, the same 32-bit random id +## and the same seven ints. A stable sort is used here so the order is total +## and deterministic (docs/RULES-BC20.md §Divergences). + +import std/[algorithm, strutils] + +const TransactionLength* = 7 + +type + Transaction* = object + cost*: int + message*: array[TransactionLength, int] + id*: int32 + serialized*: string + team*: int ## 0 = A, 1 = B; `transactionToTeam` in the engine + +proc serializeMessage*(message: array[TransactionLength, int]): string = + var parts: seq[string] + for v in message: + parts.add($int32(v)) + parts.join("_") + +proc newTransaction*(cost: int, message: array[TransactionLength, int], + id: int32, team: int): Transaction = + Transaction(cost: cost, message: message, id: id, + serialized: serializeMessage(message), team: team) + +proc compareTransactions*(a, b: Transaction): int = + ## Sorts the pool into MINTING order: `a` before `b` when `a.compareTo(b) < 0`. + if a.cost != b.cost: + return b.cost - a.cost + if a.id != b.id: + return (if b.id > a.id: 1 else: -1) + cmp(a.serialized, b.serialized) + +proc mintBlock*(pool: var seq[Transaction], perBlock: int): seq[Transaction] = + ## Drains up to `perBlock` transactions in comparator order. Everything that + ## did not make the cut STAYS IN THE POOL and remains eligible for ever. + if pool.len == 0: + return @[] + pool.sort(compareTransactions) + let take = min(perBlock, pool.len) + result = pool[0 ..< take] + for _ in 0 ..< take: + pool.delete(0) diff --git a/src/battlecode/years/bc20/chassis/boc.nim b/src/battlecode/years/bc20/chassis/boc.nim new file mode 100644 index 0000000..6e1b7ee --- /dev/null +++ b/src/battlecode/years/bc20/chassis/boc.nim @@ -0,0 +1,59 @@ +## `bowl-of-chowder` — the strong bc20 baseline and the champion chassis. +## +## Behaviour ported from `StoneT2000/Battlecode2020` `src/FinalChowBotStable/` +## (AGPL-3.0; see NOTICE), parameterised by the ten doctrine knobs. None of its +## Java is copied, compiled or shipped: the passive-lattice build order, the +## wall-then-terraform landscaper priority, the builder-miner election, the +## drone roles and the signal-code discipline are its IDEAS, rewritten in Nim. +## +## This file is only the dispatcher; the roles live one per module, exactly as +## the design note lays them out. + +import kit, hq, miner, designschool, landscaper, fulfillment, drone, netgun + +export kit + +proc openingPlan*(side: Side): string = + ## The build order and role split for the first 400 rounds, in one word — + ## read by the endcard and by `tests/test_bc20_knobs.nim`. + case side.doctrine.opening + of opRush: "rush" + of opLattice: "lattice" + of opPassiveLattice: "passive_lattice" + of opTurtle: "turtle" + +proc applyOpening*(side: Side) = + ## `opening` is a preset over the other knobs, applied ONCE when the side is + ## created. Every knob the cog set explicitly still wins where it is more + ## specific; the preset only moves the pacing the opening is named for. + case side.doctrine.opening + of opRush: + ## Get there before their wall does: terraform late, lattice small. + side.doctrine.terraformStartRound = + max(side.doctrine.terraformStartRound, 500) + if side.doctrine.rushTrigger == 0: + side.doctrine.rushTrigger = 250 + of opLattice: + ## Contest the middle while terraforming outward. + side.doctrine.terraformStartRound = + min(side.doctrine.terraformStartRound, 260) + of opPassiveLattice: + discard ## the defaults ARE the Bowl of Chowder build + of opTurtle: + ## Wall immediately, minimum miners, maximum net guns. + side.doctrine.wallHqRound = + (if side.doctrine.wallHqRound == 0: 0 + else: min(side.doctrine.wallHqRound, 120)) + side.doctrine.netGunRing = max(side.doctrine.netGunRing, 4) + side.doctrine.latticeRadius = min(side.doctrine.latticeRadius, 4) + +proc runBowlOfChowder*(w: World, side: Side, r: Robot) = + case r.kind + of rtHq: w.runHq(side, r) + of rtMiner: w.runMiner(side, r) + of rtDesignSchool: w.runDesignSchool(side, r) + of rtLandscaper: w.runLandscaper(side, r) + of rtFulfillmentCenter: w.runFulfillmentCenter(side, r) + of rtDeliveryDrone: w.runDrone(side, r) + of rtNetGun: w.runNetGun(side, r) + of rtRefinery, rtVaporator, rtCow: discard diff --git a/src/battlecode/years/bc20/chassis/designschool.nim b/src/battlecode/years/bc20/chassis/designschool.nim new file mode 100644 index 0000000..112adc8 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/designschool.nim @@ -0,0 +1,29 @@ +## The Design School: build a Landscaper whenever the roster is under the +## `landscaper_count_curve` target and the pool can pay for one, preferring a +## direction on the HQ side so a new landscaper starts where the wall is. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit + +proc runDesignSchool*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + if not isReady(r): return + if w.alive(side, rtLandscaper) >= + side.doctrine.landscaperTarget(w.currentRound): return + if w.stats.soup[ord(side.team)] < RobotSpecs[rtLandscaper].cost: return + var bestDir = dCenter + var bestScore = high(int) + for d in MoveDirs: + if not r.spend(1): break + if not w.canBuildRobot(r, rtLandscaper, d): continue + let candidate = r.loc + d + var score = if side.hasHq: chebyshev(candidate, side.hqLoc) else: 0 + if w.willFloodNextRound(candidate): score += 100 + if score < bestScore: + bestScore = score + bestDir = d + if bestDir == dCenter: return + if w.buildRobot(r, rtLandscaper, bestDir) >= 0: + w.firstBuild(side, rtLandscaper) diff --git a/src/battlecode/years/bc20/chassis/drone.nim b/src/battlecode/years/bc20/chassis/drone.nim new file mode 100644 index 0000000..fbf145c --- /dev/null +++ b/src/battlecode/years/bc20/chassis/drone.nim @@ -0,0 +1,210 @@ +## The Delivery Drone: four roles, selected by the `drone_role` knob. +## +## harass hunt enemy units inside `r² <= 3` and drop them on the +## nearest flooded tile +## wall hold a ring at Chebyshev 4 from the enemy HQ, blocking +## ground movement +## buster hunt enemy drones sitting in a wall by baiting them +## over our own net guns, and ferry a landscaper to the +## enemy HQ +## carry_landscapers ferry our own landscapers over impassable elevation +## onto the lattice +## +## Every role avoids tiles within `r² <= 15` of a known enemy Net Gun or enemy +## HQ unless it is carrying a landscaper to the enemy HQ, and a carried unit is +## always dropped on the nearest flooded tile that is not adjacent to a +## friendly building. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit, pathing, signals + +proc underGunFire(w: World, side: Side, r: Robot, l: Loc): bool = + ## A cheap, bounded threat test: the drone only knows about guns it can see. + for seen in w.sensed(r): + let occupant = w.getRobot(seen) + if occupant == nil: continue + if occupant.team == side.team or occupant.team == teamNeutral: continue + if occupant.kind notin {rtNetGun, rtHq}: continue + if l.distanceSquaredTo(seen) <= NetGunShootRadiusSquared: return true + false + +proc nearestWaterTile(w: World, side: Side, r: Robot): (bool, Loc) = + var best = loc(0, 0) + var bestD = high(int) + var found = false + for l in w.sensed(r): + if not w.isFlooded(l): continue + if w.isLocationOccupied(l): continue + if side.hasHq and chebyshev(l, side.hqLoc) <= 2: continue + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + best = l + found = true + (found, best) + +proc pickupTarget(w: World, side: Side, r: Robot, + wantOwn: bool): (bool, int, Loc) = + var bestId = -1 + var bestLoc = loc(0, 0) + var bestD = high(int) + for l in w.locationsWithinRadiusSquared(r.loc, + DeliveryDronePickupRadiusSquared): + if not r.spend(1): break + let target = w.getRobot(l) + if target == nil: continue + if not target.kind.canBePickedUp(): continue + if target.blocked: continue + let mine = target.team == side.team + if wantOwn: + if not mine or target.kind != rtLandscaper: continue + else: + if mine or target.team == teamNeutral: continue + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + bestId = target.id + bestLoc = l + (bestId >= 0, bestId, bestLoc) + +proc dropCargo(w: World, side: Side, r: Robot, intoWater: bool): bool = + if not r.holdingUnit: return false + if intoWater: + for d in MoveDirs: + if not r.spend(1): break + let l = r.loc + d + if not w.isFlooded(l): continue + if not w.canDropUnit(r, d): continue + w.dropUnit(r, d) + return true + return false + for d in MoveDirs: + if not r.spend(1): break + let l = r.loc + d + if w.isFlooded(l): continue + if not w.canDropUnit(r, d): continue + w.dropUnit(r, d) + return true + false + +proc huntEnemies(w: World, side: Side, r: Robot): bool = + ## Look for something to lift anywhere in sight and close on it; the pickup + ## itself only fires inside `r² <= 3`. + var best = loc(0, 0) + var bestD = high(int) + var found = false + for l in w.sensed(r): + let target = w.getRobot(l) + if target == nil: continue + if target.team == side.team or target.team == teamNeutral: continue + if not target.kind.canBePickedUp(): continue + if target.blocked: continue + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + best = l + found = true + if not found: return false + w.stepToward(side, r, best) + +proc runHarass(w: World, side: Side, r: Robot) = + if r.holdingUnit: + let (haveWater, water) = w.nearestWaterTile(side, r) + if haveWater and r.loc.isAdjacentTo(water): + if w.dropCargo(side, r, intoWater = true): return + if haveWater: + w.stepToward(side, r, water) + return + if side.hasEnemyHq: + w.stepToward(side, r, side.enemyHqLoc) + return + let (havePickup, id, _) = w.pickupTarget(side, r, wantOwn = false) + if havePickup and w.canPickUpUnit(r, id): + w.pickUpUnit(r, id) + return + if w.huntEnemies(side, r): return + if side.hasEnemyHq and not w.underGunFire(side, r, r.loc): + w.stepToward(side, r, side.enemyHqLoc) + +proc runWallRole(w: World, side: Side, r: Robot) = + ## A ring at Chebyshev 4 from the enemy HQ. The station is a pure function of + ## the robot id, so the ring fills evenly without any signalling. + if not side.hasEnemyHq: + w.runHarass(side, r) + return + if r.holdingUnit: + discard w.dropCargo(side, r, intoWater = true) + return + let slot = r.id mod 8 + let d = MoveDirs[slot] + let station = loc(side.enemyHqLoc.x + d.dx * 4, side.enemyHqLoc.y + d.dy * 4) + if r.loc == station: + let (havePickup, id, _) = w.pickupTarget(side, r, wantOwn = false) + if havePickup and w.canPickUpUnit(r, id): + w.pickUpUnit(r, id) + return + if not w.onTheMap(station): + w.runHarass(side, r) + return + w.stepToward(side, r, station) + +proc runBuster(w: World, side: Side, r: Robot) = + ## Ferry a landscaper to the enemy HQ, and bait enemy drones back over our + ## own guns when we are empty. + if r.holdingUnit: + if side.hasEnemyHq and chebyshev(r.loc, side.enemyHqLoc) <= 2: + if w.dropCargo(side, r, intoWater = false): return + if side.hasEnemyHq: + w.stepToward(side, r, side.enemyHqLoc) + return + discard w.dropCargo(side, r, intoWater = false) + return + let (haveOwn, ownId, _) = w.pickupTarget(side, r, wantOwn = true) + if haveOwn and w.canPickUpUnit(r, ownId): + w.pickUpUnit(r, ownId) + return + let (haveEnemy, enemyId, _) = w.pickupTarget(side, r, wantOwn = false) + if haveEnemy and w.canPickUpUnit(r, enemyId): + w.pickUpUnit(r, enemyId) + return + if side.hasHq and w.underGunFire(side, r, r.loc): + ## Bait: fall back over our own guns rather than trade with theirs. + w.stepToward(side, r, side.hqLoc) + return + if side.hasHq and w.alive(side, rtLandscaper) > 0: + w.stepToward(side, r, side.hqLoc) + +proc runCarryLandscapers(w: World, side: Side, r: Robot) = + if r.holdingUnit: + if side.hasHq and chebyshev(r.loc, side.hqLoc) <= + side.doctrine.latticeRadius: + if w.dropCargo(side, r, intoWater = false): return + if side.hasHq: + w.stepToward(side, r, side.hqLoc) + return + discard w.dropCargo(side, r, intoWater = false) + return + let (haveOwn, ownId, at) = w.pickupTarget(side, r, wantOwn = true) + if haveOwn and w.canPickUpUnit(r, ownId): + ## Only ferry a landscaper that is actually stuck — one standing outside + ## the lattice or on ground the flood takes next round. + let stuck = w.willFloodNextRound(at) or + (side.hasHq and chebyshev(at, side.hqLoc) > side.doctrine.latticeRadius) + if stuck: + w.pickUpUnit(r, ownId) + return + if side.hasHq: + w.stepToward(side, r, side.hqLoc) + +proc runDrone*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + w.readBlocks(side, r) + w.noteEnemyHq(side) + if not isReady(r): return + case side.doctrine.droneRole + of drHarass: w.runHarass(side, r) + of drWall: w.runWallRole(side, r) + of drBuster: w.runBuster(side, r) + of drCarryLandscapers: w.runCarryLandscapers(side, r) diff --git a/src/battlecode/years/bc20/chassis/fulfillment.nim b/src/battlecode/years/bc20/chassis/fulfillment.nim new file mode 100644 index 0000000..46ca5d5 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/fulfillment.nim @@ -0,0 +1,22 @@ +## The Fulfillment Center: build a Delivery Drone whenever the roster is under +## `4 + round/300` (capped 14) and the pool can pay, and always when +## `NEED_DRONES` is on the chain. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit + +proc droneTarget*(round: int): int = min(14, 4 + round div 300) + +proc runFulfillmentCenter*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + if not isReady(r): return + if w.alive(side, rtDeliveryDrone) >= droneTarget(w.currentRound): return + if w.stats.soup[ord(side.team)] < RobotSpecs[rtDeliveryDrone].cost: return + for d in MoveDirs: + if not r.spend(1): break + if not w.canBuildRobot(r, rtDeliveryDrone, d): continue + if w.buildRobot(r, rtDeliveryDrone, d) >= 0: + w.firstBuild(side, rtDeliveryDrone) + return diff --git a/src/battlecode/years/bc20/chassis/hq.nim b/src/battlecode/years/bc20/chassis/hq.nim new file mode 100644 index 0000000..c3293d5 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/hq.nim @@ -0,0 +1,91 @@ +## The HQ: build miners on the curve, shoot drones, and open the round-1 +## broadcast. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit, signals + +proc bestBuildDir(w: World, side: Side, r: Robot, kind: RobotKind): Dir = + ## Prefer the direction AWAY from the nearest flooded tile, so a fresh miner + ## is not spawned onto ground the water takes next. + var bestDir = dCenter + var bestScore = low(int) + for d in MoveDirs: + if not r.spend(1): break + if not w.canBuildRobot(r, kind, d): continue + let candidate = r.loc + d + var score = w.getDirt(candidate) * 4 + if w.willFloodNextRound(candidate): score -= 100 + if side.hasEnemyHq: + ## Miners come out on the side facing the map, not into a corner. + score -= chebyshev(candidate, side.enemyHqLoc) div 8 + if score > bestScore: + bestScore = score + bestDir = d + bestDir + +proc shootNearestDrone*(w: World, side: Side, r: Robot): bool + {.discardable.} = + ## Both the HQ and a net gun have this; the HQ runs it BEFORE building. + if not isReady(r): return false + var bestId = -1 + var bestD = high(int) + var bestCarrying = false + for l in w.locationsWithinRadiusSquared(r.loc, NetGunShootRadiusSquared): + if not r.spend(1): break + let target = w.getRobot(l) + if target == nil: continue + if target.kind != rtDeliveryDrone: continue + if target.team == r.team or target.team == teamNeutral: continue + let d = r.loc.distanceSquaredTo(l) + ## A drone that is CARRYING something is the one to take down first: the + ## unit it is holding dies with it if it is over water. + if (target.holdingUnit and not bestCarrying) or + (target.holdingUnit == bestCarrying and d < bestD): + bestD = d + bestId = target.id + bestCarrying = target.holdingUnit + if bestId < 0: return false + if not w.canShootUnit(r, bestId): return false + w.shootUnit(r, bestId) + true + +proc runHq*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + w.noteHq(side) + w.noteEnemyHq(side) + + if brain.turnCount == 1: + w.broadcast(side, r, SigHqLocation, r.loc.x, r.loc.y) + if side.doctrine.wallHqRound > 0 and + w.currentRound == w.effectiveWallRound(side): + w.broadcast(side, r, SigWallIn, r.loc.x, r.loc.y) + if side.doctrine.rushTrigger > 0 and + w.currentRound == side.doctrine.rushTrigger: + w.broadcast(side, r, SigRushNow, side.enemyHqLoc.x, side.enemyHqLoc.y) + + ## A half-buried HQ shouts about it and stops spending. + if r.dirtCarrying >= RobotSpecs[rtHq].dirtLimit div 2: + w.broadcast(side, r, SigHqUnderAttack, r.dirtCarrying) + discard w.shootNearestDrone(side, r) + return + + discard w.shootNearestDrone(side, r) + if not isReady(r): return + if w.alive(side, rtMiner) >= side.doctrine.minerTarget(w.currentRound): + return + ## RESERVE for the first Design School. Six miners at 70 is 420 soup and the + ## pool starts at 200, so an HQ that spends every credit on miners never lets + ## the builder afford a school — and a team with no landscapers cannot wall, + ## and an HQ that cannot wall drowns on the schedule. Three miners is enough + ## to earn the 150 back. + let reserve = + if w.alive(side, rtDesignSchool) < 1 and w.alive(side, rtMiner) >= 3: + RobotSpecs[rtDesignSchool].cost + else: 0 + if w.stats.soup[ord(side.team)] < RobotSpecs[rtMiner].cost + reserve: return + let d = w.bestBuildDir(side, r, rtMiner) + if d == dCenter: return + if w.buildRobot(r, rtMiner, d) >= 0: + w.firstBuild(side, rtMiner) diff --git a/src/battlecode/years/bc20/chassis/kit.nim b/src/battlecode/years/bc20/chassis/kit.nim new file mode 100644 index 0000000..1f3ca2f --- /dev/null +++ b/src/battlecode/years/bc20/chassis/kit.nim @@ -0,0 +1,170 @@ +## Shared bc20 chassis scaffolding: the team's doctrine, per-robot memory, and +## the DECISION BUDGET that replaces the engine's JVM bytecode limit. +## +## The engine meters a robot's turn in bytecodes through an instrumenting class +## loader. There is no JVM here, so every primitive step a bot takes — a tile +## sensed, a robot examined, a BFS node expanded, a direction evaluated, a +## block read — is charged against `Robot.opsLeft` instead, and the robot ends +## its turn WHERE IT STANDS when the budget runs out (no mid-turn resumption). +## Bounded, machine-independent, deterministic, and a deliberate divergence +## (docs/RULES-BC20.md §Divergences). +## +## The budget is ENFORCED BY THE SIM, not by the bot: every loop below runs +## through `spend`, and `tests/test_bc20_baselines.nim` asserts that no robot +## ever exceeds it. + +import std/tables +import ../../../rng +import ../knobs, ../world, ../flood + +export knobs, world, flood, rng, tables + +type + Brain* = ref object + ## One robot's private memory, keyed by robot id so it follows the robot + ## for its whole life. In Battlecode each robot gets its own class loader, + ## so a bot's `static` fields are per robot — reproduced here. + rng*: JavaRandom + turnCount*: int + role*: int ## per-unit role slot (wall index, drone station…) + hasRole*: bool + hasTarget*: bool + target*: Loc + hasSoupTip*: bool + soupTip*: Loc + announcedSoup*: bool + mode*: int ## landscaper: 0 wall, 1 terraform, 2 attack + history*: seq[Loc] ## the 6-tile no-repeat window that breaks oscillation + + Side* = ref object + team*: Team + doctrine*: Doctrine20 + brains*: Table[int, Brain] + hqLoc*: Loc + hasHq*: bool + enemyHqLoc*: Loc + hasEnemyHq*: bool + builderId*: int + wallClosed*: bool + wallClosedRound*: int + rushLaunched*: bool + rushUnits*: int + soupTips*: seq[Loc] + firstBuilt*: array[RobotKind, bool] + nextBlockToRead*: int + wallRoundCache*: int + +proc newSide*(team: Team, doctrine: Doctrine20): Side = + Side(team: team, doctrine: doctrine, brains: initTable[int, Brain](), + builderId: -1, wallClosedRound: -1, nextBlockToRead: 1, + wallRoundCache: -1) + +proc brainFor*(side: Side, r: Robot): Brain = + if r.id notin side.brains: + ## Seeded from the robot id, so a robot's private stream is a pure + ## function of the map seed (which decides ids) and nothing else. + side.brains[r.id] = Brain(rng: initJavaRandom(r.id)) + side.brains[r.id] + +proc spend*(r: Robot, ops: int): bool {.discardable.} = + ## Charge `ops` decision credits. False once the robot is out of budget; + ## every loop in the chassis checks it and stops. + if r.opsLeft <= 0: return false + r.opsLeft -= ops + r.opsLeft > 0 + +func other*(team: Team): Team = + if team == teamA: teamB else: teamA + +func alive*(w: World, side: Side, kind: RobotKind): int = + w.typeCount[ord(side.team)][kind] + +proc ownHq*(w: World, side: Side): Robot = + let id = w.hqId[ord(side.team)] + if id >= 0 and id in w.robotsById: w.robotsById[id] else: nil + +proc noteHq*(w: World, side: Side) = + let hq = w.ownHq(side) + if hq != nil: + side.hqLoc = hq.loc + side.hasHq = true + +proc symmetricLoc*(w: World, l: Loc): Loc = + ## The map is symmetric, so the enemy HQ is at the mirror of ours. The + ## chassis uses the symmetry the SIM recomputed, never the map file's. + case w.symmetry + of symVertical: loc(w.width - 1 - l.x, l.y) + of symHorizontal: loc(l.x, w.height - 1 - l.y) + of symRotational: loc(w.width - 1 - l.x, w.height - 1 - l.y) + +proc noteEnemyHq*(w: World, side: Side) = + if side.hasEnemyHq: return + if not side.hasHq: return + side.enemyHqLoc = w.symmetricLoc(side.hqLoc) + side.hasEnemyHq = true + +proc wallTarget*(w: World, side: Side): int = + ## `landscaper.nim` mode (a): the bar every one of the 8 tiles adjacent to + ## the own HQ must clear. The note's `waterLevel(round + 400) + 2` is a + ## MOVING bar, and a moving bar declares the wall closed at round 76 on a map + ## whose ring already sits at elevation 4 and then lets the same ring drown + ## at round 932. The bar used here is the same expression evaluated at the + ## LAST round the game can reach, so a wall is built once, to a height the + ## water never gets to, and stays closed. + int(waterLevelAt(min(WaterTableMaxRound, + max(w.currentRound + 400, w.maxRounds)))) + 2 + +proc ringTiles*(w: World, centre: Loc): seq[Loc] = + for d in MoveDirs: + let l = centre + d + if w.onTheMap(l): result.add(l) + +proc effectiveWallRound*(w: World, side: Side): int = + ## `wall_hq_round` says WHEN to start walling; the map says when it is too + ## late. An HQ is a building, so dirt dropped on its tile buries it rather + ## than raising it: the only thing that keeps an HQ dry is a ring of eight + ## tiles the water can never cross. On a low map (`maptestsmall`'s ring sits + ## at elevation 1 and floods at round 256) a doctrine that says "wall at 300" + ## has already lost, so the chassis starts 200 rounds before the ring's own + ## flood round when that is EARLIER than the knob. + ## + ## `wall_hq_round = 0` still means NEVER, which is what gives the knob its + ## teeth: never walling drowns the HQ, exactly as the sheet documents. + if side.doctrine.wallHqRound == 0: return 0 + if side.wallRoundCache >= 0: return side.wallRoundCache + if not side.hasHq: return side.doctrine.wallHqRound + var minElev = high(int) + for l in w.ringTiles(side.hqLoc): + minElev = min(minElev, w.map.elevation[w.idx(l)]) + if minElev == high(int) or minElev < 0: + side.wallRoundCache = side.doctrine.wallHqRound + else: + side.wallRoundCache = min(side.doctrine.wallHqRound, + max(1, roundWaterReaches(minElev) - 200)) + side.wallRoundCache + +proc latticeTarget*(side: Side, round: int): int = + ## `lattice.nim`: every non-wall tile inside `lattice_radius` is raised to + ## `waterLevel(round + 250) + 1`. + int(waterLevelAt(min(WaterTableMaxRound, round + 250))) + 1 + +proc willFloodNextRound*(w: World, l: Loc): bool = + ## A tile a ground unit must not be standing on when the flood arrives. + if not w.onTheMap(l): return false + if w.isFlooded(l): return true + float32(w.getDirt(l)) < waterLevelAt(w.currentRound + 1) + +iterator sensed*(w: World, r: Robot): Loc = + ## Every tile this robot can see this turn, charged one credit each. + let r2 = w.currentSensorRadiusSquared(r) + for l in w.locationsWithinRadiusSquared(r.loc, r2): + if r.opsLeft <= 0: break + r.opsLeft -= 1 + yield l + +proc firstBuild*(w: World, side: Side, kind: RobotKind) = + ## `first_build` is emitted once per team per unit kind — the beat the + ## scrubber draws. + if side.firstBuilt[kind]: return + side.firstBuilt[kind] = true + w.emit("first_build", ord(side.team), ord(kind), w.currentRound) diff --git a/src/battlecode/years/bc20/chassis/landscaper.nim b/src/battlecode/years/bc20/chassis/landscaper.nim new file mode 100644 index 0000000..541a90f --- /dev/null +++ b/src/battlecode/years/bc20/chassis/landscaper.nim @@ -0,0 +1,217 @@ +## The landscaper: three modes, in priority order. +## +## (a) WALL, from round `wall_hq_round`: claim one of the 8 tiles adjacent to +## the own HQ, stand ON it, and raise it under yourself to +## `waterLevel(round + 400) + 2` by digging from a tile OUTSIDE the ring. +## An HQ is a building, so dirt dropped on it BURIES it — the HQ's own +## tile can never be raised. The only way an HQ survives the flood is a +## ring of eight tiles the water can never cross, which is exactly what +## this mode builds. When every ring tile clears the bar the side emits +## `wall_closed` and the landscaper drops to (b). +## (b) TERRAFORM, from `terraform_start_round`: raise the lowest lattice tile +## inside `lattice_radius` that is below `waterLevel(round + 250) + 1`, +## keeping Bowl of Chowder's checkerboard parity so units can still path. +## (c) ATTACK: a landscaper delivered next to an enemy building digs its wall +## down and then buries it. 50 dirt kills an HQ; a landscaper carries 25. +## +## Any landscaper on a tile that floods next round MOVES FIRST and digs second. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit, pathing, signals, lattice + +const + ModeWall = 0 + ModeTerraform = 1 + ModeAttack = 2 + +proc wallTileNeedsWork*(w: World, side: Side, l: Loc): bool = + let occupant = w.getRobot(l) + if occupant != nil and occupant.kind.isBuilding(): return false + w.getDirt(l) < w.wallTarget(side) + +proc wallComplete*(w: World, side: Side): bool = + if not side.hasHq: return false + for l in w.ringTiles(side.hqLoc): + if w.wallTileNeedsWork(side, l): return false + true + +proc minRingElevation*(w: World, side: Side): int = + if not side.hasHq: return 0 + result = high(int) + for l in w.ringTiles(side.hqLoc): + result = min(result, w.getDirt(l)) + if result == high(int): result = 0 + +proc claimWallTile(w: World, side: Side, r: Robot): (bool, Loc) = + ## The ring tile this landscaper works on. A tile it is ALREADY STANDING ON + ## or standing NEXT TO wins outright, because a landscaper that has raised + ## its own tile to the bar is usually eight elevation steps above everything + ## around it and can no longer walk anywhere — but it is still adjacent to + ## the two neighbouring ring tiles, and it can deposit onto them from where + ## it stands. Preferring the globally lowest tile instead is what left three + ## ring tiles at elevation 3 while one landscaper piled 235 dirt onto its own. + if not side.hasHq: return (false, loc(0, 0)) + var best = loc(0, 0) + var bestScore = high(int) + var found = false + for l in w.ringTiles(side.hqLoc): + if not r.spend(1): break + if not w.wallTileNeedsWork(side, l): continue + ## A ring tile with a UNIT standing on it is still claimable: dirt dropped + ## on a non-building goes onto the GROUND, so a miner in the way does not + ## stop the wall going up under it. Only a building blocks, and + ## `wallTileNeedsWork` has already excluded those. + let reach = + if r.loc == l: 0 + elif r.loc.isAdjacentTo(l): 1 + else: 2 + let score = reach * 1_000_000 + w.getDirt(l) * 64 + + r.loc.distanceSquaredTo(l) + if score < bestScore: + bestScore = score + best = l + found = true + (found, best) + +proc depositToward(w: World, r: Robot, target: Loc): bool = + let d = fromDelta(target.x - r.loc.x, target.y - r.loc.y) + if not w.canDepositDirt(r, d): return false + w.depositDirt(r, d) + true + +proc digFrom(w: World, side: Side, r: Robot, protect: openArray[Loc]): bool = + let d = w.digSourceFor(side, r, protect) + if d == dCenter: return false + if not w.canDigDirt(r, d): return false + w.digDirt(r, d) + true + +proc runWallMode(w: World, side: Side, r: Robot): bool = + let (haveTile, tile) = w.claimWallTile(side, r) + if not haveTile: return false + if r.loc == tile: + if r.dirtCarrying > 0 and w.wallTileNeedsWork(side, tile): + return w.depositToward(r, r.loc) + var protect = w.ringTiles(side.hqLoc) + protect.add(side.hqLoc) + return w.digFrom(side, r, protect) + if r.loc.isAdjacentTo(tile): + ## Standing next to an unclaimed ring tile: raise it from here — this is + ## also how a FLOODED ring tile is resurfaced, since `addDirt` runs + ## `tryResurface` the moment the elevation clears the water. + if r.dirtCarrying > 0: + if w.depositToward(r, tile): return true + var protect = w.ringTiles(side.hqLoc) + protect.add(side.hqLoc) + if w.digFrom(side, r, protect): return true + ## Nothing to dig here — step on if we can. + return w.stepToward(side, r, tile) + w.stepToward(side, r, tile) + +proc runTerraformMode(w: World, side: Side, r: Robot): bool = + let (haveTile, tile) = w.lowestLatticeTileNear(side, r) + if not haveTile: + if side.hasHq and chebyshev(r.loc, side.hqLoc) > side.doctrine.latticeRadius: + return w.stepToward(side, r, side.hqLoc) + return false + if r.loc.isAdjacentTo(tile) or r.loc == tile: + if r.dirtCarrying > 0: + if w.depositToward(r, tile): return true + var protect = w.ringTiles(side.hqLoc) + protect.add(side.hqLoc) + protect.add(tile) + return w.digFrom(side, r, protect) + w.stepToward(side, r, tile) + +proc adjacentEnemyBuilding(w: World, side: Side, r: Robot): (bool, Loc) = + for d in MoveDirs: + if not r.spend(1): break + let l = r.loc + d + let occupant = w.getRobot(l) + if occupant == nil: continue + if occupant.team == side.team or occupant.team == teamNeutral: continue + if not occupant.kind.isBuilding(): continue + return (true, l) + (false, loc(0, 0)) + +proc runAttackMode(w: World, side: Side, r: Robot): bool = + let (haveTarget, target) = w.adjacentEnemyBuilding(side, r) + if haveTarget: + if r.dirtCarrying > 0: + return w.depositToward(r, target) + ## Nothing carried: take dirt from the ground beside the enemy wall. + return w.digFrom(side, r, [target]) + if not side.hasEnemyHq: return false + ## The enemy HQ's own ring is walled: dig it back down on the way in. + if chebyshev(r.loc, side.enemyHqLoc) <= 2 and r.dirtCarrying < + RobotSpecs[rtLandscaper].dirtLimit: + if w.digFrom(side, r, [side.enemyHqLoc]): return true + w.stepToward(side, r, side.enemyHqLoc) + +proc chooseMode(w: World, side: Side, r: Robot, brain: Brain): int = + let d = side.doctrine + if brain.mode == ModeAttack: return ModeAttack + let rushing = + d.rushTrigger > 0 and w.currentRound >= d.rushTrigger and side.hasEnemyHq + if rushing: + ## With `opening = rush` half the roster commits; with any other opening + ## the knob sends a SINGLE harassing landscaper (§Decisions). + let commit = + if d.opening == opRush: (r.id and 1) == 0 + else: side.rushUnits == 0 + if commit: + side.rushUnits += 1 + if not side.rushLaunched: + side.rushLaunched = true + w.emit("rush_launched", ord(side.team), side.rushUnits, w.currentRound) + return ModeAttack + if d.wallHqRound > 0 and w.currentRound >= w.effectiveWallRound(side) and + not w.wallComplete(side): + return ModeWall + if w.currentRound >= d.terraformStartRound: + return ModeTerraform + if d.wallHqRound > 0 and not w.wallComplete(side): + return ModeWall + ModeTerraform + +proc runLandscaper*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + w.readBlocks(side, r) + w.noteEnemyHq(side) + if w.fleeWater(side, r): return + if r.dead: return + if not isReady(r): return + + let mode = w.chooseMode(side, r, brain) + brain.mode = mode + var acted = false + case mode + of ModeAttack: acted = w.runAttackMode(side, r) + of ModeWall: acted = w.runWallMode(side, r) + else: acted = w.runTerraformMode(side, r) + if r.dead: return + + ## The wall is a team fact, so it is announced once, by whoever closes it. + if not side.wallClosed and side.doctrine.wallHqRound > 0 and + w.wallComplete(side): + side.wallClosed = true + side.wallClosedRound = w.currentRound + w.emit("wall_closed", ord(side.team), w.minRingElevation(side), + w.currentRound) + w.broadcast(side, r, SigWallClosed, w.minRingElevation(side)) + + if not acted and isReady(r) and mode != ModeAttack: + ## An idle landscaper tops up the tile it stands on — but ONLY up to the + ## bar. Without the cap a landscaper that had finished its wall tile and + ## could no longer walk anywhere (it is eight steps above its neighbours) + ## dug and dropped for the rest of the match and built a 235-high spike. + let cap = max(w.wallTarget(side), side.latticeTarget(w.currentRound)) + if r.dirtCarrying > 0 and w.getDirt(r.loc) < cap: + discard w.depositToward(r, r.loc) + elif r.dirtCarrying < RobotSpecs[rtLandscaper].dirtLimit and + w.getDirt(r.loc) < cap: + var protect = w.ringTiles(side.hqLoc) + protect.add(side.hqLoc) + discard w.digFrom(side, r, protect) diff --git a/src/battlecode/years/bc20/chassis/lattice.nim b/src/battlecode/years/bc20/chassis/lattice.nim new file mode 100644 index 0000000..5700e08 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/lattice.nim @@ -0,0 +1,71 @@ +## The lattice: which tiles inside `lattice_radius` of the own HQ are raised, +## and to what height. +## +## Bowl of Chowder's checkerboard parity is kept: only tiles whose `(x + y)` is +## EVEN are raised, so the odd tiles stay at ground level and units can always +## path through a finished lattice instead of being walled in by their own +## terraforming. The bar is `waterLevel(round + 250) + 1`, i.e. the lattice is +## always being built for the flood a quarter of a game ahead. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit + +func isLatticeTile*(l: Loc): bool = ((l.x + l.y) and 1) == 0 + +func isWallRing*(side: Side, l: Loc): bool = + side.hasHq and chebyshev(l, side.hqLoc) == 1 + +proc latticeNeedsWork*(w: World, side: Side, l: Loc): bool = + if not side.hasHq: return false + if not l.isLatticeTile(): return false + if chebyshev(l, side.hqLoc) > side.doctrine.latticeRadius: return false + if l == side.hqLoc: return false + if side.isWallRing(l): return false + let occupant = w.getRobot(l) + if occupant != nil and occupant.kind.isBuilding(): return false + w.getDirt(l) < side.latticeTarget(w.currentRound) + +proc lowestLatticeTileNear*(w: World, side: Side, r: Robot): (bool, Loc) = + ## The lowest tile within the robot's own sense window that the lattice still + ## wants raised. Charged per tile examined. + var best = loc(0, 0) + var bestElev = high(int) + var found = false + for l in w.sensed(r): + if not w.latticeNeedsWork(side, l): continue + let e = w.getDirt(l) + if e < bestElev or (e == bestElev and found and + l.distanceSquaredTo(r.loc) < best.distanceSquaredTo(r.loc)): + bestElev = e + best = l + found = true + (found, best) + +proc digSourceFor*(w: World, side: Side, r: Robot, + protect: openArray[Loc]): Dir = + ## A tile adjacent to the landscaper that dirt may be taken FROM: on the map, + ## never the HQ ring, never a lattice tile the plan wants raised, never a + ## building without dirt on it, and preferring the HIGHEST tile so the dig + ## does not open a new flood path. + var bestDir = dCenter + var bestElev = low(int) + for d in MoveDirs: + if not r.spend(1): break + let candidate = r.loc + d + if not w.onTheMap(candidate): continue + var skip = false + for p in protect: + if p == candidate: skip = true + if skip: continue + if side.isWallRing(candidate): continue + if w.latticeNeedsWork(side, candidate): continue + let occupant = w.getRobot(candidate) + if occupant != nil and occupant.kind.isBuilding() and + occupant.dirtCarrying <= 0: continue + if not w.canDigDirt(r, d): continue + let e = w.getDirt(candidate) + if e > bestElev: + bestElev = e + bestDir = d + bestDir diff --git a/src/battlecode/years/bc20/chassis/miner.nim b/src/battlecode/years/bc20/chassis/miner.nim new file mode 100644 index 0000000..52f4259 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/miner.nim @@ -0,0 +1,195 @@ +## The miner: mine, refine, and — for the ONE elected builder — put up the +## team's buildings in the doctrine's own order. +## +## The builder is the lowest-id living miner, so the election is stable, free +## and needs no signalling. It builds, in this order and only when the team +## pool can afford it without stalling miner production: +## 1 Design School at Chebyshev 2 from the HQ, on the side away from the water +## `net_gun_ring` Net Guns, also at Chebyshev 2 (a net gun ON the HQ ring +## would be buried by our own wall — see docs/RULES-BC20.md) +## 1 Fulfillment Center +## `vaporator_budget` Vaporators inside the lattice +## a second Design School after round 600 +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit, pathing, signals, lattice + +const + BuildReserve = 70 ## never spend the last miner out of the pool + SoupTipThreshold = 200 + +proc electBuilder(w: World, side: Side) = + if side.builderId >= 0 and side.builderId in w.robotsById: + let existing = w.robotsById[side.builderId] + if existing.kind == rtMiner and existing.team == side.team: + return + var best = high(int) + for id, r in w.robotsById: + if r.team == side.team and r.kind == rtMiner and id < best: + best = id + side.builderId = if best == high(int): -1 else: best + +proc nextBuilding(w: World, side: Side): (bool, RobotKind) = + let d = side.doctrine + if w.alive(side, rtDesignSchool) < 1: return (true, rtDesignSchool) + if w.alive(side, rtNetGun) < d.netGunRing: return (true, rtNetGun) + if w.alive(side, rtFulfillmentCenter) < 1: return (true, rtFulfillmentCenter) + ## One Refinery once the near seam is worked out: a miner that has to walk + ## the whole way back to the HQ spends most of the match in transit, and a + ## starved pool is what stops the Design School making landscapers. + if w.currentRound >= 250 and w.alive(side, rtRefinery) < 1: + return (true, rtRefinery) + if w.alive(side, rtVaporator) < d.vaporatorBudget: return (true, rtVaporator) + if w.currentRound >= 600 and w.alive(side, rtDesignSchool) < 2: + return (true, rtDesignSchool) + (false, rtMiner) + +proc buildSiteScore(w: World, side: Side, kind: RobotKind, l: Loc): int = + ## Buildings go at Chebyshev 2 from the HQ: close enough to defend, far + ## enough that the HQ wall does not bury them. + if not side.hasHq: return low(int) + let ring = chebyshev(l, side.hqLoc) + if ring < 2: return low(int) + ## High ground first: a building cannot be raised (dirt dropped on a + ## building buries it), so the elevation it is founded on is the elevation it + ## drowns at. + var score = 100 - abs(ring - 2) * 20 + w.getDirt(l) * 12 + if w.willFloodNextRound(l): score -= 500 + if kind == rtVaporator and not l.isLatticeTile(): score -= 10 + score + +proc tryBuild(w: World, side: Side, r: Robot, kind: RobotKind): bool = + ## The FIRST Design School is survival, not economy: it is bought with the + ## last credit in the pool. Everything after it leaves the miners a reserve. + let reserve = + if kind == rtDesignSchool and w.alive(side, rtDesignSchool) == 0: 0 + else: BuildReserve + if w.stats.soup[ord(side.team)] < RobotSpecs[kind].cost + reserve: + return false + var bestDir = dCenter + var bestScore = low(int) + for d in MoveDirs: + if not r.spend(1): break + if not w.canBuildRobot(r, kind, d): continue + let score = w.buildSiteScore(side, kind, r.loc + d) + if score > bestScore: + bestScore = score + bestDir = d + if bestDir == dCenter or bestScore == low(int): return false + if w.buildRobot(r, kind, bestDir) < 0: return false + w.firstBuild(side, kind) + true + +proc nearestRefinery(w: World, r: Robot, side: Side): (bool, Loc) = + ## The HQ always refines; a refinery is a bonus. The HQ location is known + ## from the round-1 broadcast, so a miner never has to search for it. + var best = loc(0, 0) + var bestD = high(int) + var found = false + if side.hasHq: + best = side.hqLoc + bestD = r.loc.distanceSquaredTo(side.hqLoc) + found = true + for l in w.sensed(r): + let occupant = w.getRobot(l) + if occupant == nil: continue + if occupant.team != side.team: continue + if not occupant.kind.canRefine(): continue + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + best = l + found = true + (found, best) + +proc nearestSoup(w: World, r: Robot, side: Side): (bool, Loc) = + var best = loc(0, 0) + var bestD = high(int) + var found = false + for l in w.sensed(r): + if w.getSoup(l) <= 0: continue + ## A FLOODED soup tile is still mineable: `assertCanMineSoup` checks only + ## that the tile is on the map and carries soup, and a miner mines from an + ## ADJACENT tile. Skipping flooded soup left the whole `WateredDown` seam + ## — every soup tile within ten of the HQ is under water there — invisible, + ## and a team that mines nothing builds nothing and drowns on schedule. + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + best = l + found = true + if found: return (true, best) + ## Nothing in sight: the seam this miner worked last, then the chain. + let brain = side.brainFor(r) + if brain.hasSoupTip: + if w.getSoup(brain.soupTip) > 0: + return (true, brain.soupTip) + brain.hasSoupTip = false + for tip in side.soupTips: + if not r.spend(1): break + let d = r.loc.distanceSquaredTo(tip) + if d < bestD: + bestD = d + best = tip + found = true + (found, best) + +proc runMiner*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + w.readBlocks(side, r) + w.noteEnemyHq(side) + if w.fleeWater(side, r): return + if r.dead: return + + w.electBuilder(side) + if r.id == side.builderId: + let (wanted, kind) = w.nextBuilding(side) + if wanted and isReady(r): + if w.tryBuild(side, r, kind): + return + + let spec = RobotSpecs[rtMiner] + let full = r.soupCarrying >= spec.soupLimit + let (haveSoup, soupAt) = w.nearestSoup(r, side) + + if full or (r.soupCarrying > 0 and not haveSoup): + let (haveRefinery, refineryAt) = w.nearestRefinery(r, side) + if haveRefinery: + if r.loc.isAdjacentTo(refineryAt): + let d = fromDelta(refineryAt.x - r.loc.x, refineryAt.y - r.loc.y) + if w.canDepositSoup(r, d): + w.depositSoup(r, d, r.soupCarrying) + return + w.stepToward(side, r, refineryAt) + return + + if haveSoup: + if r.loc.isAdjacentTo(soupAt) or r.loc == soupAt: + let d = fromDelta(soupAt.x - r.loc.x, soupAt.y - r.loc.y) + if w.canMineSoup(r, d): + w.mineSoup(r, d) + brain.soupTip = soupAt + brain.hasSoupTip = w.getSoup(soupAt) > 0 + if not brain.announcedSoup and w.getSoup(soupAt) >= SoupTipThreshold: + brain.announcedSoup = true + w.broadcast(side, r, SigAnnounceSoup, soupAt.x, soupAt.y) + return + w.stepToward(side, r, soupAt) + return + + ## Nothing in sight and nothing on the chain: EXPLORE. A soup pool is 66 + ## tiles on 1024 on some maps, and a miner that only ever looks inside its + ## own 113-tile window and then walks home mines nothing at all for the whole + ## game — which is how a team ends up with no design school, no landscapers + ## and a drowned HQ. The target is a pure function of the robot's own RNG, so + ## the search stays deterministic. + if not brain.hasTarget or r.loc == brain.target or + w.isFlooded(brain.target) or brain.turnCount mod 60 == 0: + brain.target = loc(int(brain.rng.nextDouble() * float(w.width)), + int(brain.rng.nextDouble() * float(w.height))) + if not w.onTheMap(brain.target): + brain.target = side.hqLoc + brain.hasTarget = true + w.stepToward(side, r, brain.target) diff --git a/src/battlecode/years/bc20/chassis/netgun.nim b/src/battlecode/years/bc20/chassis/netgun.nim new file mode 100644 index 0000000..81378f4 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/netgun.nim @@ -0,0 +1,12 @@ +## The Net Gun: shoot the closest enemy drone within `r² <= 15`, preferring one +## that is CARRYING a unit — the carried unit dies with it if the drop would +## have been over water. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import kit, hq + +proc runNetGun*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + discard w.shootNearestDrone(side, r) diff --git a/src/battlecode/years/bc20/chassis/pathing.nim b/src/battlecode/years/bc20/chassis/pathing.nim new file mode 100644 index 0000000..bbedb46 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/pathing.nim @@ -0,0 +1,106 @@ +## Pathing: the bug-walk-with-BFS-window Bowl of Chowder used. +## +## A bounded BFS over the tiles this robot can actually SENSE (r² 24 or 35 by +## unit), falling back to a greedy wall-follow when the target is outside the +## window, with a six-tile no-repeat history to break oscillation. Every node +## expansion and every direction evaluation is charged against the robot's +## `DecisionOps` budget, so a pathing call can never blow the per-turn cost. +## +## Behaviour, not code, from `StoneT2000/Battlecode2020` (AGPL-3.0; see NOTICE). + +import std/tables +import kit + +const HistoryLen = 6 + +proc remember(brain: Brain, l: Loc) = + brain.history.add(l) + if brain.history.len > HistoryLen: + brain.history.delete(0) + +proc recentlyVisited(brain: Brain, l: Loc): bool = + for seen in brain.history: + if seen == l: return true + false + +proc passable*(w: World, r: Robot, fromLoc, toLoc: Loc): bool = + ## The move legality a path may plan through: on the map, unoccupied, within + ## the elevation step, and — for a ground unit — dry both now and next round. + if not w.onTheMap(toLoc): return false + if w.isLocationOccupied(toLoc): return false + if not r.kind.canFly(): + if w.getDirtDifference(fromLoc, toLoc) > MaxDirtDifference: return false + if w.isFlooded(toLoc): return false + true + +proc stepToward*(w: World, side: Side, r: Robot, target: Loc): bool + {.discardable.} = + ## One move toward `target`. Returns true when the robot moved. + if not isReady(r): return false + if r.loc == target: return false + let brain = side.brainFor(r) + var bestDir = dCenter + var bestScore = high(int) + var fallbackDir = dCenter + var fallbackScore = high(int) + for d in MoveDirs: + if not r.spend(1): break + let candidate = r.loc + d + if not w.passable(r, r.loc, candidate): continue + ## A ground unit never steps onto a tile the flood takes next round. + if not r.kind.canFly() and w.willFloodNextRound(candidate): continue + let score = candidate.distanceSquaredTo(target) + if score < fallbackScore: + fallbackScore = score + fallbackDir = d + if brain.recentlyVisited(candidate): continue + if score < bestScore: + bestScore = score + bestDir = d + var chosen = bestDir + if chosen == dCenter: chosen = fallbackDir + if chosen == dCenter: return false + ## Only step if it is an improvement, or if the history says we are stuck — + ## the wall-follow half of the bug walk. + let here = r.loc.distanceSquaredTo(target) + let there = (r.loc + chosen).distanceSquaredTo(target) + if there >= here and brain.history.len < HistoryLen: + brain.remember(r.loc) + w.move(r, chosen) + if r.dead: return false + brain.remember(r.loc) + true + +proc fleeWater*(w: World, side: Side, r: Robot): bool {.discardable.} = + ## Every ground unit leaves a tile that will flood next round, before it does + ## anything else. A drone ignores this. + if r.kind.canFly(): return false + if not w.willFloodNextRound(r.loc): return false + if not isReady(r): return false + var bestDir = dCenter + var bestElev = low(int) + for d in MoveDirs: + if not r.spend(1): break + let candidate = r.loc + d + if not w.passable(r, r.loc, candidate): continue + if w.willFloodNextRound(candidate): continue + let e = w.getDirt(candidate) + if e > bestElev: + bestElev = e + bestDir = d + if bestDir == dCenter: return false + w.move(r, bestDir) + true + +proc nearestOf*(w: World, r: Robot, candidates: openArray[Loc]): (bool, Loc) = + var best = loc(0, 0) + var bestD = high(int) + var found = false + for l in candidates: + if not r.spend(1): break + let d = r.loc.distanceSquaredTo(l) + if d < bestD: + bestD = d + best = l + found = true + (found, best) diff --git a/src/battlecode/years/bc20/chassis/scaffold.nim b/src/battlecode/years/bc20/chassis/scaffold.nim new file mode 100644 index 0000000..b0742a7 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/scaffold.nim @@ -0,0 +1,133 @@ +## `examplefuncsplayer` — the weak floor and the parity oracle's other side. +## +## Ported STATEMENT FOR STATEMENT from +## `battlecode20/example-bots/src/main/examplefuncsplayer/RobotPlayer.java` at +## the pinned commit. The fidelity is the point: `parity-oracle` runs the Java +## engine driving the real examplefuncsplayer against itself and diffs the +## trace row for row against this (docs/PARITY.md). **It may not gain +## behaviour.** A "helpful" addition here — walling, terraforming, choosing a +## better direction — would break the only test that proves the ported rule set +## is the same rule set. +## +## Two upstream facts that look like bugs and are load-bearing: +## +## * `tryBlockchain` builds `new int[10]`, and +## `assertCanSubmitTransaction` rejects anything whose length is not +## `BLOCKCHAIN_TRANSACTION_LENGTH = 7`. So `canSubmitTransaction` is always +## false and **examplefuncsplayer never mints a transaction in 2020**. The +## attempt is reproduced, and so is the refusal. +## * `randomDirection()` calls `Math.random()`, which is wall-clock seeded, so +## the stock bot is not reproducible even against itself. The CI oracle runs +## a one-hunk patch of that file that replaces the single live call site +## with a per-robot `new java.util.Random(rc.getID())`; this port reproduces +## exactly that stream through `rng.nim`. + +import kit + +const + Directions = [dNorth, dEast, dSouth, dWest] + ## `RobotPlayer.directions`, in its own order — the random index means the + ## order is part of the behaviour. + ScaffoldMessageLength = 10 + ## `new int[10]`. Kept as a named constant so the length refusal below is + ## obviously the upstream file's and not a typo here. + ScaffoldMessageValue = 123 + ScaffoldMessageCost = 10 + +proc randomDirection(brain: Brain): Dir = + Directions[int(brain.rng.nextDouble() * float(Directions.len))] + +proc tryMove(w: World, r: Robot, d: Dir): bool {.discardable.} = + if isReady(r) and w.canMove(r, d): + w.move(r, d) + return true + false + +proc tryBuild(w: World, r: Robot, kind: RobotKind, d: Dir): bool + {.discardable.} = + if isReady(r) and w.canBuildRobot(r, kind, d): + w.buildRobot(r, kind, d) + return true + false + +proc tryMine(w: World, r: Robot, d: Dir): bool {.discardable.} = + if isReady(r) and w.canMineSoup(r, d): + w.mineSoup(r, d) + return true + false + +proc tryDepositSoup(w: World, r: Robot, d: Dir): bool {.discardable.} = + if isReady(r) and w.canDepositSoup(r, d): + w.depositSoup(r, d, r.soupCarrying) + return true + false + +proc tryBlockchain(w: World, r: Robot, brain: Brain) = + ## `turnCount < 3`, i.e. the robot's first two turns. The message is ten ints + ## of 123 and the engine's own length check refuses it, so nothing is ever + ## submitted — reproduced, not corrected. + if brain.turnCount >= 3: return + if ScaffoldMessageLength != TransactionLength: return + var message: array[TransactionLength, int] + for i in 0 ..< TransactionLength: + message[i] = ScaffoldMessageValue + if w.canSubmitTransaction(r, ScaffoldMessageCost): + w.submitTransaction(r, message, ScaffoldMessageCost) + +proc runHq(w: World, r: Robot) = + for d in Directions: + if not r.spend(1): return + w.tryBuild(r, rtMiner, d) + +proc runMiner(w: World, side: Side, r: Robot, brain: Brain) = + w.tryBlockchain(r, brain) + ## BOTH `randomDirection()` draws are always consumed, because the second is + ## the argument of a `tryMove` inside an `if`. + let first = brain.randomDirection() + let second = brain.randomDirection() + w.tryMove(r, first) + w.tryMove(r, second) + for d in Directions: + if not r.spend(1): return + w.tryBuild(r, rtFulfillmentCenter, d) + for d in Directions: + if not r.spend(1): return + w.tryDepositSoup(r, d) + for d in Directions: + if not r.spend(1): return + w.tryMine(r, d) + +proc runFulfillmentCenter(w: World, r: Robot) = + for d in Directions: + if not r.spend(1): return + w.tryBuild(r, rtDeliveryDrone, d) + +proc runDeliveryDrone(w: World, side: Side, r: Robot, brain: Brain) = + if not r.holdingUnit: + ## `senseNearbyRobots(DELIVERY_DRONE_PICKUP_RADIUS_SQUARED, enemy)` returns + ## the window in `getAllLocationsWithinRadiusSquared` order — x then y — + ## and the bot takes `robots[0]`. + for l in w.locationsWithinRadiusSquared(r.loc, + DeliveryDronePickupRadiusSquared): + if not r.spend(1): return + let target = w.getRobot(l) + if target == nil: continue + if target.id == r.id: continue + if not w.canSenseLocation(r, l): continue + if target.team != side.team.other(): continue + if w.canPickUpUnit(r, target.id): + w.pickUpUnit(r, target.id) + return + else: + w.tryMove(r, brain.randomDirection()) + +proc runScaffold*(w: World, side: Side, r: Robot) = + let brain = side.brainFor(r) + brain.turnCount += 1 + case r.kind + of rtHq: w.runHq(r) + of rtMiner: w.runMiner(side, r, brain) + of rtFulfillmentCenter: w.runFulfillmentCenter(r) + of rtDeliveryDrone: w.runDeliveryDrone(side, r, brain) + of rtRefinery, rtVaporator, rtDesignSchool, rtLandscaper, rtNetGun, rtCow: + discard diff --git a/src/battlecode/years/bc20/chassis/signals.nim b/src/battlecode/years/bc20/chassis/signals.nim new file mode 100644 index 0000000..c7f0480 --- /dev/null +++ b/src/battlecode/years/bc20/chassis/signals.nim @@ -0,0 +1,76 @@ +## Signalling: the seven-int blockchain messages the chassis speaks, their fee +## model, and the plain-words table the endcard decodes them with. +## +## A message is `[SIGNAL_KEY, teamOrdinal, code, p0, p1, p2, p3]`, submitted at +## a fee of `max(1, teamSoup / 200)` and only when `teamSoup > 120`, so the +## blockchain never starves the build. +## +## `SIGNAL_KEY` is a FIXED CONSTANT SHARED BY BOTH TEAMS, and that is +## deliberate: it reproduces the year's famous meta-gaming vector (both teams +## read every block) and it is what lets the endcard decode both sides' +## traffic. A team that wants privacy has to pay for it in obscurity, exactly +## as in 2020. + +import kit + +const + SignalKey* = 0x50415041 ## "PAPA" — Bowl of Chowder's own convention + SigHqLocation* = 1 + SigAnnounceSoup* = 2 + SigWallIn* = 3 + SigHqUnderAttack* = 4 + SigNeedDrones* = 5 + SigRushNow* = 6 + SigWallClosed* = 7 + + SignalNames*: array[1 .. 7, string] = [ + "HQ_LOCATION", "ANNOUNCE_SOUP", "WALL_IN", "HQ_UNDER_ATTACK", + "NEED_DRONES", "RUSH_NOW", "WALL_CLOSED" + ] + +proc signalName*(code: int): string = + if code >= 1 and code <= 7: SignalNames[code] else: "CODE_" & $code + +proc fee(w: World, side: Side): int = + max(1, w.stats.soup[ord(side.team)] div 200) + +proc broadcast*(w: World, side: Side, r: Robot, code: int, + p0 = 0, p1 = 0, p2 = 0, p3 = 0): bool {.discardable.} = + ## Submitting a transaction is NOT an action: no cooldown, no `isReady` + ## check. It still costs soup, so it is gated on a healthy pool. + if w.stats.soup[ord(side.team)] <= 120: return false + let cost = fee(w, side) + let message: array[TransactionLength, int] = + [SignalKey, ord(side.team), code, p0, p1, p2, p3] + if not w.canSubmitTransaction(r, cost): return false + w.submitTransaction(r, message, cost) + true + +proc readBlocks*(w: World, side: Side, r: Robot) = + ## Every block minted since this side last looked. Charged one credit per + ## block read, exactly like a sense. + while side.nextBlockToRead < w.currentRound: + if not r.spend(1): return + let minted = w.getBlock(side.nextBlockToRead) + side.nextBlockToRead += 1 + for t in minted: + if t.message[0] != SignalKey: continue + if t.message[1] != ord(side.team): continue + case t.message[2] + of SigHqLocation: + side.hqLoc = loc(t.message[3], t.message[4]) + side.hasHq = true + of SigAnnounceSoup: + let tip = loc(t.message[3], t.message[4]) + var known = false + for existing in side.soupTips: + if existing == tip: known = true + if not known and side.soupTips.len < 8: + side.soupTips.add(tip) + of SigWallClosed: + if not side.wallClosed: + side.wallClosed = true + side.wallClosedRound = w.currentRound + of SigRushNow: + side.rushLaunched = true + else: discard diff --git a/src/battlecode/years/bc20/constants.nim b/src/battlecode/years/bc20/constants.nim new file mode 100644 index 0000000..542b1f7 --- /dev/null +++ b/src/battlecode/years/bc20/constants.nim @@ -0,0 +1,123 @@ +## Battlecode 2020 "Soup" gameplay constants -- GENERATED, do not edit. +## +## Source: github.com/battlecode/battlecode20 at commit `7618f6be7d12da39f2e6e25801e578f1fecfbd86`, +## files `common/GameConstants.java` and `common/RobotType.java`, read by +## `tools/gen_year_constants.py --year bc20`. `tests/test_bc20_constants.nim` +## regenerates this file and byte-diffs it, so an edit here fails the build +## instead of quietly changing the rules under a `GameVersion` that no +## longer describes them. +## +## The two derived functions `getWaterLevel`, `getSensorRadiusPollutionCoefficient` +## and `getCooldownPollutionCoefficient` are NOT constants and live in +## `flood.nim` and `pollution.nim`. + +const EngineCommit* = "7618f6be7d12da39f2e6e25801e578f1fecfbd86" + +type + RobotKind* = enum + rtHq = "HQ" + rtMiner = "MINER" + rtRefinery = "REFINERY" + rtVaporator = "VAPORATOR" + rtDesignSchool = "DESIGN_SCHOOL" + rtFulfillmentCenter = "FULFILLMENT_CENTER" + rtLandscaper = "LANDSCAPER" + rtDeliveryDrone = "DELIVERY_DRONE" + rtNetGun = "NET_GUN" + rtCow = "COW" + + RobotSpec* = object + ## `common/RobotType.java`'s constructor arguments, verbatim. + cost*, dirtLimit*, soupLimit*: int + actionCooldown*: float32 + sensorRadiusSquared*, pollutionRadiusSquared*: int + localPollutionAdditiveEffect*: int + localPollutionMultiplicativeEffect*: float32 + globalPollutionAmount*, maxSoupProduced*, bytecodeLimit*: int + decisionOps*: int + +const + SpecVersion*: string = "1.0" + MapMinHeight*: int = 32 + MapMaxHeight*: int = 64 + MapMinWidth*: int = 32 + MapMaxWidth*: int = 64 + MinWaterElevation*: int = -1073741824 + NumberOfIndicatorStrings*: int = 3 + ExceptionBytecodePenalty*: int = 500 + MaxRobotId*: int = 32000 + InitialSoup*: int = 200 + BaseIncomePerRound*: int = 1 + SoupMiningRate*: int = 7 + MaxDirtDifference*: int = 3 + DeliveryDronePickupRadiusSquared*: int = 3 + NetGunShootRadiusSquared*: int = 15 + BlockchainTransactionLength*: int = 7 + NumberOfTransactionsPerBlock*: int = 7 + InitialCooldownTurns*: int = 10 + GameDefaultSeed*: int = 6370 + GameMaxNumberOfRounds*: int = 10000 + + RobotSpecs*: array[RobotKind, RobotSpec] = [ + rtHq: RobotSpec(cost: 0, dirtLimit: 50, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 48, + pollutionRadiusSquared: 35, localPollutionAdditiveEffect: 500, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 1, maxSoupProduced: 20, + bytecodeLimit: 20000, decisionOps: 2000), + rtMiner: RobotSpec(cost: 70, dirtLimit: 0, soupLimit: 100, + actionCooldown: 1'f32, sensorRadiusSquared: 35, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 10000, decisionOps: 1000), + rtRefinery: RobotSpec(cost: 200, dirtLimit: 15, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 35, localPollutionAdditiveEffect: 500, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 1, maxSoupProduced: 20, + bytecodeLimit: 5000, decisionOps: 500), + rtVaporator: RobotSpec(cost: 500, dirtLimit: 15, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 35, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 0.80'f32, + globalPollutionAmount: -1, maxSoupProduced: 2, + bytecodeLimit: 5000, decisionOps: 500), + rtDesignSchool: RobotSpec(cost: 150, dirtLimit: 15, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 5000, decisionOps: 500), + rtFulfillmentCenter: RobotSpec(cost: 150, dirtLimit: 15, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 5000, decisionOps: 500), + rtLandscaper: RobotSpec(cost: 150, dirtLimit: 25, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 10000, decisionOps: 1000), + rtDeliveryDrone: RobotSpec(cost: 150, dirtLimit: 0, soupLimit: 0, + actionCooldown: 1.5'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 10000, decisionOps: 1000), + rtNetGun: RobotSpec(cost: 250, dirtLimit: 15, soupLimit: 0, + actionCooldown: 1'f32, sensorRadiusSquared: 24, + pollutionRadiusSquared: 0, localPollutionAdditiveEffect: 0, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 7000, decisionOps: 700), + rtCow: RobotSpec(cost: 0, dirtLimit: 0, soupLimit: 0, + actionCooldown: 2'f32, sensorRadiusSquared: 10000, + pollutionRadiusSquared: 15, localPollutionAdditiveEffect: 2000, + localPollutionMultiplicativeEffect: 1'f32, + globalPollutionAmount: 0, maxSoupProduced: 0, + bytecodeLimit: 0, decisionOps: 0), + ] + diff --git a/src/battlecode/years/bc20/cows.nim b/src/battlecode/years/bc20/cows.nim new file mode 100644 index 0000000..45ac7b5 --- /dev/null +++ b/src/battlecode/years/bc20/cows.nim @@ -0,0 +1,75 @@ +## The NPC cows: `world/control/CowControlProvider.java`, ported exactly. +## +## A cow's RNG is `new Random(84307 * mapSeed + 20201 * (cowId / 2))`, created +## LAZILY on the cow's first turn. Each turn: +## +## * if the cow is ready, draw up to FOUR directions as +## `DIRECTIONS[floor(nextDouble() * 8)]`, reversing the direction when the +## cow's id is odd, and move on the first draw that is legal AND whose +## destination is not flooded — STOPPING THE DRAW LOOP the moment it moves; +## * if the cow is not ready, burn exactly four `nextDouble()` calls and do +## nothing. +## +## The reversal uses the map symmetry, which the engine recomputes on every +## NEUTRAL spawn. Cows only exist at map load, so the value is fixed for the +## match — but it is still read from the world, not from the map file, because +## that is where the engine reads it. + +import ../../rng +import world + +const CowDirections* = MoveDirs + ## `CowControlProvider.DIRECTIONS`: N, NE, E, SE, S, SW, W, NW. + +proc reverseDirection*(w: World, d: Dir): Dir = + case w.symmetry + of symHorizontal: + case d + of dNortheast: dSoutheast + of dSoutheast: dNortheast + of dNorth: dSouth + of dSouth: dNorth + of dNorthwest: dSouthwest + of dSouthwest: dNorthwest + else: d + of symVertical: + case d + of dNortheast: dNorthwest + of dSoutheast: dSouthwest + of dNorthwest: dNortheast + of dSouthwest: dSoutheast + of dWest: dEast + of dEast: dWest + else: d + of symRotational: + d.opposite() + +proc cowSeed*(mapSeed, cowId: int): int = + ## `84307 * world.getMapSeed() + 20201 * (cow.getID() / 2)` is Java `int` + ## arithmetic and it OVERFLOWS on most map seeds (`84307 * 43223` alone is + ## 3 644 001 461). Java wraps; a checked Nim conversion would raise on + ## exactly the seeds that matter, so the product is taken in `uint32` and + ## reinterpreted, then sign-extended by `new Random(long)`. + let a = cast[uint32](int32(mapSeed)) * 84307'u32 + let b = cast[uint32](int32(cowId div 2)) * 20201'u32 + int(cast[int32](a + b)) + +proc runCow*(w: World, cow: Robot) = + if cow.id notin w.cowRand: + w.cowRand[cow.id] = initJavaRandom(cowSeed(w.map.randomSeed, cow.id)) + var rng = w.cowRand[cow.id] + var i = 4 + if isReady(cow): + while i > 0: + dec i + var d = CowDirections[int(rng.nextDouble() * 8.0)] + if cow.id mod 2 == 1: + d = w.reverseDirection(d) + if w.canMove(cow, d) and not w.isFlooded(cow.loc + d): + w.move(cow, d) + break + else: + while i > 0: + dec i + discard rng.nextDouble() + w.cowRand[cow.id] = rng diff --git a/src/battlecode/years/bc20/flood.nim b/src/battlecode/years/bc20/flood.nim new file mode 100644 index 0000000..07cf9a8 --- /dev/null +++ b/src/battlecode/years/bc20/flood.nim @@ -0,0 +1,103 @@ +## The rising water: the committed level table, the one-ring-per-round flood +## fill, and drowning. +## +## THE WATER LEVEL IS A COMMITTED TABLE, NOT A FORMULA. +## `GameConstants.getWaterLevel` is +## +## (float)(Math.exp(0.0028x - 1.38*Math.sin(0.00157x - 1.73) +## + 1.38*Math.sin(-1.73)) - 1) +## +## and `Math.exp` / `Math.sin` are HotSpot intrinsics that are not bit-identical +## to any libm this port would link natively or under emscripten. The function +## depends only on the round number, so `tools/JavaWaterLevels.java` emits every +## value in `data/bc20/water_levels.json` as a float32 BIT PATTERN under the CI +## JDK, the file is COMMITTED, and the `parity-oracle` job regenerates and +## byte-diffs it as a BLOCKING step. Bit-exact against Java by construction, on +## every platform, with no libm in the loop. +## +## `floodfill` snapshots the flooded set BEFORE it changes anything, which is +## what makes the flood advance exactly one ring per round. + +import std/[json, os, strutils] +import world + +const WaterTableMaxRound* = 1500 + +var + waterTable: seq[float32] + waterTableLoaded = false + +proc bc20DataRoot*(): string = + ## `/data` is where emscripten mounts the preloaded directory in the wasm + ## bundle; `data` is the repo layout the container and the tests use. + ## + ## `getAppDir()` is DELIBERATELY not a candidate: under emscripten it walks + ## `os.getApplAux`, whose `readlink("/proc/self/exe")` returns -1 and whose + ## next line is a `Natural` conversion that raises before anything is opened. + for candidate in ["data", "/data", "/workspace/battlecode/data"]: + if dirExists(candidate / "maps" / "bc20"): + return candidate + "data" + +proc loadWaterTable*() = + if waterTableLoaded: return + let path = bc20DataRoot() / "bc20" / "water_levels.json" + if not fileExists(path): + raise newException(IOError, + "the bc20 water-level table is missing: no " & path & + " (in the wasm bundle this means the preloaded data package did not " & + "mount). It is generated by tools/JavaWaterLevels.java and committed.") + let doc = parseJson(readFile(path)) + waterTable = @[] + for entry in doc["levels"]: + let bits = uint32(parseHexInt(entry.getStr())) + waterTable.add(cast[float32](bits)) + if waterTable.len != WaterTableMaxRound + 1: + raise newException(IOError, + "the bc20 water-level table has " & $waterTable.len & + " entries, expected " & $(WaterTableMaxRound + 1)) + waterTableLoaded = true + +proc waterLevelAt*(round: int): float32 = + ## `GameConstants.getWaterLevel(round)`, from the committed table. Rounds + ## past the table's end clamp to its last value; the bc20 variant caps at + ## 1500, and the chassis look ahead by at most 400 rounds, so the clamp is + ## only ever reached by a doctrine planning past the end of the game. + loadWaterTable() + if round <= 0: return waterTable[0] + waterTable[min(round, WaterTableMaxRound)] + +proc updateWaterLevel*(w: World) = + w.waterLevel = waterLevelAt(w.currentRound) + +proc floodfill*(w: World) = + ## `GameWorld.floodfill`. The origin set is SNAPSHOTTED first, so the flood + ## advances exactly one ring per round; `setFloodStatus` destroys any robot + ## standing on a newly flooded tile that cannot fly. + var origins: seq[int] + for i in 0 ..< w.flooded.len: + if w.flooded[i]: origins.add(i) + for i in origins: + let center = w.indexToLoc(i) + for d in AllDirs: + let target = center + d + if not w.onTheMap(target): continue + let j = w.idx(target) + if w.flooded[j]: continue + if float32(w.elevation[j]) >= w.waterLevel: continue + w.setFloodStatus(j, true) + +proc roundWaterReaches*(level: int): int = + ## The first round at which the water is strictly above `level`, i.e. the + ## round a tile at that elevation floods if it is next to water. Returns + ## `WaterTableMaxRound + 1` when the water never gets there inside the cap. + loadWaterTable() + for r in 0 .. WaterTableMaxRound: + if waterTable[r] > float32(level): + return r + WaterTableMaxRound + 1 + +proc floodStageFor*(level: float32): int = + ## Which integer elevation the water has just reached — the `flood_stage` + ## beat the viewer draws. 0 while the water is below elevation 1. + if level < 1.0'f32: 0 else: int(level) diff --git a/src/battlecode/years/bc20/knobs.nim b/src/battlecode/years/bc20/knobs.nim new file mode 100644 index 0000000..8975e56 --- /dev/null +++ b/src/battlecode/years/bc20/knobs.nim @@ -0,0 +1,178 @@ +## The Battlecode 2020 "Soup" knob table: TEN knobs, and NO `chassis` key. +## +## D1 (sibling review finding, 2026-09-03): the chassis is not an +## LLM-selectable knob. The chassis a seat drives comes from `PLAYER_SCRIPTED` +## (scripted seats) or is the fixed champion chassis (LLM seats). A submitted +## `chassis` is therefore recorded as an UNKNOWN FIELD and never honoured, and +## `tests/test_bc20_sheet.nim` asserts exactly that — the test fails if anyone +## re-adds the knob. +## +## Unknown key, wrong type or out-of-range value takes THAT FIELD'S DEFAULT and +## the repair is recorded. A sheet can never be rejected, so a cog can never +## forfeit a match by answering badly — only by answering weakly. + +import std/[json, tables] +import ../../sheet_common + +export sheet_common + +type + Opening* = enum + opRush = "rush" + opLattice = "lattice" + opPassiveLattice = "passive_lattice" + opTurtle = "turtle" + + CountCurve* = enum + ccLean = "lean" + ccSteady = "steady" + ccSwarm = "swarm" + + DroneRole* = enum + drHarass = "harass" + drWall = "wall" + drBuster = "buster" + drCarryLandscapers = "carry_landscapers" + + Doctrine20* = object + ## The ten knobs, each with a named site in the bc20 chassis + ## (docs/RULES-BC20.md §The doctrine sheet lists the site per knob). + opening*: Opening + terraformStartRound*: int + latticeRadius*: int + landscaperCountCurve*: CountCurve + minerCountCurve*: CountCurve + vaporatorBudget*: int + droneRole*: DroneRole + netGunRing*: int + rushTrigger*: int + wallHqRound*: int + +const + KnownKeys20* = [ + "opening", "terraform_start_round", "lattice_radius", + "landscaper_count_curve", "miner_count_curve", "vaporator_budget", + "drone_role", "net_gun_ring", "rush_trigger", "wall_hq_round" + ] + ## Exactly ten. `chassis` is deliberately NOT here (D1). + +proc defaultDoctrine20*(): Doctrine20 = + Doctrine20( + opening: opPassiveLattice, + terraformStartRound: 300, + latticeRadius: 6, + landscaperCountCurve: ccSteady, + minerCountCurve: ccSteady, + vaporatorBudget: 2, + droneRole: drHarass, + netGunRing: 2, + rushTrigger: 0, + wallHqRound: 250 + ) + +proc applyKnobs20*(seen: Table[string, JsonNode], + defaultsApplied: var seq[string]): Doctrine20 = + result = defaultDoctrine20() + + template repair(name: string) = + defaultsApplied.add(name) + + template enumKnob(name: string, field: untyped, T: typedesc) = + if name in seen: + if seen[name].kind == JString: + let text = normalizeKey(seen[name].getStr()) + var found = false + for value in T: + if normalizeKey($value) == text: + field = value + found = true + if not found: repair(name) + else: + repair(name) + + template intKnob(name: string, field: untyped, lo, hi: int) = + if name in seen: + let n = readNumber(seen[name]) + if n.ok and n.value >= float(lo) and n.value <= float(hi): + field = int(n.value) + else: + repair(name) + + enumKnob("opening", result.opening, Opening) + intKnob("terraform_start_round", result.terraformStartRound, 1, 1500) + intKnob("lattice_radius", result.latticeRadius, 2, 12) + enumKnob("landscaper_count_curve", result.landscaperCountCurve, CountCurve) + enumKnob("miner_count_curve", result.minerCountCurve, CountCurve) + intKnob("vaporator_budget", result.vaporatorBudget, 0, 6) + enumKnob("drone_role", result.droneRole, DroneRole) + intKnob("net_gun_ring", result.netGunRing, 0, 6) + intKnob("rush_trigger", result.rushTrigger, 0, 1500) + intKnob("wall_hq_round", result.wallHqRound, 0, 1500) + +proc toJson20*(d: Doctrine20): JsonNode = + %*{ + "opening": $d.opening, + "terraform_start_round": d.terraformStartRound, + "lattice_radius": d.latticeRadius, + "landscaper_count_curve": $d.landscaperCountCurve, + "miner_count_curve": $d.minerCountCurve, + "vaporator_budget": d.vaporatorBudget, + "drone_role": $d.droneRole, + "net_gun_ring": d.netGunRing, + "rush_trigger": d.rushTrigger, + "wall_hq_round": d.wallHqRound + } + +proc plainWords20*(d: Doctrine20): seq[string] = + ## The endcard/`#bc20-doctrines` readout: the sheet in words a spectator can + ## read without knowing the schema. + case d.opening + of opRush: result.add("rushes the enemy HQ") + of opLattice: result.add("lattices outward") + of opPassiveLattice: result.add("lattices inward first") + of opTurtle: result.add("turtles") + if d.rushTrigger > 0: + result.add("commits at round " & $d.rushTrigger) + else: + result.add("never commits a rush") + if d.wallHqRound > 0: + result.add("walls at " & $d.wallHqRound) + else: + result.add("never walls its HQ") + result.add("terraforms from " & $d.terraformStartRound) + result.add("lattice radius " & $d.latticeRadius) + result.add($d.landscaperCountCurve & " landscapers") + result.add($d.minerCountCurve & " miners") + if d.vaporatorBudget == 0: + result.add("no vaporators") + else: + result.add($d.vaporatorBudget & " vaporators") + case d.droneRole + of drHarass: result.add("drones harass") + of drWall: result.add("drones wall") + of drBuster: result.add("drones bust walls") + of drCarryLandscapers: result.add("drones ferry landscapers") + if d.netGunRing == 0: + result.add("no net guns") + else: + result.add($d.netGunRing & " net guns") + +proc landscaperTarget*(d: Doctrine20, round: int): int = + ## `hq.nim` / `designschool.nim`: `4 + round/220`, scaled 0.6 / 1.0 / 1.7 by + ## `landscaper_count_curve` and capped at 40. + let base = 4.0 + float(round) / 220.0 + let scale = case d.landscaperCountCurve + of ccLean: 0.6 + of ccSteady: 1.0 + of ccSwarm: 1.7 + min(40, int(base * scale)) + +proc minerTarget*(d: Doctrine20, round: int): int = + ## `hq.nim`: `6 + round/300`, scaled 0.6 / 1.0 / 1.7 by `miner_count_curve` + ## and capped at 25. + let base = 6.0 + float(round) / 300.0 + let scale = case d.minerCountCurve + of ccLean: 0.6 + of ccSteady: 1.0 + of ccSwarm: 1.7 + min(25, max(1, int(base * scale))) diff --git a/src/battlecode/years/bc20/maps.nim b/src/battlecode/years/bc20/maps.nim new file mode 100644 index 0000000..4bb64fd --- /dev/null +++ b/src/battlecode/years/bc20/maps.nim @@ -0,0 +1,155 @@ +## The converted bc20 map pool, the loader and the per-episode draw. +## +## Maps are read from `data/maps/bc20/.json`, produced by +## `tools/convert_maps_bc20.py` from the official `.map20` files at the pinned +## battlecode20 commit and COMMITTED (CI re-converts and diffs). The wasm +## bundle gets the same directory through emscripten's `--preload-file +## data@data`, so the browser re-derives from exactly the bytes the server +## played. +## +## 18 of the 52 official maps are converted. `CowFarm` and +## `DidAMonkeyMakeThis` are deliberately excluded: both carry tiles at +## `Integer.MAX_VALUE/2` elevation, which is legal but makes the elevation +## shading meaningless and the timing untypical (docs/RULES-BC20.md). + +import std/[json, os, strutils] +import ../../sim_types +import world, flood + +const + SmallPool* = ["maptestsmall", "WateredDown", "Infinity", "Spiral", + "Hourglass", "ALandDivided"] + MixedPool* = ["maptestsmall", "WateredDown", "Infinity", "Spiral", + "Hourglass", "ALandDivided", "Climb", "Constriction", + "CentralLake", "Toothpaste", "TwoLakeLand", "CentralSoup"] + LargePool* = ["Hills", "OmgThisIsProcedural", "Squares", + "BeachFrontProperty", "Maze", "TheHighGround"] + +proc poolNames*(pool: string): seq[string] = + case pool.toLowerAscii() + of "small": @SmallPool + of "large": @LargePool + of "mixed", "": @MixedPool + else: @[] + +proc dataRoot*(): string = bc20DataRoot() + +proc parseSymmetry(text: string): Symmetry = + case text + of "vertical": symVertical + of "horizontal": symHorizontal + else: symRotational + +proc parseKind(text: string): RobotKind = + case text + of "hq": rtHq + of "miner": rtMiner + of "refinery": rtRefinery + of "vaporator": rtVaporator + of "design_school": rtDesignSchool + of "fulfillment_center": rtFulfillmentCenter + of "landscaper": rtLandscaper + of "delivery_drone": rtDeliveryDrone + of "net_gun": rtNetGun + else: rtCow + +proc parseTeam(text: string): Team = + case text + of "A": teamA + of "B": teamB + else: teamNeutral + +proc parseMapSpec*(text: string): MapSpec = + let doc = parseJson(text) + result.name = doc["name"].getStr() + result.width = doc["width"].getInt() + result.height = doc["height"].getInt() + result.symmetry = parseSymmetry(doc["symmetry"].getStr()) + result.randomSeed = doc["random_seed"].getInt() + result.initialWater = doc["initial_water"].getInt() + for v in doc["elevation"]: result.elevation.add(v.getInt()) + for v in doc["water"]: result.water.add(v.getBool()) + for v in doc["pollution"]: result.pollution.add(v.getInt()) + for v in doc["soup"]: result.soup.add(v.getInt()) + for b in doc["initial_bodies"]: + result.initialBodies.add(InitialBody( + id: b["id"].getInt(), + team: parseTeam(b["team"].getStr()), + kind: parseKind(b["type"].getStr()), + loc: loc(b["x"].getInt(), b["y"].getInt()))) + let expected = result.width * result.height + if result.elevation.len != expected or result.water.len != expected or + result.soup.len != expected or result.pollution.len != expected: + raise newException(ConfigError, + "bc20 map " & result.name & " has inconsistent tile arrays") + +proc mapPath*(name: string): string = + dataRoot() / "maps" / "bc20" / (name & ".json") + +proc loadMap*(name: string): MapSpec = + let path = mapPath(name) + if not fileExists(path): + raise newException(ConfigError, "no converted bc20 map at " & path) + parseMapSpec(readFile(path)) + +proc drawMaps*(pool: string, seed, count: int): seq[string] = + ## `count` DISTINCT maps from the pool, chosen by successive seed-derived + ## indices. Deterministic in the episode seed and recorded in the results, so + ## a spectator can re-run the same three games. Identical in shape to bc26's + ## draw, so the two years rank the same way for the same seed. + let names = poolNames(pool) + var remaining = names + var s = uint32(seed) xor 0x9E3779B9'u32 + for i in 0 ..< min(count, remaining.len): + s = s * 1664525'u32 + 1013904223'u32 + let pick = int(s shr 16) mod remaining.len + result.add(remaining[pick]) + remaining.delete(pick) + +proc sideAslotFor*(seed, gameIndex: int): int = + ## `(seed shr 8) and 1` picks which SEAT takes side A in game 1; sides + ## alternate every game after that. + ((seed shr 8) and 1) xor (gameIndex and 1) + +proc hqLocations*(spec: MapSpec): array[2, Loc] = + for b in spec.initialBodies: + if b.kind == rtHq and b.team != teamNeutral: + result[ord(b.team)] = b.loc + +proc mapCard*(spec: MapSpec, slot, sideAslot: int): JsonNode = + ## The per-map facts a seat may legitimately know before writing its + ## doctrine. Every map is symmetric, so both seats see numerically identical + ## cards; `you_are` is the only asymmetry. + let hqs = spec.hqLocations() + let side = if sideAslot == slot: 0 else: 1 + let mine = hqs[side] + var soupTiles = 0 + var soupTotal = 0 + var soupNearHq = 0 + for i, amount in spec.soup: + if amount > 0: + soupTiles += 1 + soupTotal += amount + let l = loc(i mod spec.width, i div spec.width) + if l.distanceSquaredTo(mine) <= 100: + soupNearHq += amount + var cows = 0 + for b in spec.initialBodies: + if b.kind == rtCow: cows += 1 + var floodedTiles = 0 + for wet in spec.water: + if wet: floodedTiles += 1 + %*{ + "map": spec.name, + "width": spec.width, + "height": spec.height, + "symmetry": ($spec.symmetry).replace("sym", "").toLowerAscii(), + "you_are": (if sideAslot == slot: "A" else: "B"), + "hq_elevation": spec.elevation[mine.x + spec.width * mine.y], + "hq_separation": chebyshev(hqs[0], hqs[1]), + "soup_tiles": soupTiles, + "soup_total": soupTotal, + "soup_near_hq": soupNearHq, + "cows": cows, + "initially_flooded_tiles": floodedTiles + } diff --git a/src/battlecode/years/bc20/pollution.nim b/src/battlecode/years/bc20/pollution.nim new file mode 100644 index 0000000..3e88229 --- /dev/null +++ b/src/battlecode/years/bc20/pollution.nim @@ -0,0 +1,35 @@ +## The two derived pollution coefficients, in Java `float`. +## +## `common/GameConstants.java`: +## +## getSensorRadiusPollutionCoefficient(p) = (float)(1.0 / Math.pow(1.0 + p/4000.0, 2)) +## getCooldownPollutionCoefficient(p) = (float)(1.0 + p/2000.0) +## +## Both are computed in `double` and narrowed to `float` on return, exactly as +## Java does; `Math.pow(x, 2)` is bit-identical to `x * x` over the whole +## integer domain `p in [0, 65535]` (checked against the JDK by the +## `parity-oracle` job's coefficient step, which is BLOCKING), so the closed +## form here needs no `pow` and therefore no libm. +## +## The per-robot local effect registry lives in `world.nim`, because it is +## world state; this module is pure arithmetic so `world.nim` can import it. + +import std/math + +func javaRoundF32*(x: float32): int = + ## `Math.round(float)` — `(int)floor(a + 0.5f)`, with Java's saturating + ## behaviour at the extremes. Used for the sensed radius and for the + ## pollution reading, both of which are `Math.round` of a `float` in the + ## engine. + if x != x: return 0 # NaN rounds to 0 in Java + let shifted = floor(float64(x) + 0.5) + if shifted >= 2147483647.0: return 2147483647 + if shifted <= -2147483648.0: return -2147483648 + int(shifted) + +func sensorCoefficient*(pollution: int): float32 = + let base = 1.0 + float64(pollution) / 4000.0 + float32(1.0 / (base * base)) + +func cooldownCoefficient*(pollution: int): float32 = + float32(1.0 + float64(pollution) / 2000.0) diff --git a/src/battlecode/years/bc20/rules.nim b/src/battlecode/years/bc20/rules.nim new file mode 100644 index 0000000..2bd6a0b --- /dev/null +++ b/src/battlecode/years/bc20/rules.nim @@ -0,0 +1,231 @@ +## The bc20 round loop, the game, and the outcome. +## +## `runRound` mirrors `GameWorld.runRound` step for step, and the step list IS +## the rules: a re-ordering is a rules change and bumps `GameVersion` +## (docs/RULES-BC20.md §The round loop). +## +## 1. round += 1, both teams gain BASE_INCOME_PER_ROUND +## 2. every robot's beginning-of-round (a no-op in 2020, kept as a named step +## because the hash chain and the parity trace are taken around it) +## 3. iterate the dynamic bodies in SPAWN order, skipping the already dead +## 4. a BLOCKED robot takes no turn and its cooldown does not decay — but its +## pollution effect is still reset +## 5. beginning of turn: cooldown decay, DecisionOps budget reset +## 6. run the controller (the team chassis, or the ported cow provider) +## 7. end of turn: clear pollution, refine, vaporate, re-install pollution +## 8. end of round: mint the block, raise the water, flood one ring, check +## the end-of-match ladder +## 9. append this round's state hash + +import std/[monotimes, strutils, times] +import ../../sim_types +import ../../sheet +import world, flood, cows, maps, knobs +import chassis/[kit, boc, scaffold] + +export world, flood, maps, knobs, kit + +type + ChassisKind* = enum + ckBowlOfChowder = "bowl-of-chowder" + ckExamplefuncsplayer = "examplefuncsplayer" + + GameOutcome20* = object + index*: int + mapName*: string + sideAslot*: int ## which SEAT plays team A this game + roundsPlayed*: int + winnerSlot*: int ## -1 = no winner recorded (abandoned) + endReason*: string + points*: array[2, int] ## BY SEAT + hashChain*: string + roundChains*: string + aborted*: bool + ## Per-game statistics, BY SEAT. These are the optional year-specific + ## siblings in `results.games[]`. + hqAlive*: array[2, bool] + hqLostRound*: array[2, int] + hqLostCause*: array[2, string] + soupMined*: array[2, int] + soupRefined*: array[2, int] + netWorth*: array[2, int] + unitsAlive*: array[2, int] + unitsBuilt*: array[2, int] + minersBuilt*: array[2, int] + landscapersBuilt*: array[2, int] + dronesBuilt*: array[2, int] + vaporatorsBuilt*: array[2, int] + netGunsBuilt*: array[2, int] + dirtMoved*: array[2, int] + dronePickups*: array[2, int] + droneWaterDrops*: array[2, int] + netGunKills*: array[2, int] + transactionsSent*: array[2, int] + transactionsMinted*: array[2, int] + blockchainSoupSpent*: array[2, int] + globalPollutionPeak*: int + floodedTilesEnd*: int + waterLevelEnd*: float + +proc parseChassisKind*(name: string): ChassisKind = + ## Anything unrecognised is `bowl-of-chowder`: a seat that says nothing + ## useful plays the strong published doctrine, not the deliberately weak + ## floor (§Decisions). + case name + of "examplefuncsplayer", "scaffold", "example": ckExamplefuncsplayer + else: ckBowlOfChowder + +proc slotOf*(outcome: GameOutcome20, team: Team): int = + if team == teamA: outcome.sideAslot else: 1 - outcome.sideAslot + +proc newSides*(sheets: array[2, Sheet], chassis: array[2, ChassisKind], + sideAslot: int): array[2, Side] = + ## `sides[ord(team)]`. Which SEAT is behind team A alternates per game. + result[0] = newSide(teamA, sheets[sideAslot].doctrine20) + result[1] = newSide(teamB, sheets[1 - sideAslot].doctrine20) + for t in 0 .. 1: + result[t].applyOpening() + +proc runControllerFor*(w: World, sides: array[2, Side], + chassis: array[2, ChassisKind], r: Robot) = + if r.kind == rtCow: + w.runCow(r) + return + if r.team == teamNeutral: return + let side = sides[ord(r.team)] + case chassis[ord(r.team)] + of ckBowlOfChowder: w.runBowlOfChowder(side, r) + of ckExamplefuncsplayer: w.runScaffold(side, r) + +proc runRound*(w: World, sides: array[2, Side], + chassis: array[2, ChassisKind]) = + w.processBeginningOfRound() + ## A SNAPSHOT of the exec order: bodies spawned this round do not act until + ## the next one, and bodies that die are skipped rather than compacted out + ## from under the iteration (`ObjectInfo.eachDynamicBodyByExecOrder`). + let order = w.execOrder + for id in order: + if id notin w.robotsById: continue + let r = w.robotsById[id] + if r.blocked: + ## Rule 4: no turn, no cooldown decay, but the pollution effect still + ## goes (`GameWorld.updateRobot`). + if r.kind.canAffectPollution(): + w.resetPollutionForRobot(r.id) + continue + w.processBeginningOfTurn(r) + if r.dead: continue + w.runControllerFor(sides, chassis, r) + if r.dead: continue + w.processEndOfTurn(r) + + ## Rule 8, in `GameWorld.processEndOfRound`'s own order. + w.processBlockchain() + let before = floodStageFor(w.waterLevel) + w.updateWaterLevel() + w.floodfill() + let after = floodStageFor(w.waterLevel) + if after > before: + w.emit("flood_stage", after, w.floodedCount, w.currentRound) + w.checkEndOfMatch() + if w.hasWinner: + w.running = false + + ## Rule 9: the per-round hash chain. Seven per-team values plus three + ## globals; a re-derivation that diverged only in one of them would + ## otherwise reproduce the chain and report no mismatch (the GV02 lesson). + for t in 0 .. 1: + w.mixHash(w.stats.soup[t]) + w.mixHash(w.stats.soupRefined[t]) + w.mixHash(w.robotCount[t]) + w.mixHash(w.netWorth(Team(t))) + w.mixHash(w.stats.dirtMoved[t]) + w.mixHash(w.stats.blockchainsSent[t]) + w.mixHash(if w.stats.destroyedHq[t]: 0 else: 1) + w.mixHash(w.globalPollution) + w.mixHash(w.floodedCount) + ## The water level enters the chain as its float32 BIT PATTERN, split into + ## two 16-bit halves: `int(uint32)` traps under wasm32, where `int` is 32 + ## bits and half the patterns do not fit. + let waterBits = cast[uint32](w.waterLevel) + w.mixHash(int(waterBits shr 16)) + w.mixHash(int(waterBits and 0xFFFF'u32)) + +# --------------------------------------------------------------------------- +# One game +# --------------------------------------------------------------------------- + +proc endReasonFor(w: World): string = + case w.domination + of dfNone: "quantity" + else: $w.domination + +proc harvest(w: World, outcome: var GameOutcome20) = + for team in [teamA, teamB]: + let t = ord(team) + let slot = outcome.slotOf(team) + outcome.hqAlive[slot] = not w.stats.destroyedHq[t] + outcome.hqLostRound[slot] = w.hqLostRound[t] + outcome.hqLostCause[slot] = w.hqLostCause[t] + outcome.soupMined[slot] = w.stats.soupMined[t] + outcome.soupRefined[slot] = w.stats.soupRefined[t] + outcome.netWorth[slot] = w.netWorth(team) + outcome.unitsAlive[slot] = w.robotCount[t] + outcome.unitsBuilt[slot] = w.stats.unitsBuilt[t] + outcome.minersBuilt[slot] = w.stats.minersBuilt[t] + outcome.landscapersBuilt[slot] = w.stats.landscapersBuilt[t] + outcome.dronesBuilt[slot] = w.stats.dronesBuilt[t] + outcome.vaporatorsBuilt[slot] = w.stats.vaporatorsBuilt[t] + outcome.netGunsBuilt[slot] = w.stats.netGunsBuilt[t] + outcome.dirtMoved[slot] = w.stats.dirtMoved[t] + outcome.dronePickups[slot] = w.stats.dronePickups[t] + outcome.droneWaterDrops[slot] = w.stats.droneWaterDrops[t] + outcome.netGunKills[slot] = w.stats.netGunKills[t] + outcome.transactionsSent[slot] = w.stats.transactionsSent[t] + outcome.transactionsMinted[slot] = w.stats.blockchainsSent[t] + outcome.blockchainSoupSpent[slot] = w.stats.blockchainSoupSpent[t] + outcome.globalPollutionPeak = w.globalPollutionPeak + outcome.floodedTilesEnd = w.floodedCount + outcome.waterLevelEnd = float(w.waterLevel) + let pts = w.gamePoints() + outcome.points[outcome.slotOf(teamA)] = pts[0] + outcome.points[outcome.slotOf(teamB)] = pts[1] + outcome.roundsPlayed = w.currentRound + outcome.hashChain = toHex(w.hashChain) + +proc playGame*( + spec: MapSpec, sheets: array[2, Sheet], chassis: array[2, ChassisKind], + index, sideAslot, maxRounds: int, budgetSeconds: int, + onRound: proc (w: World, round: int) {.closure.} = nil +): (World, GameOutcome20) = + ## Plays one game to its end, or abandons it when `budgetSeconds` of + ## monotonic wall clock elapse. An abandoned game is DISCARDED by the match + ## (its `aborted` flag says so); it is never scored half-played. + var w = newWorld(spec, maxRounds) + var sides = newSides(sheets, chassis, sideAslot) + ## `sides` is indexed by TEAM, and `chassis` arrives by SEAT — re-index it + ## once here so the round loop never has to. + let chassisByTeam = [chassis[sideAslot], chassis[1 - sideAslot]] + var outcome = GameOutcome20( + index: index, mapName: spec.name, sideAslot: sideAslot, winnerSlot: -1) + let started = getMonoTime() + let budget = initDuration(seconds = budgetSeconds) + while w.running and w.currentRound < maxRounds: + runRound(w, sides, chassisByTeam) + outcome.roundChains.add(toHex(w.hashChain)) + if onRound != nil: + onRound(w, w.currentRound) + if budgetSeconds > 0 and (w.currentRound and 0x1F) == 0 and + getMonoTime() - started >= budget: + outcome.aborted = true + break + if outcome.aborted: + outcome.endReason = "abandoned" + harvest(w, outcome) + outcome.winnerSlot = -1 + return (w, outcome) + outcome.endReason = w.endReasonFor() + harvest(w, outcome) + if w.hasWinner: + outcome.winnerSlot = outcome.slotOf(w.winner) + (w, outcome) diff --git a/src/battlecode/years/bc20/world.nim b/src/battlecode/years/bc20/world.nim new file mode 100644 index 0000000..d458211 --- /dev/null +++ b/src/battlecode/years/bc20/world.nim @@ -0,0 +1,998 @@ +## The Battlecode 2020 "Soup" world: state, geometry and every legality rule. +## +## A behaviour-for-behaviour port of `battlecode/world/GameWorld.java`, +## `world/InternalRobot.java`, `world/ObjectInfo.java`, `world/TeamInfo.java` +## and `world/RobotControllerImpl.java` at commit +## `7618f6be7d12da39f2e6e25801e578f1fecfbd86` (the last 2020 state), together +## with the pieces of `common/MapLocation.java` and `common/Direction.java` the +## rules depend on. The port is the authority at runtime; the Java engine +## survives only as the `parity-oracle` CI job (docs/PARITY.md). +## +## Four things in here look like details and are not: +## +## * **Exec order is SPAWN order, not id order.** `ObjectInfo.eachDynamicBodyByExecOrder` +## walks `dynamicBodyExecOrder`, an append-on-spawn / remove-on-death list. +## * **`float32` everywhere the engine says `float`.** `cooldownTurns`, the +## water level and both pollution coefficients are Java `float`s; widening +## them to `float64` changes which round a robot becomes ready. +## * **Blocked robots.** A unit held by a delivery drone takes no turn and its +## cooldown does NOT decay — but it still has its pollution effect reset. +## * **The transaction-id RNG is a Java `static` that the engine re-seeds with +## the map seed inside `new RobotControllerImpl(...)`** — i.e. on EVERY +## spawn, including the two HQs and every cow at map load. That quirk decides +## the minting order among equal-fee transactions and is reproduced exactly. + +import std/[math, tables] + +export tables +import ../../rng +import constants, pollution, blockchain + +export constants, pollution, blockchain + +type + Team* = enum + teamA = 0 + teamB = 1 + teamNeutral = 2 + + Dir* = enum + ## Ordinals match `common/Direction.java`. `allDirections()` is + ## `values()`, so it INCLUDES `CENTER` — which matters in `floodfill`, + ## where the centre tile is re-examined (and always skipped, being already + ## flooded). + dNorth = 0 + dNortheast + dEast + dSoutheast + dSouth + dSouthwest + dWest + dNorthwest + dCenter + + Symmetry* = enum + ## `CowControlProvider.MapSymmetry`, in the engine's own candidate order. + ## `vertical` flips x, `horizontal` flips y, `rotational` flips both. + symVertical = 0 + symHorizontal = 1 + symRotational = 2 + + Loc* = object + x*, y*: int + + Domination* = enum + dfNone + dfHqDestroyed = "hq_destroyed" + dfQuantity = "quantity" + dfQuality = "quality" + dfBroadcasts = "broadcasts" + dfHighestId = "highest_id" + dfCoinFlip = "coin_flip" + + InitialBody* = object + id*: int + team*: Team + kind*: RobotKind + loc*: Loc + + MapSpec* = object + ## One converted `.map20`, as `data/maps/bc20/.json` carries it. + name*: string + width*, height*: int + symmetry*: Symmetry + randomSeed*: int + initialWater*: int + elevation*: seq[int] ## the `dirt` int array + water*: seq[bool] + pollution*: seq[int] + ## The map's own pollution array. `GameWorld` reads it into a field and + ## then never consults it: `getPollution` is derived entirely from the + ## global level and the local effect registry. Carried for fidelity. + soup*: seq[int] + initialBodies*: seq[InitialBody] + + Robot* = ref object + id*: int + team*: Team + kind*: RobotKind + loc*: Loc + roundsAlive*: int + soupCarrying*: int + dirtCarrying*: int + cooldownTurns*: float32 + holdingUnit*: bool + heldId*: int + blocked*: bool + ## Held by a delivery drone: no turn, no cooldown decay. + opsLeft*: int + ## The DecisionOps budget that replaces the JVM bytecode limit. + dead*: bool + + LocalPollutionEffect* = object + loc*: Loc + radiusSquared*: int + additive*: int + multiplicative*: float32 + + TeamStats* = object + ## `TeamInfo` plus the per-game counters `results.games[]` reports. The + ## engine keeps only the first three; everything else is telemetry and is + ## never read by a rule. + soup*: array[2, int] + destroyedHq*: array[2, bool] + blockchainsSent*: array[2, int] + soupMined*: array[2, int] + soupRefined*: array[2, int] + dirtMoved*: array[2, int] + unitsBuilt*: array[2, int] + minersBuilt*: array[2, int] + landscapersBuilt*: array[2, int] + dronesBuilt*: array[2, int] + vaporatorsBuilt*: array[2, int] + netGunsBuilt*: array[2, int] + designSchoolsBuilt*: array[2, int] + fulfillmentBuilt*: array[2, int] + refineriesBuilt*: array[2, int] + dronePickups*: array[2, int] + droneWaterDrops*: array[2, int] + netGunKills*: array[2, int] + transactionsSent*: array[2, int] + blockchainSoupSpent*: array[2, int] + + World* = ref object + map*: MapSpec + width*, height*: int + currentRound*: int + maxRounds*: int + running*: bool + idGen*: IdGenerator + rand*: JavaRandom + ## `GameWorld.rand`, seeded from the map's own `randomSeed`. Replaces + ## `setWinnerArbitrary`'s `Math.random()` (docs/RULES-BC20.md §Divergences). + txRand*: JavaRandom + ## `RobotControllerImpl.random`, a Java `private static Random` that the + ## constructor assigns `new Random(gameWorld.getMapSeed())` — so it is + ## RE-SEEDED ON EVERY SPAWN. Per-world here so two worlds in one process + ## cannot bleed into each other; re-seeded identically. + cowRand*: Table[int, JavaRandom] + symmetry*: Symmetry + elevation*: seq[int] + soup*: seq[int] + initialSoup*: seq[int] + flooded*: seq[bool] + floodedCount*: int + localAdditive*: seq[int] + localMultiplicative*: seq[float32] + localPollutions*: Table[int, LocalPollutionEffect] + globalPollution*: int + globalPollutionPeak*: int + waterLevel*: float32 + occupant*: seq[Robot] + robotsById*: Table[int, Robot] + execOrder*: seq[int] + robotCount*: array[3, int] + typeCount*: array[3, array[RobotKind, int]] + ## `ObjectInfo.robotTypeCount`. The chassis reads it every turn; deriving + ## it by walking `robotsById` per robot turn is O(n^2) per round and + ## costs the perf gate. + stats*: TeamStats + txPool*: seq[Transaction] + blockchain*: seq[seq[Transaction]] + hqId*: array[2, int] + hqLostRound*: array[2, int] + hqLostCause*: array[2, string] + winner*: Team + hasWinner*: bool + domination*: Domination + ## Replay/telemetry sinks — never read by a rule. + events*: seq[tuple[round: int, kind: string, a, b, c: int, s: string]] + hashChain*: uint64 + +const + AllDirs* = [dNorth, dNortheast, dEast, dSoutheast, + dSouth, dSouthwest, dWest, dNorthwest, dCenter] + ## `Direction.allDirections()` == `Direction.values()`, CENTER included. + MoveDirs* = [dNorth, dNortheast, dEast, dSoutheast, + dSouth, dSouthwest, dWest, dNorthwest] + ## `CowControlProvider.DIRECTIONS`, and the eight a unit can move in. + +# --------------------------------------------------------------------------- +# Geometry +# --------------------------------------------------------------------------- + +func dx*(d: Dir): int = + case d + of dNorth, dSouth, dCenter: 0 + of dNortheast, dEast, dSoutheast: 1 + of dSouthwest, dWest, dNorthwest: -1 + +func dy*(d: Dir): int = + case d + of dEast, dWest, dCenter: 0 + of dNorth, dNortheast, dNorthwest: 1 + of dSoutheast, dSouth, dSouthwest: -1 + +func opposite*(d: Dir): Dir = + if ord(d) >= 8: d else: Dir((ord(d) + 4) mod 8) + +func rotateLeft*(d: Dir): Dir = + if ord(d) >= 8: d else: Dir((ord(d) + 7) mod 8) + +func rotateRight*(d: Dir): Dir = + if ord(d) >= 8: d else: Dir((ord(d) + 1) mod 8) + +func loc*(x, y: int): Loc = Loc(x: x, y: y) + +func fromDelta*(dx, dy: int): Dir = + for d in AllDirs: + if d.dx == dx and d.dy == dy: + return d + dCenter +func `+`*(a: Loc, d: Dir): Loc = loc(a.x + d.dx, a.y + d.dy) +func `==`*(a, b: Loc): bool = a.x == b.x and a.y == b.y + +func distanceSquaredTo*(a, b: Loc): int = + let ddx = a.x - b.x + let ddy = a.y - b.y + ddx * ddx + ddy * ddy + +func isWithinDistanceSquared*(a, b: Loc, r2: int): bool = + a.distanceSquaredTo(b) <= r2 + +func isAdjacentTo*(a, b: Loc): bool = + abs(a.x - b.x) <= 1 and abs(a.y - b.y) <= 1 + +func chebyshev*(a, b: Loc): int = + max(abs(a.x - b.x), abs(a.y - b.y)) + +func directionTo*(a, b: Loc): Dir = + ## `MapLocation.directionTo`, ported with the engine's own 2.414 fan. + let ddx = float64(b.x - a.x) + let ddy = float64(b.y - a.y) + if abs(ddx) >= 2.414 * abs(ddy): + if ddx > 0: return dEast + if ddx < 0: return dWest + return dCenter + elif abs(ddy) >= 2.414 * abs(ddx): + if ddy > 0: return dNorth + return dSouth + elif ddx > 0: + return (if ddy > 0: dNortheast else: dSoutheast) + elif ddx < 0: + return (if ddy > 0: dNorthwest else: dSouthwest) + dCenter + +func idx*(w: World, l: Loc): int = l.x + l.y * w.width +func indexToLoc*(w: World, i: int): Loc = loc(i mod w.width, i div w.width) +func onTheMap*(w: World, l: Loc): bool = + l.x >= 0 and l.y >= 0 and l.x < w.width and l.y < w.height + +iterator locationsWithinRadiusSquared*(w: World, center: Loc, + r2: int): Loc = + ## `GameWorld.getAllLocationsWithinRadiusSquared`, in its own x-then-y + ## order, with the engine's `ceil(sqrt(r2)) + 1` window. + let ceiled = int(ceil(sqrt(float64(max(0, r2))))) + 1 + let minX = max(center.x - ceiled, 0) + let minY = max(center.y - ceiled, 0) + let maxX = min(center.x + ceiled, w.width - 1) + let maxY = min(center.y + ceiled, w.height - 1) + for x in minX .. maxX: + for y in minY .. maxY: + let l = loc(x, y) + if center.isWithinDistanceSquared(l, r2): + yield l + +# --------------------------------------------------------------------------- +# Telemetry +# --------------------------------------------------------------------------- + +proc emit*(w: World, kind: string, a = 0, b = 0, c = 0, s = "") = + w.events.add((round: w.currentRound, kind: kind, a: a, b: b, c: c, s: s)) + +proc mixHash*(w: World, v: int) = + ## The per-round hash chain (`coworld-ctf`'s `gameHash` discipline). The + ## viewer re-derives every round and compares, exposing `bc_mismatch_round`. + w.hashChain = (w.hashChain xor uint64(v and 0xFFFFFFFF)) * + 0x100000001B3'u64 + +# --------------------------------------------------------------------------- +# Soup, dirt, water, pollution +# --------------------------------------------------------------------------- + +func getSoup*(w: World, l: Loc): int = + if w.onTheMap(l): w.soup[w.idx(l)] else: 0 + +proc removeSoup*(w: World, l: Loc, amount: int) = + if w.onTheMap(l): + let i = w.idx(l) + w.soup[i] = max(0, w.soup[i] - amount) + +func getDirt*(w: World, l: Loc): int = + ## The engine calls the elevation "dirt". Off-map reads are 0, which is what + ## the symmetry detector and every range check rely on. + if w.onTheMap(l): w.elevation[w.idx(l)] else: 0 + +func getDirtDifference*(w: World, a, b: Loc): int = + abs(w.getDirt(a) - w.getDirt(b)) + +func getPollution*(w: World, l: Loc): int = + if not w.onTheMap(l): return 0 + let i = w.idx(l) + javaRoundF32(float32(float32(w.globalPollution + w.localAdditive[i]) * + w.localMultiplicative[i])) + +proc addGlobalPollution*(w: World, amount: int) = + w.globalPollution = max(w.globalPollution + amount, 0) + w.globalPollutionPeak = max(w.globalPollutionPeak, w.globalPollution) + +proc addLocalPollution*(w: World, robotId: int, l: Loc, r2, additive: int, + multiplicative: float32) = + w.localPollutions[robotId] = LocalPollutionEffect( + loc: l, radiusSquared: r2, additive: additive, + multiplicative: multiplicative) + for affected in w.locationsWithinRadiusSquared(l, r2): + let i = w.idx(affected) + w.localAdditive[i] += additive + w.localMultiplicative[i] = w.localMultiplicative[i] * multiplicative + +proc resetPollutionForRobot*(w: World, robotId: int) = + if robotId notin w.localPollutions: + return + let e = w.localPollutions[robotId] + for affected in w.locationsWithinRadiusSquared(e.loc, e.radiusSquared): + let i = w.idx(affected) + w.localAdditive[i] -= e.additive + w.localMultiplicative[i] = w.localMultiplicative[i] / e.multiplicative + w.localPollutions.del(robotId) + +func isFlooded*(w: World, l: Loc): bool = + if w.onTheMap(l): w.flooded[w.idx(l)] else: false + +func getRobot*(w: World, l: Loc): Robot = + if w.onTheMap(l): w.occupant[w.idx(l)] else: nil + +proc destroyRobot*(w: World, id: int) + +proc setFloodStatus*(w: World, i: int, newStatus: bool) = + if w.flooded[i] == newStatus: + return + w.flooded[i] = newStatus + w.floodedCount += (if newStatus: 1 else: -1) + let victim = w.occupant[i] + if newStatus and victim != nil and victim.kind != rtDeliveryDrone: + w.destroyRobot(victim.id) + +proc tryResurface*(w: World, l: Loc) = + ## `GameWorld.tryResurface`: `dirt[idx] >= waterLevel` un-floods the tile, + ## the moment a deposit lifts it above the water. `int >= float` widens the + ## elevation to `float`, exactly as Java does. + let i = w.idx(l) + if float32(w.elevation[i]) >= w.waterLevel: + w.setFloodStatus(i, false) + +# --------------------------------------------------------------------------- +# Robot bookkeeping +# --------------------------------------------------------------------------- + +func isBuilding*(kind: RobotKind): bool = + kind in {rtHq, rtRefinery, rtVaporator, rtDesignSchool, + rtFulfillmentCenter, rtNetGun} + +func canRefine*(kind: RobotKind): bool = kind in {rtHq, rtRefinery} +func canAffectPollution*(kind: RobotKind): bool = + kind in {rtHq, rtRefinery, rtVaporator, rtCow} +func canMoveKind*(kind: RobotKind): bool = + kind in {rtMiner, rtLandscaper, rtDeliveryDrone, rtCow} +func canFly*(kind: RobotKind): bool = kind == rtDeliveryDrone +func canDig*(kind: RobotKind): bool = kind == rtLandscaper +func canMine*(kind: RobotKind): bool = kind == rtMiner +func canShootKind*(kind: RobotKind): bool = kind in {rtNetGun, rtHq} +func canBeShot*(kind: RobotKind): bool = kind == rtDeliveryDrone +func canBePickedUp*(kind: RobotKind): bool = + kind in {rtMiner, rtLandscaper, rtCow} +func canPickUpUnits*(kind: RobotKind): bool = kind == rtDeliveryDrone + +func canBuildKind*(builder, target: RobotKind): bool = + ## `RobotType.canBuild` == `this == type.spawnSource`. + case target + of rtMiner: builder == rtHq + of rtRefinery, rtVaporator, rtDesignSchool, rtFulfillmentCenter, rtNetGun: + builder == rtMiner + of rtLandscaper: builder == rtDesignSchool + of rtDeliveryDrone: builder == rtFulfillmentCenter + else: false + +func spec*(r: Robot): RobotSpec = RobotSpecs[r.kind] + +func currentSensorRadiusSquared*(w: World, r: Robot): int = + javaRoundF32(float32(float32(RobotSpecs[r.kind].sensorRadiusSquared) * + sensorCoefficient(w.getPollution(r.loc)))) + +func canSenseLocation*(w: World, r: Robot, l: Loc): bool = + r.loc.distanceSquaredTo(l) <= w.currentSensorRadiusSquared(r) + +func isReady*(r: Robot): bool = r.cooldownTurns < 1.0'f32 + +proc addCooldownTurns*(w: World, r: Robot) = + r.cooldownTurns = r.cooldownTurns + + RobotSpecs[r.kind].actionCooldown * cooldownCoefficient(w.getPollution(r.loc)) + +proc addRobotAt(w: World, l: Loc, r: Robot) = + w.occupant[w.idx(l)] = r + +proc removeRobotAt(w: World, l: Loc) = + w.occupant[w.idx(l)] = nil + +proc addDirt*(w: World, l: Loc, amount: int) + +proc addDirtCarrying*(w: World, r: Robot, amount: int) = + ## `InternalRobot.addDirtCarrying`: a BUILDING whose accumulated dirt reaches + ## its `dirtLimit` (HQ 50, every other building 15) is destroyed on the spot. + r.dirtCarrying += amount + if r.kind.isBuilding() and r.dirtCarrying >= RobotSpecs[r.kind].dirtLimit: + if r.kind == rtHq and r.team != teamNeutral: + w.hqLostCause[ord(r.team)] = "buried" + w.destroyRobot(r.id) + +proc removeDirt*(w: World, l: Loc) = + ## `GameWorld.removeDirt`: dirt comes off a DIRTY BUILDING if one stands + ## there, otherwise off the ground. There is no `tryResurface` here — digging + ## a tile down never un-floods it. + if not w.onTheMap(l): return + let target = w.getRobot(l) + if target != nil and target.kind.isBuilding() and target.dirtCarrying > 0: + target.dirtCarrying = max(0, target.dirtCarrying - 1) + else: + w.elevation[w.idx(l)] -= 1 + +proc addDirt*(w: World, l: Loc, amount: int) = + ## `GameWorld.addDirt`: onto a BUILDING if one stands there, otherwise onto + ## the ground, and then `tryResurface`. + if not w.onTheMap(l): return + let target = w.getRobot(l) + if target != nil and target.kind.isBuilding(): + w.addDirtCarrying(target, amount) + else: + w.elevation[w.idx(l)] += amount + w.tryResurface(l) + +# --------------------------------------------------------------------------- +# Spawning and destruction +# --------------------------------------------------------------------------- + +proc recomputeSymmetry*(w: World): Symmetry = + ## `CowControlProvider.getSymmetry`, ported literally: candidates in the + ## order [vertical, horizontal, rotational], each eliminated by the first + ## tile where soup, elevation or robot TYPE disagrees under that transform, + ## first survivor wins, `rotational` when none survives. + var possible = @[symVertical, symHorizontal, symRotational] + + proc symX(w: World, x: int, s: Symmetry): int = + if s == symHorizontal: x else: w.width - 1 - x + proc symY(w: World, y: int, s: Symmetry): int = + if s == symVertical: y else: w.height - 1 - y + + for x in 0 ..< w.width: + for y in 0 ..< w.height: + let here = loc(x, y) + let bot = w.getRobot(here) + for i in countdown(possible.high, 0): + if i > possible.high: continue + let s = possible[i] + let there = loc(w.symX(x, s), w.symY(y, s)) + var drop = false + if w.getSoup(here) != w.getSoup(there): drop = true + if w.getDirt(here) != w.getDirt(there): drop = true + let other = w.getRobot(there) + if bot != nil or other != nil: + if bot == nil or other == nil or bot.kind != other.kind: + drop = true + if drop: + possible.delete(i) + if possible.len <= 1: break + if possible.len <= 1: break + if possible.len > 0: possible[0] else: symRotational + +proc spawnRobot*(w: World, id: int, kind: RobotKind, l: Loc, + team: Team): int {.discardable.} = + ## `GameWorld.spawnRobot`. Three side effects are load-bearing: + ## * the new body is APPENDED to the exec order; + ## * `new RobotControllerImpl(...)` re-seeds the static transaction RNG with + ## the map seed; + ## * `controlProvider.robotSpawned` recomputes the map symmetry — but only + ## for the NEUTRAL provider (`TeamControlProvider` delegates by team), so + ## only a COW spawn moves it. Cows only exist at map load, which is why the + ## symmetry is fixed for the whole match. + w.txRand = initJavaRandom(w.map.randomSeed) + let r = Robot(id: id, team: team, kind: kind, loc: l, heldId: -1) + w.robotsById[id] = r + w.execOrder.add(id) + w.robotCount[ord(team)] += 1 + w.typeCount[ord(team)][kind] += 1 + w.addRobotAt(l, r) + if kind == rtHq and team != teamNeutral: + w.hqId[ord(team)] = id + if team == teamNeutral: + w.symmetry = w.recomputeSymmetry() + id + +proc spawnRobot*(w: World, kind: RobotKind, l: Loc, + team: Team): int {.discardable.} = + w.spawnRobot(w.idGen.nextId(), kind, l, team) + +proc dropHeldUnit*(w: World, drone: Robot, target: Loc) + +proc destroyRobot*(w: World, id: int) = + ## `GameWorld.destroyRobot`, in its own order: off the grid, HQ flag, drop a + ## carried unit at the drone's own tile, spill carried dirt, clear pollution. + var r: Robot + if not w.robotsById.pop(id, r): + return + r.dead = true + w.removeRobotAt(r.loc) + if r.kind == rtHq and r.team != teamNeutral: + w.stats.destroyedHq[ord(r.team)] = true + if w.hqLostRound[ord(r.team)] < 0: + w.hqLostRound[ord(r.team)] = w.currentRound + if w.hqLostCause[ord(r.team)] == "none": + w.hqLostCause[ord(r.team)] = "drowned" + if r.kind.canPickUpUnits() and r.holdingUnit: + w.dropHeldUnit(r, r.loc) + if r.dirtCarrying > 0: + w.addDirt(r.loc, r.dirtCarrying) + if r.kind.canAffectPollution(): + w.resetPollutionForRobot(id) + w.robotCount[ord(r.team)] -= 1 + w.typeCount[ord(r.team)][r.kind] -= 1 + let i = w.execOrder.find(id) + if i >= 0: w.execOrder.delete(i) + w.emit("died", id, ord(r.team), ord(r.kind)) + +# --------------------------------------------------------------------------- +# Rule 6 — the legal actions, with their exact preconditions +# --------------------------------------------------------------------------- + +func isLocationOccupied*(w: World, l: Loc): bool = + w.getRobot(l) != nil + +proc canMove*(w: World, r: Robot, d: Dir): bool = + let target = r.loc + d + if not r.kind.canMoveKind(): return false + if not r.loc.isAdjacentTo(target): return false + if not w.onTheMap(target): return false + if w.isLocationOccupied(target): return false + if w.getDirtDifference(r.loc, target) > MaxDirtDifference and + not r.kind.canFly(): return false + isReady(r) + +proc movePickedUpUnit(w: World, drone: Robot, center: Loc) = + if drone.heldId in w.robotsById: + w.robotsById[drone.heldId].loc = center + +proc move*(w: World, r: Robot, d: Dir) = + ## `RobotControllerImpl.move`. The engine checks the destination for flooding + ## AFTER the legality assert and disintegrates the mover — a non-flying unit + ## that walks into water dies instead of moving. + let center = r.loc + d + if not w.canMove(r, d): return + if w.isFlooded(center) and not r.kind.canFly(): + w.destroyRobot(r.id) + return + w.addCooldownTurns(r) + w.removeRobotAt(r.loc) + w.addRobotAt(center, r) + r.loc = center + if r.holdingUnit: + w.movePickedUpUnit(r, center) + +proc canBuildRobot*(w: World, r: Robot, kind: RobotKind, d: Dir): bool = + let spawnLoc = r.loc + d + if not r.kind.canBuildKind(kind): return false + if r.team == teamNeutral: return false + if w.stats.soup[ord(r.team)] < RobotSpecs[kind].cost: return false + if not w.onTheMap(spawnLoc): return false + if w.isLocationOccupied(spawnLoc): return false + if w.isFlooded(spawnLoc) and kind != rtDeliveryDrone: return false + if kind != rtDeliveryDrone and + w.getDirtDifference(r.loc, spawnLoc) > MaxDirtDifference: return false + isReady(r) + +proc countBuilt(w: World, team: Team, kind: RobotKind) = + let t = ord(team) + w.stats.unitsBuilt[t] += 1 + case kind + of rtMiner: w.stats.minersBuilt[t] += 1 + of rtLandscaper: w.stats.landscapersBuilt[t] += 1 + of rtDeliveryDrone: w.stats.dronesBuilt[t] += 1 + of rtVaporator: w.stats.vaporatorsBuilt[t] += 1 + of rtNetGun: w.stats.netGunsBuilt[t] += 1 + of rtDesignSchool: w.stats.designSchoolsBuilt[t] += 1 + of rtFulfillmentCenter: w.stats.fulfillmentBuilt[t] += 1 + of rtRefinery: w.stats.refineriesBuilt[t] += 1 + else: discard + +proc buildRobot*(w: World, r: Robot, kind: RobotKind, d: Dir): int + {.discardable.} = + if not w.canBuildRobot(r, kind, d): return -1 + w.addCooldownTurns(r) + w.stats.soup[ord(r.team)] -= RobotSpecs[kind].cost + let id = w.spawnRobot(kind, r.loc + d, r.team) + w.robotsById[id].cooldownTurns = float32(InitialCooldownTurns) + w.countBuilt(r.team, kind) + w.emit("built", id, ord(r.team), ord(kind)) + id + +proc canMineSoup*(w: World, r: Robot, d: Dir): bool = + let center = r.loc + d + if not r.kind.canMine(): return false + if r.soupCarrying >= RobotSpecs[r.kind].soupLimit: return false + if not w.onTheMap(center): return false + if w.getSoup(center) <= 0: return false + isReady(r) + +proc mineSoup*(w: World, r: Robot, d: Dir) = + if not w.canMineSoup(r, d): return + w.addCooldownTurns(r) + let mineLoc = r.loc + d + let mined = min(SoupMiningRate, + min(w.getSoup(mineLoc), RobotSpecs[r.kind].soupLimit - r.soupCarrying)) + w.removeSoup(mineLoc, mined) + r.soupCarrying += mined + w.stats.soupMined[ord(r.team)] += mined + +proc canDepositSoup*(w: World, r: Robot, d: Dir): bool = + let center = r.loc + d + if r.kind != rtMiner: return false + if r.soupCarrying <= 0: return false + if not w.onTheMap(center): return false + let adjacent = w.getRobot(center) + if adjacent == nil or not adjacent.kind.canRefine(): return false + isReady(r) + +proc depositSoup*(w: World, r: Robot, d: Dir, amount: int) = + if not w.canDepositSoup(r, d): return + let give = min(amount, r.soupCarrying) + w.addCooldownTurns(r) + r.soupCarrying = (if give > r.soupCarrying: 0 else: r.soupCarrying - give) + w.getRobot(r.loc + d).soupCarrying += give + +proc canDigDirt*(w: World, r: Robot, d: Dir): bool = + let center = r.loc + d + if not r.kind.canDig(): return false + if r.dirtCarrying >= RobotSpecs[r.kind].dirtLimit: return false + if not w.onTheMap(center): return false + let adjacent = w.getRobot(center) + if adjacent != nil and adjacent.kind.isBuilding() and + adjacent.dirtCarrying <= 0: return false + isReady(r) + +proc digDirt*(w: World, r: Robot, d: Dir) = + if not w.canDigDirt(r, d): return + w.addCooldownTurns(r) + w.removeDirt(r.loc + d) + w.addDirtCarrying(r, 1) + w.stats.dirtMoved[ord(r.team)] += 1 + +proc canDepositDirt*(w: World, r: Robot, d: Dir): bool = + let center = r.loc + d + if r.kind != rtLandscaper: return false + if r.dirtCarrying < 1: return false + if not w.onTheMap(center): return false + isReady(r) + +proc depositDirt*(w: World, r: Robot, d: Dir) = + if not w.canDepositDirt(r, d): return + w.addCooldownTurns(r) + r.dirtCarrying = max(0, r.dirtCarrying - 1) + w.addDirt(r.loc + d, 1) + w.stats.dirtMoved[ord(r.team)] += 1 + +proc canPickUpUnit*(w: World, r: Robot, id: int): bool = + if not r.kind.canPickUpUnits(): return false + if r.holdingUnit: return false + if id notin w.robotsById: return false + let target = w.robotsById[id] + if not target.kind.canBePickedUp(): return false + if not target.loc.isWithinDistanceSquared(r.loc, + DeliveryDronePickupRadiusSquared): return false + if target.blocked: return false + isReady(r) + +proc pickUpUnit*(w: World, r: Robot, id: int) = + if not w.canPickUpUnit(r, id): return + let target = w.robotsById[id] + target.blocked = true + w.removeRobotAt(target.loc) + r.holdingUnit = true + r.heldId = id + target.loc = r.loc + w.addCooldownTurns(r) + w.stats.dronePickups[ord(r.team)] += 1 + w.emit("pickup", r.id, ord(r.team), id) + +proc canDropUnit*(w: World, r: Robot, d: Dir): bool = + let center = r.loc + d + if not r.kind.canPickUpUnits(): return false + if not r.holdingUnit: return false + if not w.onTheMap(center): return false + if w.isLocationOccupied(center): return false + isReady(r) + +proc dropHeldUnit*(w: World, drone: Robot, target: Loc) = + ## `dropUnit(dir, checkConditions=false)`: the unconditional drop a dying + ## drone performs onto its own tile. A non-drone dropped into water is + ## destroyed immediately. + let id = drone.heldId + if id notin w.robotsById: + drone.holdingUnit = false + drone.heldId = -1 + return + let dropped = w.robotsById[id] + dropped.blocked = false + dropped.loc = target + drone.holdingUnit = false + drone.heldId = -1 + w.addRobotAt(target, dropped) + if w.isFlooded(target): + if dropped.team != drone.team and drone.team != teamNeutral: + w.stats.droneWaterDrops[ord(drone.team)] += 1 + w.emit("drone_water_drop", drone.id, ord(drone.team), ord(dropped.kind)) + w.destroyRobot(id) + +proc dropUnit*(w: World, r: Robot, d: Dir) = + if not w.canDropUnit(r, d): return + let target = r.loc + d + let id = r.heldId + let dropped = w.robotsById.getOrDefault(id) + dropped.blocked = false + dropped.loc = target + r.holdingUnit = false + r.heldId = -1 + w.addRobotAt(target, dropped) + w.addCooldownTurns(r) + if w.isFlooded(target): + if dropped.team != r.team and r.team != teamNeutral: + w.stats.droneWaterDrops[ord(r.team)] += 1 + w.emit("drone_water_drop", r.id, ord(r.team), ord(dropped.kind)) + w.destroyRobot(id) + +proc canShootUnit*(w: World, r: Robot, id: int): bool = + if not r.kind.canShootKind(): return false + if id notin w.robotsById: return false + let target = w.robotsById[id] + if not target.kind.canBeShot(): return false + if not target.loc.isWithinDistanceSquared(r.loc, + NetGunShootRadiusSquared): return false + isReady(r) + +proc shootUnit*(w: World, r: Robot, id: int) = + if not w.canShootUnit(r, id): return + w.destroyRobot(id) + w.addCooldownTurns(r) + if r.team != teamNeutral: + w.stats.netGunKills[ord(r.team)] += 1 + w.emit("net_gun_kill", r.id, ord(r.team), id) + +proc canSubmitTransaction*(w: World, r: Robot, cost: int): bool = + ## NOT an action: no cooldown, no `isReady` check. `message.length == 7` is + ## a type invariant here. + if r.team == teamNeutral: return false + if w.stats.soup[ord(r.team)] < cost: return false + cost > 0 + +proc submitTransaction*(w: World, r: Robot, + message: array[TransactionLength, int], cost: int) = + if not canSubmitTransaction(w, r, cost): return + let t = ord(r.team) + w.stats.soup[t] -= cost + w.stats.blockchainSoupSpent[t] += cost + w.stats.transactionsSent[t] += 1 + let id = w.txRand.nextInt() + w.txPool.add(newTransaction(cost, message, id, t)) + +proc getBlock*(w: World, roundNumber: int): seq[Transaction] = + if roundNumber <= 0 or roundNumber >= w.currentRound: return @[] + if roundNumber - 1 >= w.blockchain.len: return @[] + w.blockchain[roundNumber - 1] + +# --------------------------------------------------------------------------- +# The end-of-match ladder, in the engine's own order +# --------------------------------------------------------------------------- + +proc setWinner*(w: World, t: Team, d: Domination) = + w.winner = t + w.hasWinner = true + w.domination = d + +func netWorth*(w: World, team: Team): int = + result = w.stats.soup[ord(team)] + for _, r in w.robotsById: + if r.team == team: + result += RobotSpecs[r.kind].cost + +func livingUnits*(w: World, team: Team): int = w.robotCount[ord(team)] + +proc setWinnerIfHqDestroyed*(w: World): bool = + let a = w.stats.destroyedHq[0] + let b = w.stats.destroyedHq[1] + if a and not b: + w.setWinner(teamB, dfHqDestroyed); true + elif b and not a: + w.setWinner(teamA, dfHqDestroyed); true + else: false + +proc setWinnerIfQuantity*(w: World): bool = + let a = w.robotCount[0] + let b = w.robotCount[1] + if b > a: w.setWinner(teamB, dfQuantity); true + elif a > b: w.setWinner(teamA, dfQuantity); true + else: false + +proc setWinnerIfQuality*(w: World): bool = + let a = w.netWorth(teamA) + let b = w.netWorth(teamB) + if a > b: w.setWinner(teamA, dfQuality); true + elif b > a: w.setWinner(teamB, dfQuality); true + else: false + +proc setWinnerIfMoreBroadcasts*(w: World): bool = + let a = w.stats.blockchainsSent[0] + let b = w.stats.blockchainsSent[1] + if a > b: w.setWinner(teamA, dfBroadcasts); true + elif b > a: w.setWinner(teamB, dfBroadcasts); true + else: false + +proc setWinnerHighestRobotId*(w: World): bool = + var best = -1 + var bestTeam = teamNeutral + for id, r in w.robotsById: + if r.team != teamNeutral and id > best: + best = id + bestTeam = r.team + if best < 0: return false + w.setWinner(bestTeam, dfHighestId) + true + +proc setWinnerArbitrary*(w: World) = + ## `setWinnerArbitrary` uses `Math.random()`, which is wall-clock seeded and + ## therefore not reproducible. A draw from the WORLD RNG replaces it — a + ## documented divergence, reachable only when neither team has a single + ## living robot (docs/RULES-BC20.md §Divergences). + w.setWinner((if w.rand.nextDouble() < 0.5: teamA else: teamB), dfCoinFlip) + +func timeLimitReached*(w: World): bool = + ## `GameWorld.timeLimitReached` is `currentRound >= rounds - 1`, so a + ## 1500-round cap plays 1499. The off-by-one is the engine's. + w.currentRound >= w.maxRounds - 1 + +proc checkEndOfMatch*(w: World) = + if not (w.timeLimitReached() or w.stats.destroyedHq[0] or + w.stats.destroyedHq[1]): + return + if w.hasWinner: return + if w.setWinnerIfHqDestroyed(): return + if w.setWinnerIfQuantity(): return + if w.setWinnerIfQuality(): return + if w.setWinnerIfMoreBroadcasts(): return + if w.setWinnerHighestRobotId(): return + w.setWinnerArbitrary() + +proc gamePoints*(w: World): array[2, int] = + ## `points[t] = int(60*hq_survival + 25*unit_share + 15*net_worth_share)`. + ## + ## Every share is narrowed through FLOAT32 before the weighted sum and the + ## sum is TRUNCATED by the `int()` cast — not for fidelity to Java (this + ## formula is ours) but for recorder/re-deriver agreement: the same + ## arithmetic runs natively on x86-64 and in wasm32 and must produce the same + ## integer. `units` counts BUILDINGS TOO and `worth` includes the team pool, + ## because those are exactly what the engine's `quantity` and `quality` + ## rungs compare. + var hq: array[2, int] + for t in 0 .. 1: + hq[t] = if w.stats.destroyedHq[t]: 0 else: 1 + let hqTotal = max(1, hq[0] + hq[1]) + let units = [w.livingUnits(teamA), w.livingUnits(teamB)] + let unitTotal = max(1, units[0] + units[1]) + let worth = [w.netWorth(teamA), w.netWorth(teamB)] + let worthTotal = max(1, worth[0] + worth[1]) + for t in 0 .. 1: + let survival = float32(hq[t]) / float32(hqTotal) + let shareU = float32(units[t]) / float32(unitTotal) + let shareW = float32(worth[t]) / float32(worthTotal) + result[t] = int(60.0'f32 * survival + 25.0'f32 * shareU + + 15.0'f32 * shareW) + +# --------------------------------------------------------------------------- +# Turn scaffolding and construction +# --------------------------------------------------------------------------- + +proc processBeginningOfRound*(w: World) = + ## Rule 1 and rule 2. `InternalRobot.processBeginningOfRound` is a no-op in + ## 2020; it is kept as a named step because the hash chain and the parity + ## trace are taken around it. + inc w.currentRound + w.stats.soup[0] += BaseIncomePerRound + w.stats.soup[1] += BaseIncomePerRound + +proc processBeginningOfTurn*(w: World, r: Robot) = + ## Rule 5. The cooldown decays and the DecisionOps budget is reset. + if r.cooldownTurns > 0: + r.cooldownTurns = max(0.0'f32, r.cooldownTurns - 1.0'f32) + r.opsLeft = RobotSpecs[r.kind].decisionOps + +proc processEndOfTurn*(w: World, r: Robot) = + ## Rule 7, in `InternalRobot.processEndOfTurn`'s own order. A refinery's + ## local +500 is installed HERE and removed at 7.1 of its own next turn, so + ## it lasts exactly one round. + if r.kind.canAffectPollution(): + w.resetPollutionForRobot(r.id) + var shouldPollute = false + if r.kind.canRefine() and r.soupCarrying > 0: + let produced = min(r.soupCarrying, RobotSpecs[r.kind].maxSoupProduced) + r.soupCarrying -= produced + w.stats.soup[ord(r.team)] += produced + w.stats.soupRefined[ord(r.team)] += produced + shouldPollute = true + if r.kind == rtVaporator: + w.stats.soup[ord(r.team)] += RobotSpecs[r.kind].maxSoupProduced + shouldPollute = true + if r.kind == rtCow: + shouldPollute = true + if r.kind.canAffectPollution() and shouldPollute: + w.addGlobalPollution(RobotSpecs[r.kind].globalPollutionAmount) + w.addLocalPollution(r.id, r.loc, RobotSpecs[r.kind].pollutionRadiusSquared, + RobotSpecs[r.kind].localPollutionAdditiveEffect, + RobotSpecs[r.kind].localPollutionMultiplicativeEffect) + r.roundsAlive += 1 + +proc processBlockchain*(w: World) = + ## Rule 8.1. `blockchainsSent` counts MINTED transactions, not submitted + ## ones — the `broadcasts` rung of the tiebreak ladder reads it. + let block0 = mintBlock(w.txPool, NumberOfTransactionsPerBlock) + for t in block0: + w.stats.blockchainsSent[t.team] += 1 + w.blockchain.add(block0) + +proc newWorld*(spec: MapSpec, maxRounds: int): World = + let n = spec.width * spec.height + result = World( + map: spec, + width: spec.width, + height: spec.height, + currentRound: 0, + maxRounds: maxRounds, + running: true, + idGen: initIdGenerator(spec.randomSeed), + rand: initJavaRandom(spec.randomSeed), + txRand: initJavaRandom(spec.randomSeed), + cowRand: initTable[int, JavaRandom](), + symmetry: symRotational, + elevation: spec.elevation, + soup: spec.soup, + initialSoup: spec.soup, + flooded: spec.water, + localAdditive: newSeq[int](n), + localMultiplicative: newSeq[float32](n), + localPollutions: initTable[int, LocalPollutionEffect](), + globalPollution: 0, + waterLevel: float32(spec.initialWater), + occupant: newSeq[Robot](n), + robotsById: initTable[int, Robot](), + winner: teamNeutral, + domination: dfNone, + hashChain: 0xCBF29CE484222325'u64 + ) + for i in 0 ..< n: + result.localMultiplicative[i] = 1.0'f32 + if result.flooded[i]: result.floodedCount += 1 + result.stats.soup[0] = InitialSoup + result.stats.soup[1] = InitialSoup + result.hqId = [-1, -1] + result.hqLostRound = [-1, -1] + result.hqLostCause = ["none", "none"] + ## `CowControlProvider.matchStarted` computes the symmetry BEFORE any body + ## exists; each cow spawn then recomputes it over the populated world. + result.symmetry = result.recomputeSymmetry() + for body in spec.initialBodies: + result.spawnRobot(body.id, body.kind, body.loc, body.team) diff --git a/src/battlecode/years/bc26/chassis/kit.nim b/src/battlecode/years/bc26/chassis/kit.nim index 86e765e..be0e3ae 100644 --- a/src/battlecode/years/bc26/chassis/kit.nim +++ b/src/battlecode/years/bc26/chassis/kit.nim @@ -10,10 +10,10 @@ import std/tables import ../../../rng -import ../../../sheet +import ../knobs import ../constants, ../world -export sheet, constants, world, rng +export knobs, constants, world, rng type Brain* = ref object diff --git a/src/battlecode/years/bc26/knobs.nim b/src/battlecode/years/bc26/knobs.nim new file mode 100644 index 0000000..b4e59b4 --- /dev/null +++ b/src/battlecode/years/bc26/knobs.nim @@ -0,0 +1,248 @@ +## The Battlecode 2026 knob table: the ten `Doctrine` knobs, its defaults, its +## per-field repair and its plain-words readout — plus the chassis selector, +## which is NOT one of them. +## +## MOVED VERBATIM out of `sheet.nim` when the bc20 year module landed, at the +## post-D1 state: no bc26 semantics change, the type, the defaults, the ranges +## and the repair order are the ones on `main`, and `tests/test_sheet.nim` is +## unchanged. `sheet.nim` is now the year-neutral envelope that dispatches in +## here. + +import std/[json, tables] +import ../../sheet_common + +export sheet_common + +type + Chassis* = enum + chAwu = "awu" + chScaffold = "scaffold" + + BackstabPolicy* = enum + bpNever = "never" + bpWhenAhead = "when_ahead" + bpAtRoundN = "at_round_N" + bpOnFirstContact = "on_first_contact" + bpRetaliateOnly = "retaliate_only" + + CatEngagement* = enum + ceAvoid = "avoid" + ceOpportunistic = "opportunistic" + ceHunt = "hunt" + ceFeed = "feed" + + SpawnCurve* = enum + scLean = "lean" + scSteady = "steady" + scSwarm = "swarm" + + DirtWallPolicy* = enum + dwNone = "none" + dwKingShell = "king_shell" + dwChoke = "choke" + + Doctrine* = object + ## The 10 doctrine knobs, each with a named site in the chassis + ## (docs/RULES.md §The doctrine sheet lists the site per knob), plus the + ## chassis selector, which is NOT one of them. + chassis*: Chassis + ## NOT A KNOB. An LLM doctrine always runs `awu`: `chassis` is absent + ## from `KnownKeys` and from the prompt's knob list, so a reply that + ## names it is recorded in `unknownFields` and ignored. The only way to + ## select `scaffold` is the scripted filler path + ## (`PLAYER_SCRIPTED=scaffold` → `baselines.baselineSheet`), which sets + ## this field directly rather than through the sheet. + backstabPolicy*: BackstabPolicy + backstabRound*: int + catEngagement*: CatEngagement + catTrapBudget*: int + ratTrapBudget*: int + spawnCurve*: SpawnCurve + cheeseFerryRatio*: float + kingCountTarget*: int + dirtWallPolicy*: DirtWallPolicy + throwRatsToFeedCats*: bool + +const + KnownKeys* = [ + "backstab_policy", "backstab_round", "cat_engagement", + "cat_trap_budget", "rat_trap_budget", "spawn_curve", "cheese_ferry_ratio", + "king_count_target", "dirt_wall_policy", "throw_rats_to_feed_cats" + ] + ## THE LLM-VISIBLE KNOB SURFACE, and the same list the prompt preamble + ## prints. `chassis` is deliberately not in it: which bot drives the clan + ## is not a strategic choice a cog gets to make, it is which policy the + ## operator ran. A reply that sends `chassis` is treated as any other + ## unknown key — recorded, ignored, and the clan plays `awu`. + +proc defaultDoctrine*(): Doctrine = + Doctrine( + chassis: chAwu, + backstabPolicy: bpRetaliateOnly, + backstabRound: 600, + catEngagement: ceOpportunistic, + catTrapBudget: 40, + ratTrapBudget: 60, + spawnCurve: scSteady, + cheeseFerryRatio: 0.5, + kingCountTarget: 3, + dirtWallPolicy: dwKingShell, + throwRatsToFeedCats: false + ) + +proc parseChassis*(text: string): Chassis = + ## The RECORDED chassis, read back on playback. `validate` never reads + ## `chassis` — it is not a knob — so the replay's applied sheet is the only + ## place the deriver can learn which bot actually drove a clan. Anything + ## unrecognised is `awu`, the chassis every LLM doctrine runs. + let key = normalizeKey(text) + for value in Chassis: + if $value == key: + return value + chAwu + +proc applyKnobs*(seen: Table[string, JsonNode], + defaultsApplied: var seq[string]): Doctrine = + ## Repair every bounded field to its default and RECORD the repair, so a cog + ## always ends the doctrine phase with a playable doctrine. + ## + ## `chassis` is NOT read here. It is not in `KnownKeys`, so the envelope has + ## already recorded it in `unknownFields`; the returned doctrine keeps + ## `defaultDoctrine()`'s `awu`, and `decide.nim` logs the seat that tried. + result = defaultDoctrine() + + template repair(name: string) = + defaultsApplied.add(name) + + # --- chassis is NOT read here -------------------------------------------- + # It is not in `KnownKeys`, so the loop above has already recorded it in + # `unknownFields`; `result.chassis` keeps `defaultDoctrine()`'s + # `awu`. `decide.nim` logs the seat that tried. + + # --- backstab_policy / backstab_round ------------------------------------ + if "backstab_policy" in seen: + if seen["backstab_policy"].kind == JString: + let text = normalizeKey(seen["backstab_policy"].getStr()) + var found = false + for value in BackstabPolicy: + if normalizeKey($value) == text: + result.backstabPolicy = value + found = true + if not found: repair("backstab_policy") + else: + repair("backstab_policy") + if "backstab_round" in seen: + let n = readNumber(seen["backstab_round"]) + if n.ok and n.value >= 1.0 and n.value <= 2000.0: + result.backstabRound = int(n.value) + else: + repair("backstab_round") + + # --- cat_engagement ------------------------------------------------------ + if "cat_engagement" in seen: + if seen["cat_engagement"].kind == JString: + let text = normalizeKey(seen["cat_engagement"].getStr()) + var found = false + for value in CatEngagement: + if normalizeKey($value) == text: + result.catEngagement = value + found = true + if not found: repair("cat_engagement") + else: + repair("cat_engagement") + + # --- integer budgets ----------------------------------------------------- + template intKnob(name: string, field: untyped, lo, hi: int) = + if name in seen: + let n = readNumber(seen[name]) + if n.ok and n.value >= float(lo) and n.value <= float(hi): + field = int(n.value) + else: + repair(name) + + intKnob("cat_trap_budget", result.catTrapBudget, 0, 200) + intKnob("rat_trap_budget", result.ratTrapBudget, 0, 200) + intKnob("king_count_target", result.kingCountTarget, 1, 5) + + # --- spawn_curve --------------------------------------------------------- + if "spawn_curve" in seen: + if seen["spawn_curve"].kind == JString: + let text = normalizeKey(seen["spawn_curve"].getStr()) + var found = false + for value in SpawnCurve: + if normalizeKey($value) == text: + result.spawnCurve = value + found = true + if not found: repair("spawn_curve") + else: + repair("spawn_curve") + + # --- cheese_ferry_ratio -------------------------------------------------- + if "cheese_ferry_ratio" in seen: + let n = readNumber(seen["cheese_ferry_ratio"]) + if n.ok and n.value >= 0.0 and n.value <= 1.0: + result.cheeseFerryRatio = n.value + else: + repair("cheese_ferry_ratio") + + # --- dirt_wall_policy ---------------------------------------------------- + if "dirt_wall_policy" in seen: + if seen["dirt_wall_policy"].kind == JString: + let text = normalizeKey(seen["dirt_wall_policy"].getStr()) + var found = false + for value in DirtWallPolicy: + if normalizeKey($value) == text: + result.dirtWallPolicy = value + found = true + if not found: repair("dirt_wall_policy") + else: + repair("dirt_wall_policy") + + # --- throw_rats_to_feed_cats --------------------------------------------- + if "throw_rats_to_feed_cats" in seen: + let b = readBool(seen["throw_rats_to_feed_cats"]) + if b.ok: + result.throwRatsToFeedCats = b.value + else: + repair("throw_rats_to_feed_cats") + +proc toJson*(d: Doctrine): JsonNode = + %*{ + "chassis": $d.chassis, + "backstab_policy": $d.backstabPolicy, + "backstab_round": d.backstabRound, + "cat_engagement": $d.catEngagement, + "cat_trap_budget": d.catTrapBudget, + "rat_trap_budget": d.ratTrapBudget, + "spawn_curve": $d.spawnCurve, + "cheese_ferry_ratio": d.cheeseFerryRatio, + "king_count_target": d.kingCountTarget, + "dirt_wall_policy": $d.dirtWallPolicy, + "throw_rats_to_feed_cats": d.throwRatsToFeedCats + } + +proc plainWords*(d: Doctrine): seq[string] = + ## The endcard/`#doctrines` readout: the sheet in words a spectator can + ## read without knowing the schema. + case d.backstabPolicy + of bpNever: result.add("never betrays") + of bpRetaliateOnly: result.add("retaliates only") + of bpWhenAhead: result.add("betrays when ahead") + of bpAtRoundN: result.add("betrays at round " & $d.backstabRound) + of bpOnFirstContact: result.add("betrays on first contact") + case d.catEngagement + of ceAvoid: result.add("avoids cats") + of ceOpportunistic: result.add("fights cats when handy") + of ceHunt: result.add("hunts cats") + of ceFeed: result.add("feeds rats to cats") + result.add($d.spawnCurve & " spawning") + result.add("keeps " & $d.kingCountTarget & " kings") + result.add($d.ratTrapBudget & " rat traps") + result.add($d.catTrapBudget & " cat traps") + result.add($int(d.cheeseFerryRatio * 100) & "% ferrying") + case d.dirtWallPolicy + of dwNone: result.add("no dirt work") + of dwKingShell: result.add("walls its kings in") + of dwChoke: result.add("walls the chokepoints") + if d.throwRatsToFeedCats: + result.add("throws rats to cats") diff --git a/src/battlecode/years/bc26/rules.nim b/src/battlecode/years/bc26/rules.nim index 84c6360..cc51ec0 100644 --- a/src/battlecode/years/bc26/rules.nim +++ b/src/battlecode/years/bc26/rules.nim @@ -23,7 +23,7 @@ import chassis/[kit, awu, scaffold] export world, constants, kit type - GameOutcome* = object + GameOutcome26* = object index*: int mapName*: string sideAslot*: int ## which SEAT plays team A this game @@ -52,7 +52,7 @@ type ## divergent round rather than the game's last. aborted*: bool -proc slotOf*(outcome: GameOutcome, team: Team): int = +proc slotOf*(outcome: GameOutcome26, team: Team): int = ## Seat index for a team in this game. Sides alternate per game, so the ## mapping is per game, never global. if team == teamA: outcome.sideAslot else: 1 - outcome.sideAslot @@ -118,7 +118,7 @@ proc endReasonFor(w: World): EndReason = elif w.numCats == 0 and w.hasWinner: erCatsCleared else: erRoundLimit -proc harvest(w: World, clans: array[2, Clan], outcome: var GameOutcome) = +proc harvest(w: World, clans: array[2, Clan], outcome: var GameOutcome26) = for team in [teamA, teamB]: let t = ord(team) let slot = outcome.slotOf(team) @@ -145,13 +145,13 @@ proc harvest(w: World, clans: array[2, Clan], outcome: var GameOutcome) = proc playGame*( spec: MapSpec, sheets: array[2, Sheet], index, sideAslot, maxRounds: int, budgetSeconds: int, onRound: proc (w: World, round: int) {.closure.} = nil -): (World, GameOutcome) = +): (World, GameOutcome26) = ## Plays one game to its end, or abandons it when `budgetSeconds` of ## monotonic wall clock elapse. An abandoned game is DISCARDED by the match ## (its `aborted` flag says so); it is never scored half-played. var w = newWorld(spec, maxRounds) let clans = newClans(sheets, sideAslot) - var outcome = GameOutcome( + var outcome = GameOutcome26( index: index, mapName: spec.name, sideAslot: sideAslot, winnerSlot: -1, backstabRound: -1, backstabBySlot: -1 ) diff --git a/src/battlecode/years/dispatch.nim b/src/battlecode/years/dispatch.nim new file mode 100644 index 0000000..2ff2184 --- /dev/null +++ b/src/battlecode/years/dispatch.nim @@ -0,0 +1,262 @@ +## The year boundary: the ONE place the year-neutral machinery meets a year +## module. +## +## `game_config.year` selects a `YearSpec` in `registry.nim`; everything +## downstream — `match`, `results`, `replay`, `render`, `broadcast`, `decide`, +## `server` — holds a `Session` from here and never names `years/bc26/…` or +## `years/bc20/…` itself. Adding 2027 is a new `years/bc27/` directory, one +## registry line, one variant, and one arm in each `case` below. +## +## A `Session` is a Nim object VARIANT, not a `case year: string`: the compiler +## checks that every year has an arm, so a half-added year does not compile. + +import std/[json, strutils] +import ../sim_types, ../sheet +import registry +import bc26/maps as maps26 +import bc26/rules as rules26 +import bc26/world as world26 +import bc20/maps as maps20 +import bc20/rules as rules20 +import bc20/world as world20 +import bc20/chassis/kit as kit20 +export rules20.ChassisKind, rules20.parseChassisKind +import bc20/flood as flood20 + +export registry + +type + YearId* = enum + yBc26 = "bc26" + yBc20 = "bc20" + + Session* = ref object + ## One game in progress, in whichever year's sim. `stepRound` advances it; + ## the renderer and the chrome builder read it through the variant. + mapName*: string + gameIndex*: int + sideAslot*: int + case year*: YearId + of yBc26: + w26*: world26.World + clans26*: array[2, rules26.Clan] + of yBc20: + w20*: world20.World + sides20*: array[2, kit20.Side] + chassis20*: array[2, rules20.ChassisKind] + + GameOutcome* = object + ## The YEAR-NEUTRAL per-game outcome. `results.games[]`'s five required + ## keys come from the named fields; `stats` carries that year's optional + ## siblings, already in SEAT order. + index*: int + mapName*: string + sideAslot*: int + roundsPlayed*: int + winnerSlot*: int + endReason*: string + points*: array[2, int] + hashChain*: string + roundChains*: string + aborted*: bool + stats*: JsonNode + +const Bc20UnitNames* = [ + "hq", "miner", "refinery", "vaporator", "design_school", + "fulfillment_center", "landscaper", "delivery_drone", "net_gun", "cow" +] + ## `RobotKind` ordinals as the replay's `first_build.unit` spells them. + +proc yearIdOf*(year: string): YearId = + case year.strip().toLowerAscii() + of "bc20": yBc20 + else: yBc26 + +# --------------------------------------------------------------------------- +# Year-neutral map access +# --------------------------------------------------------------------------- + +proc poolNamesFor*(year, pool: string): seq[string] = + case yearIdOf(year) + of yBc26: maps26.poolNames(pool) + of yBc20: maps20.poolNames(pool) + +proc drawMapsFor*(year, pool: string, seed, count: int): seq[string] = + case yearIdOf(year) + of yBc26: maps26.drawMaps(pool, seed, count) + of yBc20: maps20.drawMaps(pool, seed, count) + +proc sideAslotFor*(year: string, seed, gameIndex: int): int = + case yearIdOf(year) + of yBc26: maps26.sideAslotFor(seed, gameIndex) + of yBc20: maps20.sideAslotFor(seed, gameIndex) + +proc mapPathFor*(year, name: string): string = + case yearIdOf(year) + of yBc26: maps26.mapPath(name) + of yBc20: maps20.mapPath(name) + +proc mapCardFor*(year, name: string, slot, sideAslot, rounds: int): JsonNode = + ## The per-map facts a seat may legitimately know before writing its + ## doctrine. Both seats see numerically identical cards; `you_are` is the + ## only asymmetry, because every map is symmetric. + case yearIdOf(year) + of yBc26: + let spec = maps26.loadMap(name) + %*{ + "map": spec.name, + "width": spec.width, + "height": spec.height, + "symmetry": ($spec.symmetry).replace("sym", "").toLowerAscii(), + "cheese_mines": spec.cheeseMines.len, + "cats": spec.catWaypointIds.len, + "rounds": rounds, + "you_are": (if sideAslot == slot: "A" else: "B") + } + of yBc20: + var card = maps20.mapCard(maps20.loadMap(name), slot, sideAslot) + card["rounds"] = %rounds + card + +# --------------------------------------------------------------------------- +# Sessions +# --------------------------------------------------------------------------- + +proc chassisForYear*(year: string, scripted: string): + array[2, rules20.ChassisKind] = + ## bc20 selects BOTH the reply sheet and the chassis from `PLAYER_SCRIPTED` + ## (D1: the chassis is never a sheet field). bc26 keeps its own mapping + ## inside `runControllerFor`. + discard year + discard scripted + [rules20.ckBowlOfChowder, rules20.ckBowlOfChowder] + +proc newSession*(year: string, mapName: string, sheets: array[2, Sheet], + sideAslot, maxRounds: int, + chassis: array[2, rules20.ChassisKind], + gameIndex = 0): Session = + case yearIdOf(year) + of yBc26: + let spec = maps26.loadMap(mapName) + result = Session(year: yBc26, mapName: mapName, sideAslot: sideAslot, + gameIndex: gameIndex) + result.w26 = world26.newWorld(spec, maxRounds) + result.clans26 = rules26.newClans(sheets, sideAslot) + of yBc20: + let spec = maps20.loadMap(mapName) + result = Session(year: yBc20, mapName: mapName, sideAslot: sideAslot, + gameIndex: gameIndex) + result.w20 = world20.newWorld(spec, maxRounds) + result.chassis20 = [chassis[sideAslot], chassis[1 - sideAslot]] + result.sides20 = rules20.newSides(sheets, chassis, sideAslot) + +proc stepRound*(s: Session) = + case s.year + of yBc26: rules26.runRound(s.w26, s.clans26) + of yBc20: rules20.runRound(s.w20, s.sides20, s.chassis20) + +proc currentRound*(s: Session): int = + case s.year + of yBc26: s.w26.currentRound + of yBc20: s.w20.currentRound + +proc running*(s: Session): bool = + case s.year + of yBc26: s.w26.running + of yBc20: s.w20.running + +proc hashChainHex*(s: Session): string = + case s.year + of yBc26: toHex(s.w26.hashChain) + of yBc20: toHex(s.w20.hashChain) + +proc mapWidth*(s: Session): int = + case s.year + of yBc26: s.w26.width + of yBc20: s.w20.width + +proc mapHeight*(s: Session): int = + case s.year + of yBc26: s.w26.height + of yBc20: s.w20.height + +# --------------------------------------------------------------------------- +# Playing a game, and converting the year's outcome to the neutral one +# --------------------------------------------------------------------------- + +proc statsJson26(o: rules26.GameOutcome26): JsonNode = + %*{ + "cooperation_at_end": o.cooperationAtEnd, + "backstab_round": o.backstabRound, + "backstab_by": o.backstabBySlot, + "cat_damage": [o.catDamage[0], o.catDamage[1]], + "cheese_transferred": [o.cheeseTransferred[0], o.cheeseTransferred[1]], + "kings_alive": [o.kingsAlive[0], o.kingsAlive[1]], + "kings_built": [o.kingsBuilt[0], o.kingsBuilt[1]], + "rats_built": [o.ratsBuilt[0], o.ratsBuilt[1]], + "rats_alive": [o.ratsAlive[0], o.ratsAlive[1]], + "traps_placed": [o.trapsPlaced[0], o.trapsPlaced[1]], + "dirt_placed": [o.dirtPlaced[0], o.dirtPlaced[1]] + } + +proc statsJson20*(o: rules20.GameOutcome20): JsonNode = + %*{ + "hq_alive": [o.hqAlive[0], o.hqAlive[1]], + "hq_lost_round": [o.hqLostRound[0], o.hqLostRound[1]], + "hq_lost_cause": [o.hqLostCause[0], o.hqLostCause[1]], + "soup_mined": [o.soupMined[0], o.soupMined[1]], + "soup_refined": [o.soupRefined[0], o.soupRefined[1]], + "net_worth": [o.netWorth[0], o.netWorth[1]], + "units_alive": [o.unitsAlive[0], o.unitsAlive[1]], + "units_built": [o.unitsBuilt[0], o.unitsBuilt[1]], + "miners_built": [o.minersBuilt[0], o.minersBuilt[1]], + "landscapers_built": [o.landscapersBuilt[0], o.landscapersBuilt[1]], + "drones_built": [o.dronesBuilt[0], o.dronesBuilt[1]], + "vaporators_built": [o.vaporatorsBuilt[0], o.vaporatorsBuilt[1]], + "net_guns_built": [o.netGunsBuilt[0], o.netGunsBuilt[1]], + "dirt_moved": [o.dirtMoved[0], o.dirtMoved[1]], + "drone_pickups": [o.dronePickups[0], o.dronePickups[1]], + "drone_water_drops": [o.droneWaterDrops[0], o.droneWaterDrops[1]], + "net_gun_kills": [o.netGunKills[0], o.netGunKills[1]], + "transactions_sent": [o.transactionsSent[0], o.transactionsSent[1]], + "transactions_minted": [o.transactionsMinted[0], o.transactionsMinted[1]], + "blockchain_soup_spent": + [o.blockchainSoupSpent[0], o.blockchainSoupSpent[1]], + "global_pollution_peak": o.globalPollutionPeak, + "flooded_tiles_end": o.floodedTilesEnd, + "water_level_end": o.waterLevelEnd + } + +proc playGameFor*( + year, mapName: string, sheets: array[2, Sheet], + chassis: array[2, rules20.ChassisKind], + index, sideAslot, maxRounds, budgetSeconds: int +): (GameOutcome, seq[tuple[round: int, kind: string, a, b, c: int, s: string]]) = + ## Plays one game and returns the neutral outcome plus the year's own event + ## stream, so `match.nim` can map the beats without knowing the year's world. + case yearIdOf(year) + of yBc26: + let spec = maps26.loadMap(mapName) + let (w, o) = rules26.playGame(spec, sheets, index, sideAslot, maxRounds, + budgetSeconds) + (GameOutcome(index: o.index, mapName: o.mapName, sideAslot: o.sideAslot, + roundsPlayed: o.roundsPlayed, winnerSlot: o.winnerSlot, + endReason: $o.endReason, points: o.points, hashChain: o.hashChain, + roundChains: o.roundChains, aborted: o.aborted, + stats: statsJson26(o)), w.events) + of yBc20: + let spec = maps20.loadMap(mapName) + let (w, o) = rules20.playGame(spec, sheets, chassis, index, sideAslot, + maxRounds, budgetSeconds) + (GameOutcome(index: o.index, mapName: o.mapName, sideAslot: o.sideAslot, + roundsPlayed: o.roundsPlayed, winnerSlot: o.winnerSlot, + endReason: o.endReason, points: o.points, hashChain: o.hashChain, + roundChains: o.roundChains, aborted: o.aborted, + stats: statsJson20(o)), w.events) + +proc floodTableJson*(): JsonNode = + ## The round each integer elevation floods at — the single most important + ## fact a bc20 doctrine has to plan around, so it goes in the observation. + result = newJObject() + for level in 1 .. 7: + result[$level] = %flood20.roundWaterReaches(level) diff --git a/src/battlecode/years/registry.nim b/src/battlecode/years/registry.nim index eea3b76..81b6b64 100644 --- a/src/battlecode/years/registry.nim +++ b/src/battlecode/years/registry.nim @@ -26,16 +26,21 @@ type const Years* = [ YearSpec(id: "bc26", title: "Battlecode 2026 — Uneasy Alliances", maxRounds: 2000, pools: @["small", "mixed", "large"], - atlas: "atlas") + atlas: "atlas"), + YearSpec(id: "bc20", title: "Battlecode 2020 — Soup", + maxRounds: 1500, pools: @["small", "mixed", "large"], + atlas: "atlas_bc20") ] proc yearSpec*(id: string): YearSpec = for spec in Years: if spec.id == id.strip().toLowerAscii(): return spec + var known: seq[string] + for spec in Years: known.add(spec.id) raise newException(ConfigError, "unknown game_config.year " & id & "; registered years: " & - Years[0].id) + known.join(", ")) proc isRegisteredYear*(id: string): bool = for spec in Years: diff --git a/tests/test_determinism.nim b/tests/test_determinism.nim index 5c9a9ef..cdd1f37 100644 --- a/tests/test_determinism.nim +++ b/tests/test_determinism.nim @@ -12,7 +12,7 @@ import battlecode/[baselines, match, replay, results, sheet, sim_types] import battlecode/years/bc26/[constants, maps, rules, world] proc play(mapName: string, sheets: array[2, Sheet], rounds: int, - sideAslot = 0): GameOutcome = + sideAslot = 0): GameOutcome26 = let (w, outcome) = playGame(loadMap(mapName), sheets, 0, sideAslot, rounds, 0) outcome @@ -73,7 +73,7 @@ proc deriveAndCompare(config: GameConfig, sheets: array[2, Sheet], for slot in 0 .. 1: doc.names[slot] = "seat" & $slot for g in games: doc.games.add(GameHeader(index: g.index, map: g.mapName, - mapSha: mapSha(g.mapName), sideAslot: g.sideAslot, + mapSha: mapSha("bc26", g.mapName), sideAslot: g.sideAslot, rounds: g.roundsPlayed, hashChain: g.hashChain)) ## Re-derive from the written bytes, exactly as the wasm viewer does. let reparsed = parseReplay($doc.toJson()) @@ -81,7 +81,7 @@ proc deriveAndCompare(config: GameConfig, sheets: array[2, Sheet], while deriver.advance(): discard (reason, deriver.mismatchRound < 0, games) -var seenReasons: seq[EndReason] +var seenReasons: seq[string] block: ## `round_limit`: a short game that neither side can finish. var config = defaultGameConfig() @@ -94,7 +94,7 @@ block: check("and re-derives with no hash mismatch", r.ok) checkEq("one game was recorded", r.outcomes.len, 1) checkEq("and it ended on the round limit", r.outcomes[0].endReason, - erRoundLimit) + $erRoundLimit) seenReasons.add(r.outcomes[0].endReason) block: @@ -109,7 +109,7 @@ block: check("and re-derives with no hash mismatch", r.ok) checkEq("one game was recorded", r.outcomes.len, 1) checkEq("and it ended on the kings", r.outcomes[0].endReason, - erKingsDestroyed) + $erKingsDestroyed) seenReasons.add(r.outcomes[0].endReason) block: @@ -140,7 +140,7 @@ block: checkEq("the cat hunt completes", r.reason, epComplete) checkEq("one game was recorded", r.outcomes.len, 1) checkEq("and it ended with the cats cleared", r.outcomes[0].endReason, - erCatsCleared) + $erCatsCleared) check("and re-derives with no hash mismatch", r.ok) seenReasons.add(r.outcomes[0].endReason) @@ -188,11 +188,11 @@ block: var frames = 0 while deriver.advance(): inc frames checkEq("and stops exactly there", frames, stoppedAt) - checkEq("at the recorded round", deriver.world.currentRound, stoppedAt) + checkEq("at the recorded round", deriver.session.w26.currentRound, stoppedAt) checkEq("with no hash mismatch", deriver.mismatchRound, -1) ## `abandoned` is the one end reason that never reaches `results.games[]` ## (playMatch discards the game); its record -> re-derive is this block. - seenReasons.add(erAbandoned) + seenReasons.add($erAbandoned) block: ## The scoring of a deadline episode is honest: only the games that @@ -214,20 +214,20 @@ block: var doc = ReplayDoc(gameVersion: GameVersion, year: "bc26", config: %*{}, seed: 1, seats: seats, plan: plan, result: %*{}, games: @[GameHeader(index: 0, map: "closeup", - mapSha: mapSha("closeup"), sideAslot: 0, rounds: outcome.roundsPlayed, + mapSha: mapSha("bc26", "closeup"), sideAslot: 0, rounds: outcome.roundsPlayed, hashChain: outcome.hashChain)]) for slot in 0 .. 1: doc.names[slot] = "s" & $slot let text = $doc.toJson() let deriver = newDeriver(parseReplay(text)) while deriver.advance(): discard checkEq("the re-derived chain matches the recorded one", - toHex(deriver.world.hashChain), outcome.hashChain) + toHex(deriver.session.w26.hashChain), outcome.hashChain) checkEq("and no mismatch round is reported", deriver.mismatchRound, -1) ## And once more from the same bytes, to prove the reader is pure. let again = newDeriver(parseReplay(text)) while again.advance(): discard checkEq("re-reading the same bytes gives the same chain", - toHex(again.world.hashChain), outcome.hashChain) + toHex(again.session.w26.hashChain), outcome.hashChain) # --- EVERY end reason was covered above ------------------------------------- block: @@ -235,6 +235,6 @@ block: ## every end reason" was a claim no assertion made (r1-N13b). Now the ## blocks fill it and this fails if one is ever dropped. for reason in EndReason: - check("record -> re-derive covered " & $reason, reason in seenReasons) + check("record -> re-derive covered " & $reason, $reason in seenReasons) finish("test_determinism") diff --git a/tests/test_perf.nim b/tests/test_perf.nim index d6d90c4..7e05431 100644 --- a/tests/test_perf.nim +++ b/tests/test_perf.nim @@ -28,7 +28,7 @@ for name in MixedPool: largest = name echo "largest map in the bc26 pool: ", largest, " (", largestTiles, " tiles)" -proc timeGame(sheets: array[2, Sheet]): (float, GameOutcome) = +proc timeGame(sheets: array[2, Sheet]): (float, GameOutcome26) = let started = getMonoTime() let (w, outcome) = playGame(loadMap(largest), sheets, 0, 0, 2000, 0) ((getMonoTime() - started).inMilliseconds.float / 1000.0, outcome) diff --git a/tests/test_replay.nim b/tests/test_replay.nim index 679f45f..98cb178 100644 --- a/tests/test_replay.nim +++ b/tests/test_replay.nim @@ -10,7 +10,10 @@ proc buildDoc(mapName: string, notes, motto: string): (ReplayDoc, GameOutcome) = var sheets = [baselineSheet(blAwu), baselineSheet(blScaffold)] sheets[0].notes = notes sheets[0].motto = motto - let (w, outcome) = playGame(loadMap(mapName), sheets, 0, 0, 500, 0) + ## Through the YEAR BOUNDARY, exactly as the server does, so the document + ## this test round-trips is the document the server writes. + let (outcome, _) = playGameFor("bc26", mapName, sheets, + [ckBowlOfChowder, ckBowlOfChowder], 0, 0, 500, 0) var plan = MatchPlan(seed: 4242, year: "bc26", maxRounds: 500, maps: @[mapName], sideAslots: @[0], abandonAfter: @[-1], sheets: sheets) var seats: array[2, SeatReport] @@ -24,13 +27,13 @@ proc buildDoc(mapName: string, notes, motto: string): (ReplayDoc, GameOutcome) = ev("game_end", game = 0, round = outcome.roundsPlayed, fields = %*{ "winner_alias": aliasFor(max(0, outcome.winnerSlot)), "winner_slot": outcome.winnerSlot, - "end_reason": $outcome.endReason, + "end_reason": outcome.endReason, "points": [outcome.points[0], outcome.points[1]]})] var doc = ReplayDoc(gameVersion: GameVersion, year: "bc26", config: %*{"pool": "small", "seed": 4242}, seed: 4242, seats: seats, events: events, plan: plan, result: resultsJson(seats, @[outcome], plan, epComplete, 1.0, 2.0), - games: @[GameHeader(index: 0, map: mapName, mapSha: mapSha(mapName), + games: @[GameHeader(index: 0, map: mapName, mapSha: mapSha("bc26", mapName), sideAslot: 0, rounds: outcome.roundsPlayed, hashChain: outcome.hashChain, roundChains: outcome.roundChains)]) for slot in 0 .. 1: doc.names[slot] = seats[slot].name @@ -127,8 +130,9 @@ block: while deriver.advance(): inc frames checkEq("the deriver plays every frame", frames, outcome.roundsPlayed) checkEq("with no hash mismatch", deriver.mismatchRound, -1) - checkEq("and the same final cheese", deriver.world.teamInfo.cheeseTransferred, - [outcome.cheeseTransferred[0], outcome.cheeseTransferred[1]]) + checkEq("and the same final cheese", deriver.session.w26.teamInfo.cheeseTransferred, + [outcome.stats["cheese_transferred"][0].getInt(), + outcome.stats["cheese_transferred"][1].getInt()]) # --- a mismatching chain is DETECTED, at the round it FIRST diverges -------- block: @@ -169,14 +173,14 @@ block: let deriver = newDeriver(parseReplay($doc.toJson())) ## Frames are 0-based and round `n` is frame `n - 1`. deriver.seek(outcome.roundsPlayed div 2) - let midway = deriver.world.hashChain + let midway = deriver.session.w26.hashChain checkEq("a forward seek lands on the right round", - deriver.world.currentRound, outcome.roundsPlayed div 2 + 1) + deriver.session.w26.currentRound, outcome.roundsPlayed div 2 + 1) deriver.seek(10) checkEq("a backward seek restarts and replays", - deriver.world.currentRound, 11) + deriver.session.w26.currentRound, 11) deriver.seek(outcome.roundsPlayed div 2) - checkEq("and returning gives the same world", deriver.world.hashChain, + checkEq("and returning gives the same world", deriver.session.w26.hashChain, midway) # --- the chrome document ---------------------------------------------------- @@ -187,7 +191,7 @@ block: discard deriver.advance() var view = initViewerState() let beats = beatsFor(back, proc (g, r: int): int = 0) - let chrome = parseJson(chromeJson(back, deriver.world, view, 0, + let chrome = parseJson(chromeJson(back, deriver.session.w26, view, 0, deriver.totalFrames, 0, 0, beats, newJArray(), false)) for key in ["t", "st", "mx", "mt", "sp", "pl", "lp", "sk", "ff", "en", "ph", "beats"]: diff --git a/tests/test_viewer.nim b/tests/test_viewer.nim index d439e35..682ac7f 100644 --- a/tests/test_viewer.nim +++ b/tests/test_viewer.nim @@ -311,7 +311,7 @@ block: var doc = ReplayDoc(gameVersion: GameVersion, year: "bc26", config: %*{}, seed: 9, seats: seats, plan: plan, result: %*{}, games: @[GameHeader(index: 0, map: "DefaultSmall", - mapSha: mapSha("DefaultSmall"), sideAslot: 0, + mapSha: mapSha("bc26", "DefaultSmall"), sideAslot: 0, rounds: outcome.roundsPlayed, hashChain: outcome.hashChain)]) for slot in 0 .. 1: doc.names[slot] = "s" & $slot @@ -321,9 +321,9 @@ block: var view = initViewerState() discard deriver.advance() let beats = beatsFor(back, proc (g, r: int): int = 0) - let first = renderer.buildPacket(deriver.world, 0, 0, - chromeJson(back, deriver.world, view, 0, deriver.totalFrames, 0, 0, - beats, newJArray(), false)) + let first = renderer.buildSessionPacket(deriver.session, + sessionChromeJson(back, deriver.session, view, 0, deriver.totalFrames, + 0, 0, beats, newJArray(), false)) check("the first frame is a non-empty sprite packet", first.len > 1000) ## The first packet must carry the layer, the viewport, the terrain sprite ## and the chrome sprite. @@ -341,7 +341,7 @@ block: elif m.sprite.label == "terrain": sawTerrain = true checkEq("the terrain sprite is the whole board", - m.sprite.width, deriver.world.width * render.TileSize) + m.sprite.width, deriver.session.w26.width * render.TileSize) of spkLayer: sawLayer = true checkEq("the board layer is zoomable", m.layer.flags, 1) @@ -356,8 +356,8 @@ block: for step in 0 ..< 30: discard deriver.advance() - let later = renderer.buildPacket(deriver.world, 0, 0, - chromeJson(back, deriver.world, view, deriver.frame, deriver.totalFrames, + let later = renderer.buildSessionPacket(deriver.session, + sessionChromeJson(back, deriver.session, view, deriver.frame, deriver.totalFrames, 0, 0, beats, newJArray(), false)) check("a later frame is a DIFF, not the whole board", later.len < first.len) check("but still carries something to draw", later.len > 16) diff --git a/tools/JavaWaterLevels.java b/tools/JavaWaterLevels.java new file mode 100644 index 0000000..577279b --- /dev/null +++ b/tools/JavaWaterLevels.java @@ -0,0 +1,64 @@ +// Regenerates `data/bc20/water_levels.json` — CI-ONLY, never in an image. +// +// `GameConstants.getWaterLevel` is +// +// (float)(Math.exp(0.0028*x - 1.38*Math.sin(0.00157*x - 1.73) +// + 1.38*Math.sin(-1.73)) - 1) +// +// and `Math.exp` / `Math.sin` are HotSpot INTRINSICS that are not bit-identical +// to any libm this port would link natively or under emscripten. The function +// depends only on the round number, so the whole domain is emitted here, once, +// under the JDK, as float32 BIT PATTERNS, and the sim (native and wasm) reads +// the committed table. Bit-exact against Java by construction, on every +// platform, with no libm in the loop. +// +// javac -d /tmp/wl tools/JavaWaterLevels.java +// java -cp /tmp/wl JavaWaterLevels > data/bc20/water_levels.json +// +// The `parity-oracle` CI job regenerates and byte-diffs this file as a BLOCKING +// step. The emitted table is also checked against `StrictMath` here, so a JDK +// whose intrinsics disagree with the reference implementation is reported +// rather than silently baked in. +public final class JavaWaterLevels { + + static final int MAX_ROUND = 1500; + + static float waterLevel(int roundNumber) { + double x = roundNumber; + return (float) (Math.exp(0.0028 * x - 1.38 * Math.sin(0.00157 * x - 1.73) + + 1.38 * Math.sin(-1.73)) - 1); + } + + static float strictWaterLevel(int roundNumber) { + double x = roundNumber; + return (float) (StrictMath.exp(0.0028 * x - 1.38 * StrictMath.sin(0.00157 * x - 1.73) + + 1.38 * StrictMath.sin(-1.73)) - 1); + } + + public static void main(String[] args) { + int strictMismatches = 0; + StringBuilder out = new StringBuilder(); + out.append("{\"source\":\"battlecode20 GameConstants.getWaterLevel\","); + out.append("\"encoding\":\"float32 bit pattern, 8 hex digits, big-endian\","); + out.append("\"max_round\":").append(MAX_ROUND).append(','); + out.append("\"levels\":["); + for (int r = 0; r <= MAX_ROUND; r++) { + float v = waterLevel(r); + if (Float.floatToRawIntBits(v) != Float.floatToRawIntBits(strictWaterLevel(r))) { + strictMismatches++; + } + if (r > 0) { + out.append(','); + } + out.append('"'); + out.append(String.format("%08x", Float.floatToRawIntBits(v))); + out.append('"'); + } + out.append("]}\n"); + System.out.print(out); + if (strictMismatches > 0) { + System.err.println("WARNING: Math and StrictMath disagree on " + + strictMismatches + " of " + (MAX_ROUND + 1) + " rounds"); + } + } +} diff --git a/tools/__pycache__/convert_maps_bc20.cpython-311.pyc b/tools/__pycache__/convert_maps_bc20.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e5d7ab557e649d504a6327466b55a3b1ac1286e GIT binary patch literal 18695 zcmdUX`)?apzTXUA;+uL|b~TNf*ws90W>?Fog7^f^Cj70seWtP-vF zjH5OKcbXGzA9AAoD+9-Ujh_}q?d*vIPn;D`9HQ%86UU7@M_r@tQIF_;7yTQp9<3Rz z9jzPnj@IX&H;ir?Z5(Y9J@2B{(aob<#2w=14^5-ZVu#p@Ym2y36mV@7cZqGdZWTMl zc3g$PHgWqU))T4Dzy;dy6TU*XzAki*`wDpY8b2+3fbn18I(PdcW{x`<3110Fig3cO zD51baL<|Uf_w3yxjP=a;XZP+I6DC7`WqfXOG9bx<5)sBkzvA!3Gr4zsV(*^bBVqqc z;8@Q^IT9Z0vD;4$b&m|48x+nDpM9rC7?}=80U;<0;Ye9Mf)wzJ0jY~pU!PZ|BVoZG z7U}NQx6TPcx|_F4b73VogTgcZsem9WfQJNS8qweks{p;h-9%(&Ca5R@QF!&j`QaX6 zObN(}+)L0${qs>zd&0q)F}oD#p3nv>v-ZT`sEk7HZHqe*OojToMqE9 z6$l3;KOr(dCF8-c7z|GVLy>T3UKo>6e8%5<(SOB1)@3i=%3}9L)$AKLdZW)2u8(|!D|@s zcxiMc-1JP2%VT!G!YY~#hQr9H5gQvF3j1Xl3kS)Q!9a-B!XlXd3-6peH#qXfJHpTf zVfg%rFf=TToEa2O4V@j-dZ1rn8fHOTY$D|OnVEnh&1=;XQtUz2*@3~4!IL9{147`M zpADLDb=5!5dYC6DF#?0}ks_hqlaZ@f=M;Tiid+d|@%2mvlneQm#tsP+ zn6V%zj+JH=MkM62j}dPOPWVG(UBcLOL<&YxjdmwR6u%OTgmHIR2>2(ag+M4c6AUvN z0!rCT1|=C7q(yR-Sb<%R%*}QoW8exaE9`2+6e?|GI)gnT7fC%O1;-8IRQAQ-yB?jc+RM-bv z!Fo|^C+9-oJ)xOESn*8&pqxE~Nt7r2vxvJzOa|s|-X}^xfLav}D88w=uxgx$T&24? zU^Arh{VIQ=(zhYqf3dix^A4AezF2IfQ*iQ|jTQpLcss}onN=)#Uutr}0vO{ykhZK`>E9^|H4q<}Ifg;moq`ZhEyws6=VK2Dh};G40Y3~(@?xh!HOYa{q*RXwG8Geq=SO;#nOU|)bu+GvwJ?R9 zs=Ktp!FL(MUx!1hR-Z2z4k|uh)QKMV6z*Q623!DfE>}~ZFfCdV_)u^1OIvWvx$$M~->>aX$Bf@o+eL^0-0<8ah)Lk^qoLiPVC)YMguVK(vYmg~&dneBTNX=bn1% zvz~3K?aQWzmK8(B^HSFH(n^2Ub0BR!AZwd=WP${BX~I|0x@>eusz| zuwN8;EbjPJk;fu?F~kgd?I;Sp5+#f6pd7WcCV*!b^XO|pY2y1V&^{$?MzqtS^5aq; zVvO)qi#A+FkE-cPC=kXz%vd&KZIn@_`k_f^G)dGObu;`H_oLI4>o@@IM_$^Ga&-;3 zSaS_c$&;U*{`7R}{| zK+?S&P;kkM+GDJvP))JhFCSq()7EZ=`sa2C4nj;=BLfxje*?|j z1y)nIt__eGfbEioPctL(d7FTNXs~=fG;A*zV@9MF*F>sF&E@sSjQW~so*;P{h6~8- zM}kJy45P$Ufnr9;44FJ+bEL^M640_n%&L`?OVZs66kWICMH5Ow`EqV~u0NdO2lw%hOOc%VvDMLDM`d{G~}5OD=j8F zl)jK(@%!I;-@Z2ZxSF8dVT6A@s_5pw1<~= zZAeqRwp0AIP6O4X8H2WglB!u)+XCXOLEYMxE+9GT(iU|dg%Kplq}5{dv@E&#hQ%Ea6ZRoXr%^Q#8cOurX;KY6dnA%zHL-gfu{?_7 zai%*yw;v(=!F@4%EZU~AVnQ=j)N>>hfv_MSL!^-BBdUQmy+6gdwC9NSUG{%$S{k|Y z)}6PLft3G~V77i&rn)m*-C4}RrV4D0tv55Q^-}@eCOJV}EcR|p5MI$tlGZ|B3d%s? z{;#PYq+tCC9a(>Y6?&(2;&=NW)%>>an?BrSJV&y4Xg$JuHQZ^Dm|pFrJ}@nO8^y{E zOUyJ@N~IXOUy*uZ-PF^R$}_HmhDt@4#P;W^Bmo?y1Y9&@2ivqurD*$J+mW}|O(pGM zZC;W#I;$nJUQ2rkyab?D`Fvtz!spZMV*QlP_McZNc9y_-0&fwZO`OfEBvNdO0PW(8 zCK-k`BL!1z2n^sSZ{mqF7Ct%CVA?deYBHHSS2;a!n$10{oF2H%=5|Ue2X2G;;sw+2|v<|9;TIdJplU1Yl}2NpBk>Vey2KFL$hE1+{tik8P|FdwB( ztP3=p5vDL*;6$8MLx9W_--4kLL6A)Ft(ky@0P&Y^tpscU1?hoGsphLe5pHZE8Pzf! z2u@8aFk-;fCJ{Zr-vS?->Ku=V^FDH~`TWXrw-}sYn>`$iiu9hg5Qq{dsKr8{mKb}b zE`q)C#MMOpbAG`rx)ahDn3mkSO97qi5yF^66=xXvNHDrT!pFShA~am!xf z$hdQs?k>?BadIt8DCK#KYx77AG4lpGi$mXAo?Co7W=fd~$S=Rf?E1V5t_^cKD{{JH zuDBuQet&Ds^}ZEXOUxB>YyFq%e?s5BQXHioRpMQAT*6uh0poa0m|y(oAD!2*#4kEx zX2}{ei)Hu?^P0oZ|65c$_GjGJU68z)BR+(48E|Wc=JQ7|M(BV@!Lu#U_C`*BXv@r- zf317Qe-YLxII6?s$|Z-j=0@Ks3z4f~Ary(s!Z8g8bSFE0L8lu0Wfh_>cvoj4&awmWOu05S_)8m{9jRd~(tMHtoAl;oBdgH=~aN*52-Wq!A{2PTM z7*J$kH(;K4twS_c;THv;f0t15_;Jovif)A(0t}B_=mA zR;o!$()(2R2L!GHD~v z-ZPZ*Hs3ve`+TZDBilEdg`fN>bt(MxRBx> zd$y%L+j7IkMR>(37Munude1HT|VVztsZM2IkU}%Lhl(@3_x5-3^FEH3(TAHV>ZA{lT^sNDZI&L^5^73=F=ur5?>Si+*uyK%mCYqmpn zw8g5}wqTE0>AK)taKs!jCrNfpZYslVX<$l2DX#Cqf*Tkw!)+;_vbhwmDC0qy_1kIQ zuuS!aWytrN4;Agyh~}6Z<_k;A16UQS2DHX%CJmD&NWX*33Rk;Orx0__hghAyj+iSi z4R-$^b=YFHpb`5I)r9AcnCtJj`&NAmAMlH(FgDuJ)&zxi_k7ToTJTPB3-v*6p&{m7 z;@>|P^KO9Ajzal0bhZg)NaJ4(P@mQ7R+tdlkAR&&9UJ8e#%?$+m))NS#~!6yqZKa7_?hA94@f`g&1|DJ87QiUj3E zDOF#FxR)|mdP?`PnwUG*@D+I4*Z64xHMbYy8_&YjY_<_VF`E46RSq+^`hsV z144JTW^Iq!4jl?eQbam*DB5_44)fzQ>&b|OgRFR4;F=a=HVr1$!DfMj!jfv6jf8L> z2nT_f$oJfENQl~Tz+1w(X6PC)&-u;`4Qto_w^jRv zd07d}3|B!-~uxas#e^L4ECS9pvB&K9T}>ScJ}z7!7D@O2urM@vcK4q#8lV%<_gA^Re?AszsYWO|LNqMHxB^ z2%0XSXc>;G$OL+I4ZnGrne|npL}Y&nXJkqQFSlVBWopd^QBB*a%G0rwMxlSiPyUw> zaG}mRJU5TuIG%AdXC2M)fv0v?LcHF&*cm^W^VHtFdgJQS!&doypi* zvbL7^iD&NWn=?0N?w`uI+q3TW`03o5gerIZSIM2p-ly)ybkmvC$QSS6qJ1;&p{#o- zZ5_g4wYvvzA50$1)U{{p+EW*P^YhPtzWj6OFu%y`csaY{<;4E@;E&7XZAuQ_I+xnN zy#Lon69-VrQN48F`my-HGl%Qup&N%jp2|2{vyRsIK+fr=V@3C^8RxdFa~rZ&*WR_? zw%$zNN)u%gCR z*rx$<`(G~Ie8=&1NB>9w&s;~LH#wVEt(>>vZr|;``$L(!j%-~={A{jiOZ-%>eFtJy zb(yNIIrru#?zYG7wl5E4+`F^x-3eo^y7@_U`{U~NFVAGEyR+5Z3G;eMo-I#2+n}j^ zxijP0o%QU-!M5$Y(%bhXMsB`){#CM>uy*m`g6|Oo9Awv zdx|5C?YE}yOh0XGxj&KGvCMz5>yw##Gug(jTx0Xo`o?6zf zwQx0ccWt+A_jhKh+p^VdxtfMtZR1mKdunj`z{aN z-Pujun2FC?K5h9gI}c`>`m#-Zx%wt5THl(h_U1MVtDBro%POY4{N7(Mn!y}!B=X}?5{t#Z3un`aSVULnkyBwG$`(H$o zpYwKlje=g|Q_bSs%&ZK5R)#o5X=5al4ZV&dsYw45Jre-p91I-J>cvA#6RDAvi)lxH z#?ha3^#6V!<2aKxonZret{pBQSHlF#6hm=%RNY3yD>wL;jtMA5)wNPYP89{BVmpXb zD(5Nks;mimLx5gbTHy2w(ua^NQf0j;Ri>|{(s!({mDw$$b$FW)8xLMc(cTiIiO>;( z3AHnsh9u~_+%!3tZ3{S~1CiZCCW+g|Mf0LP?k zoWd=Xg`J?6%VeFxV;(dh??8FDgKLjz-=z1ESBtImFTGeyMubgxr%lRH+vZTjk1eh$ zZ>CXA%ua*T`t;@`PUzd{Wj)rgYGdMz61b+|wH9X3$8i^bXb!RtubLTd%8Ail>az4L zn)=@~zH!#cry$2ypW3Pyn?Ih;*tTYETc6lEAKN<9yN_pVuVihnJh8s^*!o(=dM;}{ z_nnb!z`%LYzETH3f)^R*-I?hI@qhq21woI>J zo;IRmJyk3a^^l~xCeUJ5%BZt+2u3tMG<}5@z9wp50qVp?wU{@m<)KB-D1M+0y!9+{ z4{bj>zg=`Zta$&K)xPeEtiT6o`C@U@qALn_6X=@1STv!{x|A{{yP`CXP+TmUs@7Mk zMfeM#jsfSTgSk8$nlQr|v5Nllf*B?{GmIFX`v%s!C1y#LIlT%v2fsn|>*B54qcj$8 zxQnhrJmoIL%CBN|`8d&94QsRZ{u*@?Z>rbfz*L!PsWZM*7|Qfq1qQY@J_hO?}p4^4*R7knH^4! zR^?wZhhzbvBH+Wzi2*Qg3*J|Th9BLn`9OPRX#zHZV7RRPUhRctDS%@O(gaw(f%Xi@ z8;Y!21J~fX^+hg4E!v+FL_IS>xIN)srFZb*dzJ1|-<}X4%8r`Z{}DvH@@fxk2lQV6 zlp8Kvy!H<-XYt(;txqsJ7`lY$j^exKa+UjysuHZc+8r zluZ*&t>nNGFySW;g5ToxQd>>dCS+`FSzBBDM9yUS===xgmtI}={_gZQ&{E<&T_ZeuekQIi~&K>tRf6{!fIX?KzWc~2W zN5dZsC-*IHj}K=|y;)Ol+SCiRkyOR{RnB4F&H(hGn#QDfV{ zleZ#wA{kFd*3&_))Zb}LD#>B`Xze&Wi-(|X+P3HFn(iLGee~AxJ2>jZyV_HePdW}f z?l_R?=*xEWJsNo2@k+Ym6?FTq^R_cNqA4)x>b5^UbJjj_wmf#WB*l!gJ?m^wvv01- z@nLk8t1|aNfySw-iZQ&F+uHuw{HOEF^_i``*{!{hom%(g_8y??LCl`3kK`!wg1Y*i z(Th({t*(#jmfpOz`OfCF`-Jw*SWjlHC)4cv2dgXXKJc*&tuJ~fiYUIz0}sz=v5fU# z)_M?rX`G`?yr^kN5FLrWe7;VnW{BBFbbgHhi;=C29edKuGIRinnOdY6#WoQj_ccyk z-JI@_gLTxL*U;NpzTgG)jguvmjq}hl^zlx6OFV?-~oYuO@QPL=|2$oHw6AIfqzF}g}`qK z`~!g{0%-z_C45UUT8!H9zu(a9J_00G6zrYa3$w>0H}c`j$)5qj^uY70rbgbh$`RPd z@$G4@t^E6p^TgRF$2sEcljGcR_Q`RsIQ!%{dz^h%Ee5xMb&&^gJFFM3hGcuHJ#Fp4 zH*Q{a*YJ3Qb}jgh(*9U_u file under client/visualizer/src/static/img +SPRITE_FILES = { + "hq_red": "sprites/HQ_red.png", + "hq_blue": "sprites/HQ_blue.png", + "miner_red": "sprites/Miner_red.png", + "miner_blue": "sprites/Miner_blue.png", + "landscaper_red": "sprites/Landscaper_red.png", + "landscaper_blue": "sprites/Landscaper_blue.png", + "drone_red": "sprites/Drone_red.png", + "drone_blue": "sprites/Drone_blue.png", + "drone_red_carry": "sprites/Drone_red_carry.png", + "drone_blue_carry": "sprites/Drone_blue_carry.png", + "refinery_red": "sprites/Refinery_red.png", + "refinery_blue": "sprites/Refinery_blue.png", + "vaporator_red": "sprites/Vaporator_red.png", + "vaporator_blue": "sprites/Vaporator_blue.png", + # SOUPER_* is the 2020 client's own file name for the Design School. + "design_school_red": "sprites/SOUPER_red.png", + "design_school_blue": "sprites/SOUPER_blue.png", + "fulfillment_center_red": "sprites/Fulfillment_red.png", + "fulfillment_center_blue": "sprites/Fulfillment_blue.png", + "net_gun_red": "sprites/Net_gun_red.png", + "net_gun_blue": "sprites/Net_gun_blue.png", + "cow": "sprites/Cow.png", + "soup": "soup.png", +} + + +def cell(img: Image.Image, size: int) -> Image.Image: + return img.convert("RGBA").resize((size, size), Image.LANCZOS) + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--engine", required=True, type=pathlib.Path) + ap.add_argument("--out", required=True, type=pathlib.Path) + ap.add_argument("--check", action="store_true") + args = ap.parse_args() + + img_dir = args.engine / "client/visualizer/src/static/img" + sprites: list[tuple[str, Image.Image]] = [] + for name in sorted(SPRITE_FILES): + source = img_dir / SPRITE_FILES[name] + if not source.exists(): + raise SystemExit(f"::error::missing 2020 client sprite: {source}") + sprites.append((name, cell(Image.open(source), TILE))) + + width_cell = max(s.width for _, s in sprites) + height_cell = max(s.height for _, s in sprites) + rows = (len(sprites) + COLUMNS - 1) // COLUMNS + atlas = Image.new("RGBA", (COLUMNS * width_cell, rows * height_cell), + (0, 0, 0, 0)) + index: dict[str, dict[str, int]] = {} + for i, (name, sprite) in enumerate(sprites): + x = (i % COLUMNS) * width_cell + y = (i // COLUMNS) * height_cell + atlas.paste(sprite, (x, y)) + index[name] = {"x": x, "y": y, "w": sprite.width, "h": sprite.height} + + args.out.mkdir(parents=True, exist_ok=True) + png = args.out / "atlas_bc20.png" + meta = args.out / "atlas_bc20.json" + doc = json.dumps({"tile": TILE, "sprites": index}, + sort_keys=True, separators=(",", ":")) + "\n" + + if args.check: + if not meta.exists() or meta.read_text() != doc: + print("atlas_bc20.json differs from a fresh cut of the client art") + return 1 + print("bc20 atlas matches the 2020 client art") + return 0 + + atlas.save(png, optimize=True) + meta.write_text(doc) + print(f"wrote {png} ({png.stat().st_size} bytes) and {meta}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/convert_maps_bc20.py b/tools/convert_maps_bc20.py new file mode 100644 index 0000000..6378c86 --- /dev/null +++ b/tools/convert_maps_bc20.py @@ -0,0 +1,298 @@ +#!/usr/bin/env python3 +"""Convert Battlecode 2020 `.map20` flatbuffers to `data/maps/bc20/.json`. + +CI-TIME ONLY. There is no flatbuffer reader, no Python and no JVM in any +runtime image stage: the sim reads the committed JSON. `tests/test_bc20_maps.nim` +re-converts and diffs (through `tools/ci/check_bc20_maps.sh`), so a hand-edited +map file fails the build. + + tools/convert_maps_bc20.py --engine /path/to/battlecode20 --out data/maps/bc20 + tools/convert_maps_bc20.py --engine ... --out data/maps/bc20 --check + +battlecode20 ships no generated Python bindings (only `schema/java`, +`schema/js` and `schema/ts`), and `flatc` is not available in the coworld +toolchain, so the four tables this converter needs are read directly off the +wire with the vtable walk below. The layout is fixed by `schema/battlecode.fbs` +at the pinned commit and is asserted field by field. + +SYMMETRY IS NOT IN THE FILE. The 2020 `.map20` schema has no symmetry field, so +it is DETECTED exactly the way the engine does in +`world/control/CowControlProvider.getSymmetry`: candidates in the order +`vertical`, `horizontal`, `rotational`; each eliminated by the first tile where +soup, elevation ("dirt") or robot type disagrees under that transform; the first +survivor wins; `rotational` when none survives. Engine naming: `vertical` flips +x (`symmetricX = w-1-x`), `horizontal` flips y (`symmetricY = h-1-y`), +`rotational` flips both. +""" + +from __future__ import annotations + +import argparse +import json +import pathlib +import struct +import sys + +# schema/battlecode.fbs `enum BodyType : byte`, in declaration order. +BODY_TYPES = ["hq", "miner", "refinery", "vaporator", "design_school", + "fulfillment_center", "landscaper", "delivery_drone", + "net_gun", "cow"] +# world/TeamMapping.java: 1 -> A, 2 -> B; cows are NEUTRAL (team id 0). +TEAMS = {0: "neutral", 1: "A", 2: "B"} + + +# --------------------------------------------------------------------------- +# A minimal flatbuffer reader (little-endian, as the format mandates) +# --------------------------------------------------------------------------- + +class Buf: + def __init__(self, data: bytes) -> None: + self.d = data + + def u16(self, at: int) -> int: + return struct.unpack_from(" int: + return struct.unpack_from(" int: + return struct.unpack_from(" int: + return struct.unpack_from(" int: + return struct.unpack_from(" None: + self.b = buf + self.pos = pos + self.vtable = pos - buf.i32(pos) + self.vlen = buf.u16(self.vtable) + + def offset(self, field: int) -> int: + """Byte offset of field `field` from the table position, 0 if absent.""" + slot = 4 + field * 2 + if slot >= self.vlen: + return 0 + return self.b.u16(self.vtable + slot) + + def indirect(self, field: int) -> int | None: + o = self.offset(field) + if o == 0: + return None + at = self.pos + o + return at + self.b.i32(at) + + def table(self, field: int) -> "Table | None": + at = self.indirect(field) + return None if at is None else Table(self.b, at) + + def struct_pos(self, field: int) -> int | None: + # An inline struct lives AT the field offset, not behind an indirection. + o = self.offset(field) + return None if o == 0 else self.pos + o + + def scalar_i32(self, field: int, default: int = 0) -> int: + o = self.offset(field) + return default if o == 0 else self.b.i32(self.pos + o) + + def string(self, field: int) -> str: + at = self.indirect(field) + if at is None: + return "" + length = self.b.u32(at) + return self.d_slice(at + 4, length).decode("utf-8") + + def d_slice(self, at: int, length: int) -> bytes: + return self.b.d[at:at + length] + + def vector(self, field: int) -> tuple[int, int]: + """(first element position, element count); (0, 0) when absent.""" + at = self.indirect(field) + if at is None: + return (0, 0) + return (at + 4, self.b.u32(at)) + + def vec_i32(self, field: int) -> list[int]: + start, n = self.vector(field) + if n == 0: + return [] + return list(struct.unpack_from(f"<{n}i", self.b.d, start)) + + def vec_i8(self, field: int) -> list[int]: + start, n = self.vector(field) + if n == 0: + return [] + return list(struct.unpack_from(f"<{n}b", self.b.d, start)) + + def vec_bool(self, field: int) -> list[bool]: + start, n = self.vector(field) + return [bool(self.b.d[start + i]) for i in range(n)] + + +# GameMap field ids, in `schema/battlecode.fbs` declaration order. +GM_NAME, GM_MIN, GM_MAX, GM_BODIES = 0, 1, 2, 3 +GM_SEED, GM_DIRT, GM_WATER, GM_POLLUTION, GM_SOUP, GM_INITIAL_WATER = 4, 5, 6, 7, 8, 9 +# SpawnedBodyTable field ids. +SB_IDS, SB_TEAMS, SB_TYPES, SB_LOCS = 0, 1, 2, 3 +# VecTable field ids. +VT_XS, VT_YS = 0, 1 + + +def detect_symmetry(width: int, height: int, soup: list[int], + dirt: list[int], body_type_at: dict[int, str]) -> str: + """`CowControlProvider.getSymmetry`, ported literally. + + The candidate list is scanned x-major then y (the engine's own loop order) + and a candidate is dropped at the first disagreeing tile. The engine breaks + out of both loops the moment one candidate is left and returns + `possible.get(0)`, so the answer is the FIRST SURVIVOR in the declaration + order [vertical, horizontal, rotational], defaulting to rotational. + """ + possible = ["vertical", "horizontal", "rotational"] + + def sym_x(x: int, s: str) -> int: + return x if s == "horizontal" else width - 1 - x + + def sym_y(y: int, s: str) -> int: + return y if s == "vertical" else height - 1 - y + + for x in range(width): + for y in range(height): + here = x + width * y + for s in list(reversed(possible)): + there = sym_x(x, s) + width * sym_y(y, s) + if soup[here] != soup[there] or dirt[here] != dirt[there]: + possible.remove(s) + continue + a = body_type_at.get(here) + b = body_type_at.get(there) + if (a is not None) or (b is not None): + if a is None or b is None or a != b: + possible.remove(s) + if len(possible) <= 1: + break + if len(possible) <= 1: + break + return possible[0] if possible else "rotational" + + +def convert(path: pathlib.Path) -> dict: + data = path.read_bytes() + buf = Buf(data) + root_pos = buf.u32(0) + gm = Table(buf, root_pos) + + min_pos = gm.struct_pos(GM_MIN) + max_pos = gm.struct_pos(GM_MAX) + if min_pos is None or max_pos is None: + raise SystemExit(f"::error::{path.name}: no minCorner/maxCorner") + min_x, min_y = buf.i32(min_pos), buf.i32(min_pos + 4) + max_x, max_y = buf.i32(max_pos), buf.i32(max_pos + 4) + width, height = max_x - min_x, max_y - min_y + size = width * height + + dirt = gm.vec_i32(GM_DIRT) + water = gm.vec_bool(GM_WATER) + pollution = gm.vec_i32(GM_POLLUTION) + soup = gm.vec_i32(GM_SOUP) + for label, arr in (("dirt", dirt), ("water", water), + ("pollution", pollution), ("soup", soup)): + if len(arr) != size: + raise SystemExit(f"::error::{path.name}: {label} array is " + f"{len(arr)}, expected {size}") + + bodies_table = gm.table(GM_BODIES) + bodies: list[dict] = [] + body_type_at: dict[int, str] = {} + if bodies_table is not None: + ids = bodies_table.vec_i32(SB_IDS) + teams = bodies_table.vec_i8(SB_TEAMS) + types = bodies_table.vec_i8(SB_TYPES) + locs = bodies_table.table(SB_LOCS) + xs = locs.vec_i32(VT_XS) if locs is not None else [] + ys = locs.vec_i32(VT_YS) if locs is not None else [] + for i, rid in enumerate(ids): + kind = BODY_TYPES[types[i]] + x, y = xs[i] - min_x, ys[i] - min_y + bodies.append({ + "id": int(rid), + "team": TEAMS[int(teams[i])], + "type": kind, + "x": int(x), + "y": int(y), + }) + body_type_at[x + width * y] = kind + bodies.sort(key=lambda b: b["id"]) + + return { + "name": gm.string(GM_NAME), + "width": width, + "height": height, + "symmetry": detect_symmetry(width, height, soup, dirt, body_type_at), + "random_seed": gm.scalar_i32(GM_SEED), + "initial_water": gm.scalar_i32(GM_INITIAL_WATER), + "elevation": dirt, + "water": water, + "pollution": pollution, + "soup": soup, + "initial_bodies": bodies, + } + + +def render(doc: dict) -> str: + # Compact and stable: the committed file is diffed byte for byte in CI, so + # the serialisation has to be deterministic. + return json.dumps(doc, sort_keys=True, separators=(",", ":")) + "\n" + + +def pool_names() -> list[str]: + pools = json.loads( + (pathlib.Path(__file__).with_name("map_pools_bc20.json")).read_text()) + return sorted({n for pool in pools.values() for n in pool}) + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--engine", required=True, type=pathlib.Path, + help="a checkout of github.com/battlecode/battlecode20") + ap.add_argument("--out", required=True, type=pathlib.Path) + ap.add_argument("--only", nargs="*", default=None) + ap.add_argument("--check", action="store_true") + args = ap.parse_args() + + map_dir = args.engine / "engine/src/main/battlecode/world/resources" + names = args.only or pool_names() + + args.out.mkdir(parents=True, exist_ok=True) + drift = [] + for name in names: + source = map_dir / f"{name}.map20" + if not source.exists(): + sys.stderr.write(f"missing map at {args.engine}: {name}\n") + return 1 + text = render(convert(source)) + target = args.out / f"{name}.json" + if args.check: + if not target.exists() or target.read_text() != text: + drift.append(name) + else: + target.write_text(text) + if args.check: + if drift: + sys.stderr.write("converted bc20 maps differ from the engine: " + + ", ".join(drift) + "\n") + return 1 + print(f"{len(names)} converted bc20 maps match the engine") + else: + print(f"wrote {len(names)} maps to {args.out}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/gen_year_constants.py b/tools/gen_year_constants.py index dcee29d..2e83acb 100644 --- a/tools/gen_year_constants.py +++ b/tools/gen_year_constants.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Generate src/battlecode/years/bc26/constants.nim from the pinned engine. +"""Generate src/battlecode/years//constants.nim from the pinned engine. CI-TIME ONLY (Python and a Java *source* checkout; no JVM is involved, and neither exists in any runtime image stage). The Battlecode 2026 rule set has @@ -13,6 +13,13 @@ --out src/battlecode/years/bc26/constants.nim tools/gen_year_constants.py --engine ... --check # diff, exit 1 on drift +`--year bc20` does the same job for Battlecode 2020 "Soup" against a checkout +of github.com/battlecode/battlecode20 at the pinned commit, reading +`common/GameConstants.java` and `common/RobotType.java`: + + tools/gen_year_constants.py --year bc20 --engine /path/to/battlecode20 \ + --out src/battlecode/years/bc20/constants.nim + The engine checkout is fetched by CI: curl -fsSL https://github.com/battlecode/battlecode26/archive/refs/tags/engine.1.2.5.tar.gz """ @@ -49,6 +56,14 @@ def nim_literal(java_type: str, raw: str) -> tuple[str, str]: text = raw.strip() if java_type == "String": return "string", text + # The 2020 rule set names one constant by a Java expression rather than a + # literal: MIN_WATER_ELEVATION = Integer.MIN_VALUE/2. Evaluate the handful + # of forms that actually occur rather than hand-typing the value. + JAVA_EXPRESSIONS = { + "Integer.MIN_VALUE/2": "-1073741824", + "Integer.MAX_VALUE/2": "1073741823", + } + text = JAVA_EXPRESSIONS.get(text.replace(" ", ""), text) if re.fullmatch(r"[-+0-9._eE]+[LlFfDd]?", text): text = text.rstrip("LlFfDd") if java_type == "int": @@ -60,8 +75,8 @@ def nim_literal(java_type: str, raw: str) -> tuple[str, str]: return "float64", repr(float(text)) -def read_constants(engine: pathlib.Path) -> list[tuple[str, str, str]]: - src = (engine / "engine/src/main/battlecode/common/GameConstants.java").read_text() +def read_constants_from(path: pathlib.Path) -> list[tuple[str, str, str]]: + src = path.read_text() out = [] for java_type, name, raw in CONST_RE.findall(src): nim_type, literal = nim_literal(java_type, raw) @@ -69,6 +84,11 @@ def read_constants(engine: pathlib.Path) -> list[tuple[str, str, str]]: return out +def read_constants(engine: pathlib.Path) -> list[tuple[str, str, str]]: + return read_constants_from( + engine / "engine/src/main/battlecode/common/GameConstants.java") + + def read_units(engine: pathlib.Path) -> list[tuple[str, list[str]]]: src = (engine / "engine/src/main/battlecode/common/UnitType.java").read_text() return [(name, [v.strip() for v in args.split(",")]) @@ -154,28 +174,143 @@ def render(engine: pathlib.Path) -> str: return "\n".join(lines) + "\n" +# --------------------------------------------------------------------------- +# bc20 — Battlecode 2020 "Soup" +# --------------------------------------------------------------------------- + +BC20_COMMIT = "7618f6be7d12da39f2e6e25801e578f1fecfbd86" + +# `common/RobotType.java`'s enum entries, one per line, with the constructor +# arguments in declaration order: +# spawnSource, cost, dirtLimit, soupLimit, actionCooldown, +# sensorRadiusSquared, pollutionRadiusSquared, localPollutionAdditiveEffect, +# localPollutionMultiplicativeEffect, globalPollutionAmount, +# maxSoupProduced, bytecodeLimit +BC20_ROBOT_RE = re.compile( + r"^\s{4}([A-Z_]+)\s*\(([^)]*)\),\s*$", re.M +) + +# The per-robot DECISION BUDGET that replaces the JVM bytecode limit, at one +# tenth of the Java limit. Hand-authored, not generated: it is this port's own +# rule (docs/RULES-BC20.md §Divergences). +BC20_DECISION_OPS = { + "HQ": 2000, + "MINER": 1000, + "LANDSCAPER": 1000, + "DELIVERY_DRONE": 1000, + "NET_GUN": 700, + "REFINERY": 500, + "VAPORATOR": 500, + "DESIGN_SCHOOL": 500, + "FULFILLMENT_CENTER": 500, + "COW": 0, +} + + +def read_bc20_robots(engine: pathlib.Path) -> list[tuple[str, list[str]]]: + src = (engine / "engine/src/main/battlecode/common/RobotType.java").read_text() + # Cut at the constructor so the field declarations below the enum body are + # not mistaken for entries. + body = src.split("RobotType(RobotType spawnSource", 1)[0] + out = [] + for name, args in BC20_ROBOT_RE.findall(body): + parts = [v.strip() for v in args.split(",")] + if len(parts) != 12: + continue + out.append((name, parts)) + return out + + +def bc20_num(text: str) -> str: + return text.rstrip("LlFfDd") + + +def render_bc20(engine: pathlib.Path) -> str: + consts = read_constants_from( + engine / "engine/src/main/battlecode/common/GameConstants.java") + robots = read_bc20_robots(engine) + if len(robots) != 10: + raise SystemExit(f"::error::expected 10 RobotType entries, saw {len(robots)}") + + lines: list[str] = [] + add = lines.append + add('## Battlecode 2020 "Soup" gameplay constants -- GENERATED, do not edit.') + add("##") + add(f"## Source: github.com/battlecode/battlecode20 at commit `{BC20_COMMIT}`,") + add("## files `common/GameConstants.java` and `common/RobotType.java`, read by") + add("## `tools/gen_year_constants.py --year bc20`. `tests/test_bc20_constants.nim`") + add("## regenerates this file and byte-diffs it, so an edit here fails the build") + add("## instead of quietly changing the rules under a `GameVersion` that no") + add("## longer describes them.") + add("##") + add("## The two derived functions `getWaterLevel`, `getSensorRadiusPollutionCoefficient`") + add("## and `getCooldownPollutionCoefficient` are NOT constants and live in") + add("## `flood.nim` and `pollution.nim`.") + add("") + add(f'const EngineCommit* = "{BC20_COMMIT}"') + add("") + add("type") + add(" RobotKind* = enum") + for name, _ in robots: + add(f' rt{camel(name)} = "{name}"') + add("") + add(" RobotSpec* = object") + add(" ## `common/RobotType.java`'s constructor arguments, verbatim.") + add(" cost*, dirtLimit*, soupLimit*: int") + add(" actionCooldown*: float32") + add(" sensorRadiusSquared*, pollutionRadiusSquared*: int") + add(" localPollutionAdditiveEffect*: int") + add(" localPollutionMultiplicativeEffect*: float32") + add(" globalPollutionAmount*, maxSoupProduced*, bytecodeLimit*: int") + add(" decisionOps*: int") + add("") + add("const") + for name, nim_type, literal in consts: + add(f" {camel(name)}*: {nim_type} = {literal}") + add("") + add(" RobotSpecs*: array[RobotKind, RobotSpec] = [") + for name, a in robots: + add(f" rt{camel(name)}: RobotSpec(cost: {bc20_num(a[1])}, " + f"dirtLimit: {bc20_num(a[2])}, soupLimit: {bc20_num(a[3])},") + add(f" actionCooldown: {bc20_num(a[4])}'f32, " + f"sensorRadiusSquared: {bc20_num(a[5])},") + add(f" pollutionRadiusSquared: {bc20_num(a[6])}, " + f"localPollutionAdditiveEffect: {bc20_num(a[7])},") + add(f" localPollutionMultiplicativeEffect: {bc20_num(a[8])}'f32,") + add(f" globalPollutionAmount: {bc20_num(a[9])}, " + f"maxSoupProduced: {bc20_num(a[10])},") + add(f" bytecodeLimit: {bc20_num(a[11])}, " + f"decisionOps: {BC20_DECISION_OPS[name]}),") + add(" ]") + add("") + return "\n".join(lines) + "\n" + + def main() -> int: ap = argparse.ArgumentParser() + ap.add_argument("--year", default="bc26", choices=["bc26", "bc20"]) ap.add_argument("--engine", required=True, type=pathlib.Path) - ap.add_argument("--out", type=pathlib.Path, - default=pathlib.Path("src/battlecode/years/bc26/constants.nim")) + ap.add_argument("--out", type=pathlib.Path, default=None) ap.add_argument("--check", action="store_true", help="diff against the committed file; exit 1 on drift") args = ap.parse_args() - text = render(args.engine) + out = args.out or pathlib.Path( + f"src/battlecode/years/{args.year}/constants.nim") + label = TAG if args.year == "bc26" else BC20_COMMIT + text = render(args.engine) if args.year == "bc26" else render_bc20(args.engine) if args.check: - current = args.out.read_text() if args.out.exists() else "" + current = out.read_text() if out.exists() else "" if current != text: sys.stderr.write( - f"{args.out} differs from a fresh generation against {TAG}.\n" + f"{out} differs from a fresh generation against {label}.\n" "Re-run tools/gen_year_constants.py and commit the result.\n") return 1 - print(f"{args.out} matches {TAG}") + print(f"{out} matches {label}") return 0 - args.out.parent.mkdir(parents=True, exist_ok=True) - args.out.write_text(text) - print(f"wrote {args.out}") + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(text) + print(f"wrote {out}") return 0 diff --git a/tools/map_pools_bc20.json b/tools/map_pools_bc20.json new file mode 100644 index 0000000..11bfe9b --- /dev/null +++ b/tools/map_pools_bc20.json @@ -0,0 +1,9 @@ +{ + "small": ["maptestsmall", "WateredDown", "Infinity", "Spiral", "Hourglass", + "ALandDivided"], + "mixed": ["maptestsmall", "WateredDown", "Infinity", "Spiral", "Hourglass", + "ALandDivided", "Climb", "Constriction", "CentralLake", + "Toothpaste", "TwoLakeLand", "CentralSoup"], + "large": ["Hills", "OmgThisIsProcedural", "Squares", "BeachFrontProperty", + "Maze", "TheHighGround"] +} From 84e9d170e0cc539c3b6c5e54a6e2b1b6524541e8 Mon Sep 17 00:00:00 2001 From: David Bloomin Date: Fri, 4 Sep 2026 05:29:31 +0000 Subject: [PATCH 2/5] bc20: the manifest variant, GV04, the policy set and the sim test suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the `bc20` manifest variant (num_agents 2 inside its own game_config), the four-year-neutral `results_schema.games.items.required` with each year's statistics as optional siblings, the fourth docs page, the two bc20 baseline player entries and the four bc20 policies. bc26's variant, its player entries and the certification fixture are byte-identical. Bumps GameVersion to GV04 with ReplayCompatibleGameVersions [GV03, GV04]: nothing a GV03 recording carries changed meaning, so every hosted bc26 replay keeps rendering. Adds tests/test_bc20_{flood,dirt,drone,burial,blockchain,pollution,cows, scoring,sheet,maps,baselines,knobs,perf}.nim — including the D1 no-chassis assertion, the D2 survival gate and the ten-knob teeth gate. --- .gitignore | 4 + coworld_manifest_template.json | 280 ++++++++++++++++-- docs/RULES-BC20.md | 194 ++++++++++++ src/battlecode/sim_types.nim | 18 +- src/battlecode/years/bc20/chassis/kit.nim | 15 +- .../years/bc20/chassis/landscaper.nim | 14 +- src/battlecode/years/bc20/chassis/lattice.nim | 2 +- src/battlecode/years/bc20/chassis/miner.nim | 41 ++- tests/test_bc20_baselines.nim | 173 +++++++++++ tests/test_bc20_blockchain.nim | 126 ++++++++ tests/test_bc20_burial.nim | 77 +++++ tests/test_bc20_cows.nim | 110 +++++++ tests/test_bc20_dirt.nim | 126 ++++++++ tests/test_bc20_drone.nim | 121 ++++++++ tests/test_bc20_flood.nim | 113 +++++++ tests/test_bc20_knobs.nim | 184 ++++++++++++ tests/test_bc20_maps.nim | 140 +++++++++ tests/test_bc20_perf.nim | 79 +++++ tests/test_bc20_pollution.nim | 105 +++++++ tests/test_bc20_scoring.nim | 158 ++++++++++ tests/test_bc20_sheet.nim | 160 ++++++++++ tests/test_manifest.nim | 88 +++++- .../convert_maps_bc20.cpython-311.pyc | Bin 18695 -> 0 bytes tools/ci/policies.json | 37 +++ 24 files changed, 2320 insertions(+), 45 deletions(-) create mode 100644 docs/RULES-BC20.md create mode 100644 tests/test_bc20_baselines.nim create mode 100644 tests/test_bc20_blockchain.nim create mode 100644 tests/test_bc20_burial.nim create mode 100644 tests/test_bc20_cows.nim create mode 100644 tests/test_bc20_dirt.nim create mode 100644 tests/test_bc20_drone.nim create mode 100644 tests/test_bc20_flood.nim create mode 100644 tests/test_bc20_knobs.nim create mode 100644 tests/test_bc20_maps.nim create mode 100644 tests/test_bc20_perf.nim create mode 100644 tests/test_bc20_pollution.nim create mode 100644 tests/test_bc20_scoring.nim create mode 100644 tests/test_bc20_sheet.nim delete mode 100644 tools/__pycache__/convert_maps_bc20.cpython-311.pyc diff --git a/.gitignore b/.gitignore index c7260b3..8829b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ nim.cfg # Local scratch /tmp/ *.log + +# Python bytecode from the CI-time tools +__pycache__/ +*.pyc diff --git a/coworld_manifest_template.json b/coworld_manifest_template.json index fea99de..ac7bf26 100644 --- a/coworld_manifest_template.json +++ b/coworld_manifest_template.json @@ -9,7 +9,7 @@ "episode_timeout_minutes": 20, "game": { "name": "battlecode", - "description": "Battlecode 2026 \"Uneasy Alliances\", played by doctrine. Two cogs each command a clan of robot rats on a symmetric grid; neither moves a rat. At t=0 each writes a sealed JSON doctrine and a deterministic Nim port of the official rule set plays a best-of-three match from those two sheets. Both clans start in COOPERATION against neutral NPC cats and the points formula is cat-damage weighted; the moment either clan bites, ratnaps, throws at or traps the other, the world flips to BACKSTAB and the weights swing to king survival. Losing every rat king loses the game outright.", + "description": "Battlecode, played by doctrine. Two cogs each write one sealed JSON strategy sheet and a deterministic Nim port of an official Battlecode rule set plays the whole match from those two sheets. Variant `bc26` is 2026 \"Uneasy Alliances\" — rat clans allied against NPC cats until one of them betrays. Variant `bc20` is 2020 \"Soup\" — the water rises every round, and a team either terraforms its way above the flood, walls its HQ in, or buries the enemy's under fifty units of dirt.", "owner": "daveey@gmail.com", "replay_viewer": { "bundle": "static-replay-viewer" @@ -38,6 +38,14 @@ "value": "https://github.com/Metta-AI/cogame-battlecode/blob/main/docs/RULES.md" } }, + { + "id": "rules-bc20.md", + "title": "Battlecode 2020 \"Soup\": rules, knobs and divergences", + "content": { + "type": "uri", + "value": "https://github.com/Metta-AI/cogame-battlecode/blob/main/docs/RULES-BC20.md" + } + }, { "id": "replay.md", "title": "Replay format", @@ -77,7 +85,8 @@ "year": { "type": "string", "enum": [ - "bc26" + "bc26", + "bc20" ] }, "pool": { @@ -239,18 +248,7 @@ "side", "rounds_played", "winner", - "end_reason", - "cooperation_at_end", - "backstab_round", - "backstab_by", - "cat_damage", - "cheese_transferred", - "kings_alive", - "kings_built", - "rats_built", - "rats_alive", - "traps_placed", - "dirt_placed" + "end_reason" ], "properties": { "map": { @@ -276,7 +274,13 @@ "kings_destroyed", "cats_cleared", "round_limit", - "abandoned" + "abandoned", + "hq_destroyed", + "quantity", + "quality", + "broadcasts", + "highest_id", + "coin_flip" ] }, "cooperation_at_end": { @@ -351,6 +355,175 @@ "items": { "type": "integer" } + }, + "hq_alive": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "boolean" + } + }, + "hq_lost_round": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "hq_lost_cause": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + }, + "soup_mined": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "soup_refined": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "net_worth": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "units_alive": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "units_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "miners_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "landscapers_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "drones_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "vaporators_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "net_guns_built": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "dirt_moved": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "drone_pickups": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "drone_water_drops": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "net_gun_kills": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "transactions_sent": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "transactions_minted": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "blockchain_soup_spent": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + } + }, + "global_pollution_peak": { + "type": "integer" + }, + "flooded_tiles_end": { + "type": "integer" + }, + "water_level_end": { + "type": "number" } } } @@ -465,7 +638,7 @@ "id": "scaffold", "name": "scaffold", "type": "player", - "description": "scaffold baseline \u2014 the ported example bot, weak", + "description": "scaffold baseline — the ported example bot, weak", "image": "{{PLAYER_IMAGE}}", "run": [ "/bin/battlecode-player" @@ -483,12 +656,58 @@ "cpu": "1" } } + }, + { + "id": "bowl-of-chowder", + "name": "bowl-of-chowder", + "type": "player", + "description": "Bowl of Chowder baseline (2020) — passive lattice, ported behaviour", + "image": "{{PLAYER_IMAGE}}", + "run": [ + "/bin/battlecode-player" + ], + "env": { + "PLAYER_SCRIPTED": "bowl-of-chowder", + "PLAYER_POLICY_LABEL": "bowl-of-chowder" + }, + "source_url": "https://github.com/Metta-AI/cogame-battlecode/tree/main", + "resources": { + "requests": { + "memory": "512Mi" + }, + "limits": { + "cpu": "1" + } + } + }, + { + "id": "examplefuncsplayer", + "name": "examplefuncsplayer", + "type": "player", + "description": "scaffold baseline (2020) — the ported example bot, weak", + "image": "{{PLAYER_IMAGE}}", + "run": [ + "/bin/battlecode-player" + ], + "env": { + "PLAYER_SCRIPTED": "examplefuncsplayer", + "PLAYER_POLICY_LABEL": "examplefuncsplayer" + }, + "source_url": "https://github.com/Metta-AI/cogame-battlecode/tree/main", + "resources": { + "requests": { + "memory": "512Mi" + }, + "limits": { + "cpu": "1" + } + } } ], "variants": [ { "id": "bc26", - "name": "Battlecode 2026 \u2014 Uneasy Alliances (2 seats)", + "name": "Battlecode 2026 — Uneasy Alliances (2 seats)", "description": "Best of three on the mixed pool. Both clans start allied against the cats; either can betray at any moment, and the points formula swings when they do.", "game_config": { "year": "bc26", @@ -512,6 +731,33 @@ } ] } + }, + { + "id": "bc20", + "name": "Battlecode 2020 — Soup (2 seats)", + "description": "Best of three on the mixed pool. The water rises every round and every HQ starts below it: a team either walls its HQ in, terraforms above the flood, or buries the enemy's HQ under fifty units of dirt before the sea gets to its own.", + "game_config": { + "year": "bc20", + "pool": "mixed", + "gamesPerMatch": 3, + "seed": 0, + "maxRounds": 1500, + "num_agents": 2, + "attempt1Ms": 20000, + "retryMs": 12000, + "doctrineBudgetMs": 45000, + "perGameBudgetSeconds": 100, + "matchBudgetSeconds": 320, + "connectTimeoutMs": 25000, + "players": [ + { + "name": "Clan Ash" + }, + { + "name": "Clan Basil" + } + ] + } } ], "certification": { diff --git a/docs/RULES-BC20.md b/docs/RULES-BC20.md new file mode 100644 index 0000000..51728ba --- /dev/null +++ b/docs/RULES-BC20.md @@ -0,0 +1,194 @@ +# Battlecode 2020 "Soup": rules, knobs and deliberate divergences + +The `bc20` variant is a **behaviour port** of MIT Battlecode 2020 "Soup" to the +same deterministic Nim sim `bc26` runs in, compiled twice from one source tree: +natively into `/bin/battlecode` and to wasm into the static replay bundle. The +2020 Java engine exists **only** in the `parity-oracle` CI job. No JDK, no JRE +and no upstream Java source exists in any image this repository builds. + +Sources, all read at commit `7618f6be7d12da39f2e6e25801e578f1fecfbd86` (the last +2020 state of `github.com/battlecode/battlecode20`): +`specs/specs.md`, `common/GameConstants.java`, `common/RobotType.java`, +`common/Transaction.java`, `common/Direction.java`, `common/MapLocation.java`, +`world/GameWorld.java`, `world/InternalRobot.java`, `world/ObjectInfo.java`, +`world/TeamInfo.java`, `world/RobotControllerImpl.java`, `world/IDGenerator.java`, +`world/control/CowControlProvider.java`, `schema/battlecode.fbs`. + +## The game in one paragraph + +Two teams on a symmetric 32×32–48×48 grid. **The water rises every round** and +floods outward one ring per round from every already-flooded tile whose +neighbour sits below the level; anything that is not a delivery drone dies on a +flooding tile. Miners mine SOUP and refine it at the HQ or a refinery. Design +schools build landscapers that dig and dump dirt. Fulfillment centers build +delivery drones that pick up any unit — including enemy landscapers — and drop +them in the water. Vaporators print soup and scrub pollution; net guns shoot +drones. The only global channel is a blockchain: seven ints a message, seven +messages a round, paid for in soup, and **both teams read every block**. + +**An HQ cannot be raised.** Dirt dropped on a building goes onto the *building* +(and buries it at 50 for an HQ, 15 for everything else), never onto its tile. The +only thing that keeps an HQ dry is a ring of eight adjacent tiles the water can +never cross. That is what `wall_hq_round` buys, and it is why never walling +drowns you on a schedule. + +## The round loop — the numbered resolution rules + +`rules.nim`'s `runRound` mirrors `GameWorld.runRound` / +`processBeginningOfRound` / `updateDynamicBodies` / `processEndOfRound` step for +step. **Re-ordering any of them is a rules change and bumps `GameVersion`.** + +1. `currentRound += 1`; both teams' pools gain `BASE_INCOME_PER_ROUND = 1`. +2. Every non-blocked robot runs `processBeginningOfRound` — a no-op in 2020, + kept as a named step because the hash chain and the parity trace are taken + around it. +3. Iterate the dynamic bodies in **spawn order** (`ObjectInfo.eachDynamicBodyByExecOrder` + — append on spawn, remove on death), **not** id order. Map bodies are + appended in file order. +4. **A blocked robot takes no turn.** A unit held by a delivery drone does not + run its beginning-of-turn, does not act, and its cooldown does **not** decay. + It *does* still get `resetPollutionForRobot` if its type can pollute. +5. Beginning of turn: `cooldownTurns = max(0, cooldownTurns − 1)` and the + robot's `DecisionOps` budget is reset. +6. Run the controller. A robot may act only while `cooldownTurns < 1`; every + action adds `type.actionCooldown × cooldownCoefficient(pollution here)`. + The legal actions and their exact preconditions are in `world.nim`, one proc + per action, each named for its `RobotControllerImpl` original. +7. End of turn, in `InternalRobot.processEndOfTurn`'s own order: clear this + robot's previous local pollution; refine (HQ/Refinery, ≤ 20 into the pool); + vaporate (+2 unconditionally); cows always pollute; then install a fresh + local effect. **A refinery's local +500 therefore lasts exactly one round.** +8. End of round: mint the block (≤ 7 transactions); raise the water; flood + exactly one ring; check the end-of-match ladder. +9. Append this round's state hash. + +## The end ladder, in the engine's own order + +| `end_reason` | engine origin | meaning | +|---|---|---| +| `hq_destroyed` | `HQ_DESTROYED` | exactly one HQ was buried under 50 dirt or drowned | +| `quantity` | `QUANTITY_OVER_QUALITY` | round cap or a double HQ loss; more living robots wins | +| `quality` | `QUALITY_OVER_QUANTITY` | equal robots; greater net worth wins | +| `broadcasts` | `GOSSIP_GIRL` | equal worth; more **minted** transactions wins | +| `highest_id` | `HIGHBORN` | equal broadcasts; the highest living robot id wins | +| `coin_flip` | `WON_BY_DUBIOUS_REASONS` | neither team has a living robot | +| `abandoned` | — | our `perGameBudgetSeconds` / `matchBudgetSeconds` guard fired | + +`roundLimitReached` is the engine's own `currentRound >= rounds − 1`, so a +1500-round cap plays **1499**. The off-by-one is the engine's and is reproduced. + +## Scoring + +``` +hq[t] = 1 if team t's HQ is alive at the final round else 0 +survival[t] = f32(hq[t]) / f32(max(1, hq[A] + hq[B])) +units[t] = living robots of t, BUILDINGS INCLUDED (the QUANTITY rung) +worth[t] = team soup pool + sum of type.cost over living non-neutral robots + (the QUALITY rung) +points[t] = int(60*survival[t] + 25*unit_share[t] + 15*net_worth_share[t]) +results.scores[t] = 100 * (games t won) + mean(points[t] over games played) +``` + +Every share is narrowed through **float32** and the sum is **truncated**, for +recorder/re-deriver agreement: the same arithmetic runs natively on x86-64 and +in wasm32 and must produce the same integer. + +## The doctrine sheet — ten knobs, and **no `chassis` key** + +The chassis is **not** an LLM-selectable knob. It comes from `PLAYER_SCRIPTED` +for a scripted seat and is the fixed champion chassis for an LLM seat. A +submitted `chassis` is recorded as an **unknown field** and never honoured; +`tests/test_bc20_sheet.nim` asserts exactly that. + +| field | type / values | default | site | +|---|---|---|---| +| `opening` | `rush` \| `lattice` \| `passive_lattice` \| `turtle` | `passive_lattice` | `chassis/boc.nim` `applyOpening` | +| `terraform_start_round` | int 1…1500 | 300 | `chassis/landscaper.nim` `chooseMode` | +| `lattice_radius` | int 2…12 | 6 | `chassis/lattice.nim` `latticeNeedsWork` | +| `landscaper_count_curve` | `lean` \| `steady` \| `swarm` | `steady` | `knobs.nim` `landscaperTarget` | +| `miner_count_curve` | `lean` \| `steady` \| `swarm` | `steady` | `knobs.nim` `minerTarget` | +| `vaporator_budget` | int 0…6 | 2 | `chassis/miner.nim` `nextBuilding` | +| `drone_role` | `harass` \| `wall` \| `buster` \| `carry_landscapers` | `harass` | `chassis/drone.nim` `runDrone` | +| `net_gun_ring` | int 0…6 | 2 | `chassis/miner.nim` `nextBuilding` | +| `rush_trigger` | int 0…1500 (**0 = never**) | 0 | `chassis/landscaper.nim` `chooseMode` | +| `wall_hq_round` | int 0…1500 (**0 = never**) | 250 | `chassis/kit.nim` `effectiveWallRound` | + +`landscaper_count_curve` targets `4 + round/220` scaled 0.6 / 1.0 / 1.7, capped +40; `miner_count_curve` targets `6 + round/300` scaled the same way, capped 25. + +## §Divergences + +Every one of these is deliberate and is the reason the parity oracle compares +*outcomes and state* rather than bytecodes. + +1. **No bytecode instrumentation.** `RobotType.bytecodeLimit` has no meaning + outside the JVM instrumenter. It is replaced by a fixed per-robot + `DecisionOps` budget at **one tenth** of the Java limit (HQ 2000; miner, + landscaper, drone 1000; net gun 700; other buildings 500; cow 0), charged + one credit per tile sensed, robot examined, BFS node expanded, direction + evaluated and block read, **enforced by the sim, not by the bot**. When the + budget reaches zero the robot's turn ends where it stands: there is **no + mid-turn resumption**, which is the one place this differs from the JVM. +2. **`setWinnerArbitrary`'s `Math.random()`** — wall-clock seeded, therefore not + reproducible — is replaced by a draw from the world RNG. Reachable only when + neither team has a single living robot. +3. **`Math.exp`/`Math.sin` in the water level** are HotSpot intrinsics that are + not bit-identical to any libm this port would link. `getWaterLevel` depends + only on the round number, so `tools/JavaWaterLevels.java` emits every value + as a float32 **bit pattern** into `data/bc20/water_levels.json` under the CI + JDK; the file is committed and the `parity-oracle` job regenerates and + byte-diffs it as a BLOCKING step. The generator also cross-checks `Math` + against `StrictMath` on the whole domain. +4. **A 1500-round cap** in place of the map's 10 000, applied through the + engine's own `currentRound >= rounds − 1`. Elevation 6 floods at round 1413 + and 7 at 1546, so by 1500 every HQ has either been terraformed above the + water or has drowned: the game is *decided* by the cap, not merely stopped. +5. **No indicator dots or lines, no profiler, no crossplay, no `.bc20` output.** +6. **The `deadline` wall-clock stop** is a coworld concept, not an engine one. + It is recorded as ONE load-bearing record (`plan.abandon_after[g]`) and + applied by the same proc on record and on playback. +7. **Both chassis are behaviour ports** parameterised by the doctrine sheet. + `bowl-of-chowder` is Bowl of Chowder's *ideas* rewritten in Nim; + `examplefuncsplayer` is ported statement for statement and may not gain + behaviour, because it is one side of the differential oracle. +8. **18 of the 52 official maps** are converted. `CowFarm` and + `DidAMonkeyMakeThis` are excluded on purpose: both carry tiles at + `Integer.MAX_VALUE/2` elevation, which is legal but makes the elevation + shading meaningless and the timing untypical. +9. **The transaction pool is drained by a stable sort**, not by a binary heap. + `Transaction.compareTo` is a total order except for two transactions that + agree on cost, on the 32-bit random id **and** on all seven ints; the sorted + extraction is deterministic where `PriorityQueue.poll` is unspecified. +10. **`maptestsmall` carries soup on all 1024 tiles.** It is kept anyway, + because it is the map the parity oracle and the fast smoke run on; the + anomaly is noted here rather than hidden. +11. **`Infinity` has no tile at `MIN_WATER_ELEVATION`** (its floor is −12) and + its HQs start at elevation 0, so `tests/test_bc20_maps.nim` asserts the + `MIN_WATER_ELEVATION` guarantee for the other seventeen and records + `Infinity`'s real floor. The 2020 spec asserts the guarantee; this map does + not honour it, and the port follows the file rather than the prose. +12. **The chassis walls before `wall_hq_round` when the map demands it.** The + knob says *when* to start; the map says when it is too late. On + `maptestsmall` the HQ ring sits at elevation 1 and floods at round 256, so + a doctrine that says "wall at 300" has already lost. `effectiveWallRound` + takes `min(wall_hq_round, ringFloodRound − 200)`. `wall_hq_round = 0` still + means **never**, which is what gives the knob its teeth. +13. **The HQ wall bar is evaluated at the last round the game can reach** + (`waterLevel(max(round + 400, maxRounds)) + 2`) rather than at + `round + 400`. A moving bar declared the wall closed at round 76 on a map + whose ring already sat at elevation 4 and then let the same ring drown at + round 932. +14. **The map symmetry is recomputed on every NEUTRAL spawn**, not on every + spawn. `TeamControlProvider` delegates `robotSpawned` **by team**, and only + the cow provider recomputes; cows exist only at map load, so the value is + fixed for the match. The port reproduces the delegation, not the prose. + +## Where the archetypes come from + +The four openings — rush, lattice, passive lattice, turtle — are the year's +published metagame, taken from Stone Tao's postmortem +() and from the spec, not from +any bot's source. `IvanGeffner/battlecode2020` declares **no licence** and is +cited here only as the public record of what the winning archetypes were: it is +not vendored, not ported, not compiled and not read into any file in this +repository. diff --git a/src/battlecode/sim_types.nim b/src/battlecode/sim_types.nim index 42b33d8..c5b28ad 100644 --- a/src/battlecode/sim_types.nim +++ b/src/battlecode/sim_types.nim @@ -13,13 +13,27 @@ import std/[strutils, unicode] const GameName* = "battlecode" - GameVersion* = "GV04" + GameVersion* = "GV05" ## PREPEND-ONLY CHANGELOG. Anything that changes what a policy sees, how a ## seat is scored, or how a round resolves bumps this in the SAME commit, ## and `tools/ci/check_gameversion.sh` compares the headline (not the ## digits) against the base branch — a number alone cannot detect two ## branches claiming the same version for different rules. ## + ## GV05 — the `bc20` year module: Battlecode 2020 "Soup" ported from + ## battlecode20 at commit 7618f6b (round loop, flood, soup and + ## refining, the seven build types, dig/dump, drone carry and + ## drop-in-water, net guns, pollution, the 64-int blockchain and + ## its cost model, the six-rung tiebreak ladder), behind + ## `game_config.year`. bc26 SEMANTICS ARE UNCHANGED: nothing a GV04 + ## recording carries changed meaning — the sheet envelope, the + ## results document and the replay all gained year-neutral shape + ## without moving a byte a bc26 recording holds — which is why + ## `ReplayCompatibleGameVersions` keeps GV04 and every hosted bc26 + ## replay from that version keeps rendering. GV03 stays out: the D2 + ## chassis change in GV04 means a GV03 recording is not re-derivable + ## under either. + ## ## GV04 — `chassis` is no longer a doctrine knob. It is gone from ## `sheet.KnownKeys` and from the prompt preamble's knob list, so ## an LLM doctrine ALWAYS runs the `awu` chassis and a reply that @@ -54,7 +68,7 @@ const ## round loop, cheese, kings, combat, ratnap/throw, traps, dirt, ## formation, squeaks, cats, backstab, float32-narrowed scoring. - ReplayCompatibleGameVersions* = [GameVersion] + ReplayCompatibleGameVersions* = ["GV04", GameVersion] ## Versions whose recordings this build can still re-derive. A replay ## carrying anything else is refused with a readable message rather than ## silently re-simulated under different rules. diff --git a/src/battlecode/years/bc20/chassis/kit.nim b/src/battlecode/years/bc20/chassis/kit.nim index 1f3ca2f..e704d02 100644 --- a/src/battlecode/years/bc20/chassis/kit.nim +++ b/src/battlecode/years/bc20/chassis/kit.nim @@ -143,10 +143,19 @@ proc effectiveWallRound*(w: World, side: Side): int = max(1, roundWaterReaches(minElev) - 200)) side.wallRoundCache -proc latticeTarget*(side: Side, round: int): int = +proc latticeTarget*(w: World, side: Side): int = ## `lattice.nim`: every non-wall tile inside `lattice_radius` is raised to - ## `waterLevel(round + 250) + 1`. - int(waterLevelAt(min(WaterTableMaxRound, round + 250))) + 1 + ## `waterLevel(round + 250) + 1` — OR to three below the HQ wall, whichever + ## is higher. + ## + ## The second term is not decoration. `MAX_DIRT_DIFFERENCE` is 3, so a wall + ## ring at elevation 8 with bare ground at 3 around it is a SHEER CLIFF: the + ## HQ survives and every miner is locked out of it, the team's only refinery + ## is unreachable, and mining stops dead around round 200. Raising the + ## lattice to `wallTarget - 3` builds the ramp that makes the wall a wall and + ## not a tomb. + max(int(waterLevelAt(min(WaterTableMaxRound, w.currentRound + 250))) + 1, + w.wallTarget(side) - 3) proc willFloodNextRound*(w: World, l: Loc): bool = ## A tile a ground unit must not be standing on when the flood arrives. diff --git a/src/battlecode/years/bc20/chassis/landscaper.nim b/src/battlecode/years/bc20/chassis/landscaper.nim index 541a90f..daf813e 100644 --- a/src/battlecode/years/bc20/chassis/landscaper.nim +++ b/src/battlecode/years/bc20/chassis/landscaper.nim @@ -171,9 +171,15 @@ proc chooseMode(w: World, side: Side, r: Robot, brain: Brain): int = return ModeWall if w.currentRound >= d.terraformStartRound: return ModeTerraform - if d.wallHqRound > 0 and not w.wallComplete(side): - return ModeWall - ModeTerraform + ## BEFORE `terraform_start_round` a landscaper WALLS and nothing else. The + ## knob has to have teeth: falling through to terraform here made + ## `terraform_start_round` inert, because the lattice went up from round one + ## whatever the sheet said. + ## + ## `wall_hq_round = 0` means NEVER, so a doctrine that refuses the wall + ## terraforms instead — and its HQ ring stays at map elevation, which is + ## exactly why never walling drowns. + if d.wallHqRound > 0: ModeWall else: ModeTerraform proc runLandscaper*(w: World, side: Side, r: Robot) = let brain = side.brainFor(r) @@ -207,7 +213,7 @@ proc runLandscaper*(w: World, side: Side, r: Robot) = ## bar. Without the cap a landscaper that had finished its wall tile and ## could no longer walk anywhere (it is eight steps above its neighbours) ## dug and dropped for the rest of the match and built a 235-high spike. - let cap = max(w.wallTarget(side), side.latticeTarget(w.currentRound)) + let cap = max(w.wallTarget(side), w.latticeTarget(side)) if r.dirtCarrying > 0 and w.getDirt(r.loc) < cap: discard w.depositToward(r, r.loc) elif r.dirtCarrying < RobotSpecs[rtLandscaper].dirtLimit and diff --git a/src/battlecode/years/bc20/chassis/lattice.nim b/src/battlecode/years/bc20/chassis/lattice.nim index 5700e08..81991de 100644 --- a/src/battlecode/years/bc20/chassis/lattice.nim +++ b/src/battlecode/years/bc20/chassis/lattice.nim @@ -24,7 +24,7 @@ proc latticeNeedsWork*(w: World, side: Side, l: Loc): bool = if side.isWallRing(l): return false let occupant = w.getRobot(l) if occupant != nil and occupant.kind.isBuilding(): return false - w.getDirt(l) < side.latticeTarget(w.currentRound) + w.getDirt(l) < w.latticeTarget(side) proc lowestLatticeTileNear*(w: World, side: Side, r: Robot): (bool, Loc) = ## The lowest tile within the robot's own sense window that the lattice still diff --git a/src/battlecode/years/bc20/chassis/miner.nim b/src/battlecode/years/bc20/chassis/miner.nim index 52f4259..05b0ae6 100644 --- a/src/battlecode/years/bc20/chassis/miner.nim +++ b/src/battlecode/years/bc20/chassis/miner.nim @@ -33,13 +33,14 @@ proc electBuilder(w: World, side: Side) = proc nextBuilding(w: World, side: Side): (bool, RobotKind) = let d = side.doctrine if w.alive(side, rtDesignSchool) < 1: return (true, rtDesignSchool) + ## One Refinery, EARLY. An HQ that has been walled in is eight elevation + ## steps above the ground outside it, and `MAX_DIRT_DIFFERENCE` is 3: a miner + ## that can no longer reach the HQ has nowhere to put its soup. A refinery + ## outside the wall is the team's second drop-off, and it also refines its + ## own 20 a round. + if w.alive(side, rtRefinery) < 1: return (true, rtRefinery) if w.alive(side, rtNetGun) < d.netGunRing: return (true, rtNetGun) if w.alive(side, rtFulfillmentCenter) < 1: return (true, rtFulfillmentCenter) - ## One Refinery once the near seam is worked out: a miner that has to walk - ## the whole way back to the HQ spends most of the match in transit, and a - ## starved pool is what stops the Design School making landscapers. - if w.currentRound >= 250 and w.alive(side, rtRefinery) < 1: - return (true, rtRefinery) if w.alive(side, rtVaporator) < d.vaporatorBudget: return (true, rtVaporator) if w.currentRound >= 600 and w.alive(side, rtDesignSchool) < 2: return (true, rtDesignSchool) @@ -54,7 +55,10 @@ proc buildSiteScore(w: World, side: Side, kind: RobotKind, l: Loc): int = ## High ground first: a building cannot be raised (dirt dropped on a ## building buries it), so the elevation it is founded on is the elevation it ## drowns at. - var score = 100 - abs(ring - 2) * 20 + w.getDirt(l) * 12 + ## A REFINERY sits further out, where the miners are and where the HQ wall + ## cannot lock it away; everything else hugs the base at Chebyshev 2. + let want = if kind == rtRefinery: 4 else: 2 + var score = 100 - abs(ring - want) * 20 + w.getDirt(l) * 12 if w.willFloodNextRound(l): score -= 500 if kind == rtVaporator and not l.isLatticeTile(): score -= 10 score @@ -103,6 +107,29 @@ proc nearestRefinery(w: World, r: Robot, side: Side): (bool, Loc) = found = true (found, best) +proc noteSoupTips(w: World, side: Side, r: Robot, brain: Brain) = + ## A seam worth telling the team about. The blockchain is the ONLY channel + ## the rules give a team, so a tip costs soup and is rate-limited; a miner + ## that has mined out its own side otherwise wanders a 1024-tile map with a + ## sensor window the cows have shrunk to three tiles, and the whole team's + ## income stops around round 200. + if brain.turnCount mod 40 != 0: return + for l in w.sensed(r): + if w.getSoup(l) < SoupTipThreshold: continue + var known = false + for existing in side.soupTips: + if existing == l: known = true + if known: continue + if side.soupTips.len < 8: + side.soupTips.add(l) + w.broadcast(side, r, SigAnnounceSoup, l.x, l.y) + return + +proc pruneSoupTips(w: World, side: Side) = + for i in countdown(side.soupTips.high, 0): + if w.getSoup(side.soupTips[i]) <= 0: + side.soupTips.delete(i) + proc nearestSoup(w: World, r: Robot, side: Side): (bool, Loc) = var best = loc(0, 0) var bestD = high(int) @@ -143,6 +170,8 @@ proc runMiner*(w: World, side: Side, r: Robot) = if w.fleeWater(side, r): return if r.dead: return + w.pruneSoupTips(side) + w.noteSoupTips(side, r, brain) w.electBuilder(side) if r.id == side.builderId: let (wanted, kind) = w.nextBuilding(side) diff --git a/tests/test_bc20_baselines.nim b/tests/test_bc20_baselines.nim new file mode 100644 index 0000000..4b7005c --- /dev/null +++ b/tests/test_bc20_baselines.nim @@ -0,0 +1,173 @@ +## The bc20 baselines: bounded, legal orders, and the D2 SURVIVAL GATE. +## +## D2 (sibling review finding, 2026-09-03): the scripted baseline has to PLAY. +## `bowl-of-chowder` walls its HQ, guns the air and terraforms, and this gate +## reads survival and positive play counters, not a win alone. A baseline that +## idled to a win fails (a) through (c). + +import std/[sequtils, strutils] +import harness +import battlecode/[baselines, sheet] +import battlecode/years/bc20/[constants, maps, rules, world] +import battlecode/years/bc20/chassis/kit + +const GateMaps = ["WateredDown", "ALandDivided"] + ## Two `small`-pool maps. The episode seed only chooses maps and side + ## assignment — the world RNG comes from the MAP's own `randomSeed` — so the + ## six games are the two maps under three seeds' worth of side assignment, + ## which is every distinct game those inputs can produce. +const GateSeeds = [1, 2, 3] + +proc bocSheet(): Sheet = baselineSheet("bc20", blBowlOfChowder) +proc scaffoldSheet(): Sheet = baselineSheet("bc20", blExamplefuncsplayer) + +# --- (a) both replies pass the SAME validate the LLM path uses -------------- +block: + for kind in [blBowlOfChowder, blExamplefuncsplayer]: + let s = baselineSheet("bc20", kind) + checkEq($kind & " applies no defaults", s.defaultsApplied.len, 0) + checkEq($kind & " has no unknown fields", s.unknownFields.len, 0) + check($kind & " names no chassis", "chassis" notin s.unknownFields) + check($kind & "'s notes are under cap", s.notes.len > 0) + check($kind & "'s motto is under cap", s.motto.len > 0) + ## Every knob is in range by construction — `validate` would have recorded + ## a repair otherwise, which the first check above forbids. + let d = s.doctrine20 + check($kind & " terraform_start_round in range", + d.terraformStartRound >= 1 and d.terraformStartRound <= 1500) + check($kind & " lattice_radius in range", + d.latticeRadius >= 2 and d.latticeRadius <= 12) + check($kind & " vaporator_budget in range", + d.vaporatorBudget >= 0 and d.vaporatorBudget <= 6) + check($kind & " net_gun_ring in range", + d.netGunRing >= 0 and d.netGunRing <= 6) + checkEq("an unrecognised PLAYER_SCRIPTED takes the STRONG baseline", + baselineFor("bc20", "nonsense"), blBowlOfChowder) + checkEq("and `examplefuncsplayer` takes the weak floor", + baselineFor("bc20", "examplefuncsplayer"), blExamplefuncsplayer) + checkEq("the chassis follows the name, not the sheet", + baselineChassis(blExamplefuncsplayer), parseChassis("examplefuncsplayer")) + +# --- (b) every emitted action is legal, and the budget is never exceeded ---- +proc assertInvariants(w: World, label: string) = + ## Every state a legal action can leave the world in. An illegal `move` + ## would put a ground unit in the water or two robots on one tile; an + ## illegal `digDirt` would push a landscaper past its carry limit; an + ## over-budget turn would leave `opsLeft` negative. + var occupied: seq[int] + for id, r in w.robotsById: + check(label & ": " & $id & " is on the map", w.onTheMap(r.loc)) + check(label & ": " & $id & " never went below zero cooldown", + r.cooldownTurns >= 0.0'f32) + check(label & ": " & $id & " respected its DecisionOps budget", + r.opsLeft >= 0 and r.opsLeft <= RobotSpecs[r.kind].decisionOps) + check(label & ": " & $id & " is inside its carry limits", + r.soupCarrying <= RobotSpecs[r.kind].soupLimit and + (r.dirtCarrying <= RobotSpecs[r.kind].dirtLimit or + r.kind.isBuilding())) + if not r.blocked: + check(label & ": " & $id & " is the occupant of its own tile", + w.getRobot(r.loc) == r) + let i = w.idx(r.loc) + check(label & ": no two robots share a tile", i notin occupied) + occupied.add(i) + if not r.kind.canFly(): + check(label & ": " & $id & " is not standing in water", + not w.isFlooded(r.loc)) + check(label & ": " & $id & " has a non-negative soup carry", + r.soupCarrying >= 0 and r.dirtCarrying >= 0) + for t in 0 .. 1: + check(label & ": team " & $t & "'s pool never went negative", + w.stats.soup[t] >= 0) + for tx in w.txPool: + check(label & ": every pooled transaction cost is positive", tx.cost > 0) + checkEq(label & ": and carries exactly seven ints", tx.message.len, 7) + +block: + ## A real match, invariants checked every fifty rounds. + let spec = loadMap("WateredDown") + var w = newWorld(spec, 1500) + var sides = newSides([bocSheet(), scaffoldSheet()], + [parseChassis("bowl-of-chowder"), parseChassis("examplefuncsplayer")], 0) + let chassis = [parseChassis("bowl-of-chowder"), + parseChassis("examplefuncsplayer")] + for round in 1 .. 466: + runRound(w, sides, chassis) + if round mod 50 == 0: + w.assertInvariants("round " & $round) + if not w.running: break + +# --- (c) THE SURVIVAL GATE -------------------------------------------------- +var games = 0 +var wins = 0 +for seed in GateSeeds: + for mapName in GateMaps: + let sideAslot = ((seed shr 8) and 1) xor (games and 1) + let spec = loadMap(mapName) + let sheets = [bocSheet(), scaffoldSheet()] + let chassis = [parseChassis("bowl-of-chowder"), + parseChassis("examplefuncsplayer")] + let (w, o) = playGame(spec, sheets, chassis, 0, sideAslot, 1500, 0) + let tag = mapName & "/seed" & $seed + ## `GameOutcome20`'s arrays are re-indexed BY SEAT in `harvest`; the raw + ## `TeamStats` and the event stream are BY TEAM, and which team seat 0 + ## drives alternates with `sideAslot`. + let bocTeam = if sideAslot == 0: 0 else: 1 + inc games + if o.winnerSlot == 0: inc wins + + ## Bowl of Chowder: it survives, and it survives BY PLAYING. + check(tag & ": bowl-of-chowder's HQ is alive at the end", o.hqAlive[0]) + checkEq(tag & ": and it won", o.winnerSlot, 0) + check(tag & ": it built at least six miners", o.minersBuilt[0] >= 6) + check(tag & ": at least three landscapers", o.landscapersBuilt[0] >= 3) + check(tag & ": at least one design school", + w.stats.designSchoolsBuilt[bocTeam] >= 1) + check(tag & ": at least one refinery or fulfillment center", + w.stats.refineriesBuilt[bocTeam] + + w.stats.fulfillmentBuilt[bocTeam] >= 1) + check(tag & ": at least two net guns", o.netGunsBuilt[0] >= 2) + check(tag & ": it moved at least ninety dirt", o.dirtMoved[0] >= 90) + var wallClosed = false + for e in w.events: + if e.kind == "wall_closed" and e.a == bocTeam: wallClosed = true + check(tag & ": and it emitted wall_closed", wallClosed) + + ## The scaffold ACTS — it is the deliberate weak floor and the oracle's + ## other side, and it may not gain behaviour. + check(tag & ": the scaffold built at least one miner", + o.minersBuilt[1] >= 1) + check(tag & ": and mined at least once", o.soupMined[1] >= 1) + ## AND IT NEVER BROADCASTS. `examplefuncsplayer.tryBlockchain` builds + ## `new int[10]`, and `assertCanSubmitTransaction` refuses anything whose + ## length is not `BLOCKCHAIN_TRANSACTION_LENGTH = 7`. The design note asks + ## for "a 7x123 transaction"; the upstream file sends ten ints and the + ## engine throws them away, so this port reproduces the refusal rather + ## than correcting the bot. Fixing it here would break the parity oracle, + ## whose Java side is that same file. + checkEq(tag & ": and never mints a transaction, exactly as upstream", + o.transactionsMinted[1], 0) + +checkEq("six games were played", games, 6) +checkEq("and bowl-of-chowder won every one", wins, 6) + +# --- the long game: both HQs alive at round 1499 ---------------------------- +block: + ## The gate above ends when the scaffold drowns. A full-length game needs + ## two survivors, so it is played bowl-of-chowder against itself: both HQs + ## must still stand at the cap. + let spec = loadMap("ALandDivided") + let sheets = [bocSheet(), bocSheet()] + let chassis = [parseChassis("bowl-of-chowder"), + parseChassis("bowl-of-chowder")] + let (w, o) = playGame(spec, sheets, chassis, 0, 0, 1500, 0) + checkEq("a bowl-of-chowder mirror runs to the cap", o.roundsPlayed, 1499) + check("and both HQs are alive at round 1499", o.hqAlive[0] and o.hqAlive[1]) + check("both sides walled", (block: + var closed: seq[int] + for e in w.events: + if e.kind == "wall_closed": closed.add(e.a) + 0 in closed and 1 in closed)) + w.assertInvariants("the mirror at the cap") + +finish("test_bc20_baselines") diff --git a/tests/test_bc20_blockchain.nim b/tests/test_bc20_blockchain.nim new file mode 100644 index 0000000..1fa376c --- /dev/null +++ b/tests/test_bc20_blockchain.nim @@ -0,0 +1,126 @@ +## The blockchain: the fee model, the seven-int message, the comparator, the +## per-round mint of at most seven, the eternal pool, and the re-seeded +## transaction-id RNG. + +import harness +import battlecode/rng +import battlecode/years/bc20/[blockchain, constants, world] + +proc flat(width, height: int): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(0) + result.water.add(false) + result.pollution.add(0) + result.soup.add(0) + +proc msg(v: int): array[TransactionLength, int] = + for i in 0 ..< TransactionLength: result[i] = v + +block: + checkEq("a message is exactly seven ints", BlockchainTransactionLength, 7) + checkEq("and the Nim record agrees", TransactionLength, 7) + checkEq("seven transactions a block", NumberOfTransactionsPerBlock, 7) + +block: + ## `cost > 0`, `cost <= team soup`, and the soup is deducted AT SUBMIT TIME. + var w = newWorld(flat(9, 9), 1500) + let id = w.spawnRobot(rtMiner, loc(4, 4), teamA) + let r = w.robotsById[id] + w.stats.soup[0] = 100 + check("a zero-cost transaction is refused", not w.canSubmitTransaction(r, 0)) + check("a negative cost is refused", not w.canSubmitTransaction(r, -5)) + check("a cost above the pool is refused", + not w.canSubmitTransaction(r, 101)) + check("and one the pool can pay is allowed", w.canSubmitTransaction(r, 40)) + w.submitTransaction(r, msg(1), 40) + checkEq("the soup came out at submit time", w.stats.soup[0], 60) + checkEq("and the pool holds one transaction", w.txPool.len, 1) + checkEq("submitted is counted", w.stats.transactionsSent[0], 1) + checkEq("minted is NOT yet", w.stats.blockchainsSent[0], 0) + +block: + ## Comparator order: cost DESC, then id DESC, then the serialized message + ## ASCENDING. `serializedMessage` is the seven ints joined by `_`. + var pool: seq[Transaction] + pool.add(newTransaction(5, msg(9), 100'i32, 0)) + pool.add(newTransaction(9, msg(1), 1'i32, 0)) + pool.add(newTransaction(5, msg(8), 200'i32, 1)) + let minted = mintBlock(pool, 7) + checkEq("every transaction minted", minted.len, 3) + checkEq("the highest cost is first", minted[0].cost, 9) + checkEq("then the higher id among equal costs", minted[1].id, 200'i32) + checkEq("and the lower id last", minted[2].id, 100'i32) + checkEq("the pool is drained", pool.len, 0) + + var tie: seq[Transaction] + var lo = msg(1) + var hi = msg(1) + hi[6] = 2 + tie.add(newTransaction(3, hi, 7'i32, 0)) + tie.add(newTransaction(3, lo, 7'i32, 0)) + let tied = mintBlock(tie, 7) + checkEq("a full id tie breaks on the lexicographically earlier message", + tied[0].serialized, "1_1_1_1_1_1_1") + +block: + ## At most seven a round; the rest STAY ELIGIBLE FOR EVER, and + ## `blockchainsSent` counts MINTED, not submitted. + var w = newWorld(flat(9, 9), 1500) + let id = w.spawnRobot(rtMiner, loc(4, 4), teamA) + let r = w.robotsById[id] + w.stats.soup[0] = 10_000 + for i in 1 .. 10: + w.submitTransaction(r, msg(i), i) + checkEq("ten submitted", w.stats.transactionsSent[0], 10) + w.currentRound = 1 + w.processBlockchain() + checkEq("seven minted this round", w.stats.blockchainsSent[0], 7) + checkEq("three still in the pool", w.txPool.len, 3) + checkEq("and the block carries seven", w.blockchain[0].len, 7) + checkEq("the most expensive went first", w.blockchain[0][0].cost, 10) + w.currentRound = 2 + w.processBlockchain() + checkEq("the leftovers mint next round", w.stats.blockchainsSent[0], 10) + checkEq("and the pool is empty", w.txPool.len, 0) + +block: + ## `getBlock` refuses non-positive rounds and the CURRENT round. + var w = newWorld(flat(9, 9), 1500) + w.currentRound = 3 + w.blockchain = @[@[], @[], @[]] + checkEq("round 0 is refused", w.getBlock(0).len, 0) + checkEq("the current round is refused", w.getBlock(3).len, 0) + checkEq("a past round is allowed", w.getBlock(1).len, 0) + +block: + ## THE QUIRK. `RobotControllerImpl.random` is a Java `private static Random` + ## assigned `new Random(gameWorld.getMapSeed())` IN THE CONSTRUCTOR, so it is + ## re-seeded on EVERY SPAWN. This vector fails if the RNG is seeded once. + var w = newWorld(flat(9, 9), 1500) + w.stats.soup[0] = 10_000 + let a = w.spawnRobot(rtMiner, loc(1, 1), teamA) + ## Draw the first id from a fresh stream, for reference. + var reference = initJavaRandom(w.map.randomSeed) + let firstDraw = reference.nextInt() + w.submitTransaction(w.robotsById[a], msg(1), 1) + checkEq("the first transaction id is the first draw of a fresh stream", + w.txPool[0].id, firstDraw) + + ## Now spawn another robot; the static RNG is re-seeded, so the NEXT + ## transaction id is the FIRST draw again, not the second. + discard w.spawnRobot(rtMiner, loc(2, 2), teamA) + w.submitTransaction(w.robotsById[a], msg(2), 1) + checkEq("a spawn RE-SEEDS the transaction RNG", w.txPool[1].id, firstDraw) + + ## And with no spawn in between the stream advances normally. + let secondDraw = reference.nextInt() + w.submitTransaction(w.robotsById[a], msg(3), 1) + checkEq("without a spawn the stream advances", w.txPool[2].id, secondDraw) + check("which is a different value", secondDraw != firstDraw) + +finish("test_bc20_blockchain") diff --git a/tests/test_bc20_burial.nim b/tests/test_bc20_burial.nim new file mode 100644 index 0000000..c75d0a0 --- /dev/null +++ b/tests/test_bc20_burial.nim @@ -0,0 +1,77 @@ +## Burial: fifty dirt kills an HQ, the end-of-round ladder awards +## `hq_destroyed` in the SAME round, the released dirt raises the tile, and a +## double HQ loss falls through to `quantity`. + +import harness +import battlecode/years/bc20/[constants, flood, world] + +proc flat(width, height, elevation: int): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(elevation) + result.water.add(false) + result.pollution.add(0) + result.soup.add(0) + +proc bury(w: World, target: Loc, from0: Loc) = + let landId = w.spawnRobot(rtLandscaper, from0, teamA) + let r = w.robotsById[landId] + for i in 1 .. 60: + r.dirtCarrying = 25 + r.cooldownTurns = 0.0'f32 + w.depositDirt(r, fromDelta(target.x - from0.x, target.y - from0.y)) + if w.getRobot(target) == nil: break + +block: + var w = newWorld(flat(11, 11, 5), 1500) + let hqB = w.spawnRobot(rtHq, loc(5, 5), teamB) + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + checkEq("an HQ takes fifty dirt", RobotSpecs[rtHq].dirtLimit, 50) + w.bury(loc(5, 5), loc(4, 5)) + check("the HQ is gone", hqB notin w.robotsById) + check("and team B is recorded as having lost it", w.stats.destroyedHq[1]) + checkEq("the released 50 dirt raised the tile", w.getDirt(loc(5, 5)), + 5 + RobotSpecs[rtHq].dirtLimit) + + ## The ladder awards `hq_destroyed` to the other team in the same round. + w.checkEndOfMatch() + check("a winner was set", w.hasWinner) + checkEq("and it is the other team", w.winner, teamA) + checkEq("on hq_destroyed", $w.domination, "hq_destroyed") + +block: + ## A DOUBLE HQ loss in one round is not `hq_destroyed`: the ladder falls + ## through to `quantity`. + var w = newWorld(flat(11, 11, 5), 1500) + let hqA = w.spawnRobot(rtHq, loc(1, 1), teamA) + let hqB = w.spawnRobot(rtHq, loc(9, 9), teamB) + discard w.spawnRobot(rtMiner, loc(2, 2), teamA) + w.destroyRobot(hqA) + w.destroyRobot(hqB) + check("both HQs are recorded lost", + w.stats.destroyedHq[0] and w.stats.destroyedHq[1]) + w.checkEndOfMatch() + check("a winner was still set", w.hasWinner) + checkEq("but not on hq_destroyed", $w.domination, "quantity") + checkEq("and it is the side with more robots", w.winner, teamA) + +block: + ## An HQ that is never walled DROWNS, and the cause is recorded as such. + var spec = flat(11, 11, 0) + spec.water[0] = true + var w = newWorld(spec, 1500) + let hqA = w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(9, 9), teamB) + w.currentRound = 300 + w.updateWaterLevel() + for i in 1 .. 4: + w.floodfill() + check("the HQ drowned", hqA notin w.robotsById) + checkEq("with the cause recorded", w.hqLostCause[0], "drowned") + check("and the round recorded", w.hqLostRound[0] >= 0) + +finish("test_bc20_burial") diff --git a/tests/test_bc20_cows.nim b/tests/test_bc20_cows.nim new file mode 100644 index 0000000..85eb34f --- /dev/null +++ b/tests/test_bc20_cows.nim @@ -0,0 +1,110 @@ +## The NPC cows: the seed, the four draws, the early exit, the odd-id +## reversal, and the symmetry the reversal reads. + +import harness +import battlecode/rng +import battlecode/years/bc20/[constants, cows, maps, world] + +proc flat(width, height: int, wet: seq[int] = @[]): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 43223 + for i in 0 ..< width * height: + result.elevation.add(0) + result.water.add(i in wet) + result.pollution.add(0) + result.soup.add(0) + +block: + ## `new Random(84307 * mapSeed + 20201 * (cowId / 2))`, in Java `int` + ## arithmetic, WHICH OVERFLOWS: 84307 * 43223 alone is 3 644 001 461. Java + ## wraps; a checked conversion would raise on exactly the seeds that matter. + checkEq("the wrapped product for map seed 43223 and cow 0", + cowSeed(43223, 0), int(cast[int32](3644001461'u32))) + checkEq("cow 1 shares cow 0's seed (id / 2)", cowSeed(43223, 1), + cowSeed(43223, 0)) + checkEq("cow 2 does not", cowSeed(43223, 2), + int(cast[int32](3644001461'u32 + 20201'u32))) + +block: + ## A READY cow draws up to four directions and STOPS THE MOMENT IT MOVES. + var w = newWorld(flat(9, 9), 1500) + let id = w.spawnRobot(6, rtCow, loc(4, 4), teamNeutral) + let cow = w.robotsById[id] + cow.cooldownTurns = 0.0'f32 + var reference = initJavaRandom(cowSeed(w.map.randomSeed, 6)) + ## On an empty flat map the first draw is always legal, so exactly ONE + ## nextDouble is consumed. + let firstDir = MoveDirs[int(reference.nextDouble() * 8.0)] + w.runCow(cow) + checkEq("the cow moved in the first drawn direction", cow.loc, + loc(4, 4) + firstDir) + var after = w.cowRand[id] + var fresh = initJavaRandom(cowSeed(w.map.randomSeed, 6)) + discard fresh.nextDouble() + checkEq("and exactly one draw was consumed", after.seed, fresh.seed) + +block: + ## A cow that is NOT ready burns exactly four `nextDouble` calls and does + ## nothing. + var w = newWorld(flat(9, 9), 1500) + let id = w.spawnRobot(6, rtCow, loc(4, 4), teamNeutral) + let cow = w.robotsById[id] + cow.cooldownTurns = 5.0'f32 + w.runCow(cow) + checkEq("it did not move", cow.loc, loc(4, 4)) + var fresh = initJavaRandom(cowSeed(w.map.randomSeed, 6)) + for i in 1 .. 4: discard fresh.nextDouble() + checkEq("and burned exactly four draws", w.cowRand[id].seed, fresh.seed) + +block: + ## An ODD id reverses the drawn direction through the map symmetry. + for sym in [symVertical, symHorizontal, symRotational]: + var w = newWorld(flat(9, 9), 1500) + w.symmetry = sym + checkEq("north reverses correctly under " & $sym, + w.reverseDirection(dNorth), + (case sym + of symVertical: dNorth + of symHorizontal: dSouth + of symRotational: dSouth)) + checkEq("east reverses correctly under " & $sym, + w.reverseDirection(dEast), + (case sym + of symVertical: dWest + of symHorizontal: dEast + of symRotational: dWest)) + checkEq("northeast reverses correctly under " & $sym, + w.reverseDirection(dNortheast), + (case sym + of symVertical: dNorthwest + of symHorizontal: dSoutheast + of symRotational: dSouthwest)) + +block: + ## A cow never walks into water. + var w = newWorld(flat(5, 5, @[0, 1, 2, 3, 4, 5, 9, 10, 14, 15, 19, + 20, 21, 22, 23, 24]), 1500) + let id = w.spawnRobot(6, rtCow, loc(2, 2), teamNeutral) + let cow = w.robotsById[id] + for turn in 1 .. 50: + cow.cooldownTurns = 0.0'f32 + w.runCow(cow) + if cow.dead: break + check("the cow is still alive", not cow.dead) + check("and stayed on dry land", not w.isFlooded(cow.loc)) + +block: + ## The sim's own symmetry detector agrees with the converter's committed + ## value on every map in the variant's pool, on round 1. + for name in MixedPool: + let spec = loadMap(name) + let w = newWorld(spec, 1500) + checkEq("the sim recomputes " & name & "'s symmetry as committed", + w.symmetry, spec.symmetry) + checkEq("and a fresh recomputation agrees: " & name, + w.recomputeSymmetry(), spec.symmetry) + +finish("test_bc20_cows") diff --git a/tests/test_bc20_dirt.nim b/tests/test_bc20_dirt.nim new file mode 100644 index 0000000..edee934 --- /dev/null +++ b/tests/test_bc20_dirt.nim @@ -0,0 +1,126 @@ +## Dirt: dig, deposit, the 25-carry limit, burying a building at its health, +## the released dirt, and `MAX_DIRT_DIFFERENCE` gating movement and placement +## but never a drone. + +import harness +import battlecode/years/bc20/[constants, maps, world] + +proc flat(width, height, elevation: int, wet: seq[int] = @[]): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(elevation) + result.water.add(i in wet) + result.pollution.add(0) + result.soup.add(0) + +proc ready(w: World, r: Robot) = r.cooldownTurns = 0.0'f32 + +block: + ## Dig lowers an empty tile by 1 and raises `dirtCarrying`, to at most 25. + var w = newWorld(flat(9, 9, 5), 1500) + let id = w.spawnRobot(rtLandscaper, loc(4, 4), teamA) + let r = w.robotsById[id] + for i in 1 .. 25: + w.ready(r) + w.digDirt(r, dEast) + checkEq("the landscaper is full at the dirt limit", r.dirtCarrying, 25) + checkEq("and the tile went down by 25", w.getDirt(loc(5, 4)), 5 - 25) + w.ready(r) + check("a full landscaper cannot dig", not w.canDigDirt(r, dEast)) + w.ready(r) + w.depositDirt(r, dWest) + checkEq("a deposit costs one carried dirt", r.dirtCarrying, 24) + checkEq("and raises the target tile", w.getDirt(loc(3, 4)), 6) + checkEq("the team's dirt-moved counter counts both", + w.stats.dirtMoved[0], 26) + +block: + ## Dig on a CLEAN building is illegal; on a dirty one it takes 1 off the + ## building, not off the ground. + var w = newWorld(flat(9, 9, 5), 1500) + let landId = w.spawnRobot(rtLandscaper, loc(4, 4), teamA) + let gunId = w.spawnRobot(rtNetGun, loc(5, 4), teamA) + let r = w.robotsById[landId] + let gun = w.robotsById[gunId] + w.ready(r) + check("digging out from under a clean building is illegal", + not w.canDigDirt(r, dEast)) + gun.dirtCarrying = 4 + w.ready(r) + check("but a dirty building can be dug out", w.canDigDirt(r, dEast)) + w.digDirt(r, dEast) + checkEq("the dirt came off the BUILDING", gun.dirtCarrying, 3) + checkEq("and not off the ground", w.getDirt(loc(5, 4)), 5) + +block: + ## Deposit on a building adds 1 and DESTROYS it at its health, releasing + ## that much dirt onto the vacated tile. + var w = newWorld(flat(9, 9, 5), 1500) + let landId = w.spawnRobot(rtLandscaper, loc(4, 4), teamA) + let gunId = w.spawnRobot(rtNetGun, loc(5, 4), teamB) + let r = w.robotsById[landId] + r.dirtCarrying = 25 + for i in 1 .. RobotSpecs[rtNetGun].dirtLimit: + w.ready(r) + w.depositDirt(r, dEast) + check("the net gun is buried at 15 dirt", gunId notin w.robotsById) + checkEq("and the 15 dirt landed on the tile", w.getDirt(loc(5, 4)), 5 + 15) + +block: + ## An HQ needs FIFTY dirt, and the burial is recorded as such. + var w = newWorld(flat(9, 9, 5), 1500) + let hqId = w.spawnRobot(rtHq, loc(5, 4), teamB) + let landId = w.spawnRobot(rtLandscaper, loc(4, 4), teamA) + let r = w.robotsById[landId] + for i in 1 .. RobotSpecs[rtHq].dirtLimit: + r.dirtCarrying = 25 + w.ready(r) + w.depositDirt(r, dEast) + if hqId notin w.robotsById: break + check("the HQ is buried", hqId notin w.robotsById) + check("team B's HQ is recorded destroyed", w.stats.destroyedHq[1]) + checkEq("with the cause recorded as burial", w.hqLostCause[1], "buried") + +block: + ## A dying landscaper drops its carry onto its own tile. + var w = newWorld(flat(9, 9, 5), 1500) + let id = w.spawnRobot(rtLandscaper, loc(4, 4), teamA) + w.robotsById[id].dirtCarrying = 7 + w.destroyRobot(id) + checkEq("the carried dirt landed where it died", w.getDirt(loc(4, 4)), + 5 + 7) + +block: + ## MAX_DIRT_DIFFERENCE gates movement and non-drone placement, never a drone. + var w = newWorld(flat(9, 9, 0), 1500) + w.elevation[w.idx(loc(5, 4))] = MaxDirtDifference + 1 + let minerId = w.spawnRobot(rtMiner, loc(4, 4), teamA) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(4, 5), teamA) + w.ready(w.robotsById[minerId]) + w.ready(w.robotsById[droneId]) + check("a miner cannot step up four", not w.canMove(w.robotsById[minerId], dEast)) + w.elevation[w.idx(loc(5, 4))] = MaxDirtDifference + check("but it can step up three", w.canMove(w.robotsById[minerId], dEast)) + w.elevation[w.idx(loc(5, 5))] = 40 + check("a drone ignores the elevation step entirely", + w.canMove(w.robotsById[droneId], dEast)) + + ## Placement: the same gate, and the same drone exemption. + var w2 = newWorld(flat(9, 9, 0), 1500) + w2.elevation[w2.idx(loc(5, 4))] = MaxDirtDifference + 1 + w2.stats.soup[0] = 10_000 + let fcId = w2.spawnRobot(rtFulfillmentCenter, loc(4, 4), teamA) + let mineId = w2.spawnRobot(rtMiner, loc(4, 6), teamA) + w2.ready(w2.robotsById[fcId]) + w2.ready(w2.robotsById[mineId]) + check("a miner cannot found a building four steps up", + not w2.canBuildRobot(w2.robotsById[mineId], rtNetGun, dSouth) or + w2.getDirtDifference(loc(4, 6), loc(4, 5)) <= MaxDirtDifference) + check("a fulfillment center can drop a drone four steps up", + w2.canBuildRobot(w2.robotsById[fcId], rtDeliveryDrone, dEast)) + +finish("test_bc20_dirt") diff --git a/tests/test_bc20_drone.nim b/tests/test_bc20_drone.nim new file mode 100644 index 0000000..c03f585 --- /dev/null +++ b/tests/test_bc20_drone.nim @@ -0,0 +1,121 @@ +## The delivery drone: pickup radius, what may and may not be lifted, the +## blocked rider, the drop, and what a dying drone does with its cargo. + +import harness +import battlecode/years/bc20/[constants, world] + +proc flat(width, height, elevation: int, wet: seq[int] = @[]): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(elevation) + result.water.add(i in wet) + result.pollution.add(0) + result.soup.add(0) + +proc ready(w: World, r: Robot) = r.cooldownTurns = 0.0'f32 + +block: + ## Pickup radius squared is 3, and only UNITS may be lifted. + var w = newWorld(flat(11, 11, 0), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(5, 5), teamA) + let drone = w.robotsById[droneId] + let nearId = w.spawnRobot(rtMiner, loc(6, 6), teamB) ## d2 = 2 + let farId = w.spawnRobot(rtMiner, loc(7, 7), teamB) ## d2 = 8 + let gunId = w.spawnRobot(rtNetGun, loc(4, 5), teamB) ## a BUILDING + let otherId = w.spawnRobot(rtDeliveryDrone, loc(5, 6), teamB) + w.ready(drone) + checkEq("the pickup radius squared is 3", DeliveryDronePickupRadiusSquared, 3) + check("a unit at d2 = 2 can be picked up", w.canPickUpUnit(drone, nearId)) + check("a unit at d2 = 8 cannot", not w.canPickUpUnit(drone, farId)) + check("a building can never be picked up", not w.canPickUpUnit(drone, gunId)) + check("and neither can another drone", not w.canPickUpUnit(drone, otherId)) + +block: + ## The held unit is BLOCKED — no turn, cooldown frozen — and rides with the + ## drone. + var w = newWorld(flat(11, 11, 0), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(5, 5), teamA) + let riderId = w.spawnRobot(rtLandscaper, loc(5, 6), teamB) + let drone = w.robotsById[droneId] + let rider = w.robotsById[riderId] + rider.cooldownTurns = 4.0'f32 + w.ready(drone) + w.pickUpUnit(drone, riderId) + check("the drone holds a unit", drone.holdingUnit) + checkEq("and knows which", drone.heldId, riderId) + check("the rider is blocked", rider.blocked) + checkEq("the rider rides at the drone's tile", rider.loc, drone.loc) + check("the rider's old tile is empty", w.getRobot(loc(5, 6)) == nil) + checkEq("the drone's pickup counter moved", w.stats.dronePickups[0], 1) + ## A blocked robot never runs `processBeginningOfTurn`, so its cooldown does + ## not decay — the round loop skips it entirely. Assert the invariant the + ## loop relies on. + checkEq("the rider's cooldown is frozen where it was", + rider.cooldownTurns, 4.0'f32) + + ## Moving the drone moves the rider. + w.ready(drone) + w.move(drone, dNorth) + checkEq("the rider follows the drone", rider.loc, drone.loc) + + ## A drop needs an unoccupied, on-map tile. + w.ready(drone) + discard w.spawnRobot(rtMiner, drone.loc + dEast, teamA) + check("a drop onto an occupied tile is refused", + not w.canDropUnit(drone, dEast)) + check("but a clear tile is fine", w.canDropUnit(drone, dWest)) + w.dropUnit(drone, dWest) + check("the rider is unblocked", not rider.blocked) + check("and the drone is empty", not drone.holdingUnit) + +block: + ## A drop into water destroys a non-drone, and counts as a water drop when + ## the victim is an enemy. + var w = newWorld(flat(11, 11, 0, @[5 + 11 * 4]), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(5, 5), teamA) + let riderId = w.spawnRobot(rtLandscaper, loc(6, 5), teamB) + let drone = w.robotsById[droneId] + w.ready(drone) + w.pickUpUnit(drone, riderId) + w.ready(drone) + w.dropUnit(drone, dSouth) + check("the enemy landscaper drowned", riderId notin w.robotsById) + checkEq("and it is recorded as a water drop", w.stats.droneWaterDrops[0], 1) + +block: + ## A dying drone drops its cargo on its OWN tile — and the cargo drowns if + ## that tile is water. + var w = newWorld(flat(11, 11, 0), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(5, 5), teamA) + let riderId = w.spawnRobot(rtMiner, loc(6, 5), teamB) + let drone = w.robotsById[droneId] + w.ready(drone) + w.pickUpUnit(drone, riderId) + w.destroyRobot(droneId) + check("the rider survived", riderId in w.robotsById) + checkEq("and stands where the drone died", w.robotsById[riderId].loc, + loc(5, 5)) + check("and is unblocked", not w.robotsById[riderId].blocked) + check("and is back on the grid", w.getRobot(loc(5, 5)) != nil) + +block: + ## A carried COW stops polluting: `processEndOfTurn` never runs for a + ## blocked body, and `updateRobot` clears its effect instead. + var w = newWorld(flat(11, 11, 0), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(5, 5), teamA) + let cowId = w.spawnRobot(rtCow, loc(6, 5), teamNeutral) + let cow = w.robotsById[cowId] + w.processEndOfTurn(cow) + check("a free cow pollutes", w.getPollution(loc(6, 5)) > 0) + let drone = w.robotsById[droneId] + w.ready(drone) + w.pickUpUnit(drone, cowId) + check("the cow is blocked", cow.blocked) + w.resetPollutionForRobot(cowId) + checkEq("and its pollution is cleared", w.getPollution(loc(6, 5)), 0) + +finish("test_bc20_drone") diff --git a/tests/test_bc20_flood.nim b/tests/test_bc20_flood.nim new file mode 100644 index 0000000..0fd0261 --- /dev/null +++ b/tests/test_bc20_flood.nim @@ -0,0 +1,113 @@ +## The rising water: the committed level table, the one-ring-per-round flood +## fill, drowning, resurfacing and drone-drop survival. + +import std/strutils +import harness +import battlecode/years/bc20/[constants, flood, maps, world] + +# --- the committed table matches the spec's own checkpoints ------------------ +block: + ## `GameConstants.getWaterLevel` is generated under the CI JDK into + ## `data/bc20/water_levels.json` as float32 bit patterns; these are the + ## rounds the 2020 spec itself quotes. + checkEq("elevation 1 floods at round 256", roundWaterReaches(1), 256) + checkEq("elevation 2 floods at round 464", roundWaterReaches(2), 464) + checkEq("elevation 3 floods at round 677", roundWaterReaches(3), 677) + checkEq("elevation 4 floods at round 931", roundWaterReaches(4), 931) + checkEq("elevation 5 floods at round 1210", roundWaterReaches(5), 1210) + checkEq("elevation 6 floods at round 1413", roundWaterReaches(6), 1413) + check("elevation 7 is outside the 1500-round cap", + roundWaterReaches(7) > 1500) + checkEq("round 0 is dry", waterLevelAt(0), 0.0'f32) + check("the level rises monotonically over the whole table", (block: + var ok = true + var prev = waterLevelAt(0) + for r in 1 .. WaterTableMaxRound: + let here = waterLevelAt(r) + if here < prev: ok = false + prev = here + ok)) + +# --- a hand-built world ----------------------------------------------------- +proc flat(width, height, elevation: int, wet: seq[int] = @[]): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + result.initialWater = 0 + for i in 0 ..< width * height: + result.elevation.add(elevation) + result.water.add(i in wet) + result.pollution.add(0) + result.soup.add(0) + +block: + ## The flood advances EXACTLY ONE RING per round, because the origin set is + ## snapshotted before anything changes. + var w = newWorld(flat(9, 9, 0, @[4 + 9 * 4]), 1500) + w.currentRound = 300 + w.updateWaterLevel() + check("the water is above elevation 0", w.waterLevel > 0.0'f32) + checkEq("one flooded tile to start", w.floodedCount, 1) + w.floodfill() + checkEq("one ring after one fill", w.floodedCount, 9) + w.floodfill() + checkEq("two rings after two fills", w.floodedCount, 25) + w.floodfill() + checkEq("three rings after three fills", w.floodedCount, 49) + +block: + ## A non-flying robot on a newly flooded tile dies; a drone does not. + var w = newWorld(flat(7, 7, 0, @[0]), 1500) + w.currentRound = 300 + w.updateWaterLevel() + let minerId = w.spawnRobot(rtMiner, loc(1, 0), teamA) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(0, 1), teamA) + w.floodfill() + check("the miner drowned", minerId notin w.robotsById) + check("the drone did not", droneId in w.robotsById) + +block: + ## A deposit that lifts a tile to `elevation >= waterLevel` resurfaces it in + ## the same action (`tryResurface`). + var w = newWorld(flat(7, 7, 0, @[3 + 7 * 3]), 1500) + w.currentRound = 250 + w.updateWaterLevel() + check("the water is under elevation 1", w.waterLevel < 1.0'f32) + check("the tile starts flooded", w.isFlooded(loc(3, 3))) + w.addDirt(loc(3, 3), 1) + check("and resurfaces the moment the dirt lands", + not w.isFlooded(loc(3, 3))) + checkEq("the flooded count follows", w.floodedCount, 0) + +block: + ## The initial flooded set comes from the map's own `water` array. + let spec = loadMap("WateredDown") + var w = newWorld(spec, 1500) + var expected = 0 + for wet in spec.water: + if wet: expected += 1 + checkEq("the initial flooded set is the map's", w.floodedCount, expected) + checkEq("and the water starts at the map's initialWater", + w.waterLevel, float32(spec.initialWater)) + +block: + ## A drone dropped into water survives; a miner does not. + var w = newWorld(flat(7, 7, 0, @[0, 1, 2, 3, 4, 5, 6]), 1500) + let droneId = w.spawnRobot(rtDeliveryDrone, loc(3, 2), teamA) + let minerId = w.spawnRobot(rtMiner, loc(3, 3), teamA) + let drone = w.robotsById[droneId] + drone.cooldownTurns = 0 + w.pickUpUnit(drone, minerId) + check("the miner is held", drone.holdingUnit) + check("and is blocked", w.robotsById[minerId].blocked) + drone.cooldownTurns = 0 + ## Fly the drone over the water and drop. + drone.loc = loc(3, 1) + w.robotsById[minerId].loc = loc(3, 1) + w.dropUnit(drone, dSouth) + check("the miner dropped into water died", minerId notin w.robotsById) + check("and the drone is still flying", droneId in w.robotsById) + +finish("test_bc20_flood") diff --git a/tests/test_bc20_knobs.nim b/tests/test_bc20_knobs.nim new file mode 100644 index 0000000..ed4729f --- /dev/null +++ b/tests/test_bc20_knobs.nim @@ -0,0 +1,184 @@ +## THE bc20 KNOB-TEETH GATE. +## +## Every one of the ten knobs must visibly change play. For each, this shard +## plays a paired set of seeded games — identical map, identical opponent, the +## two sides identical except that knob at its LOW and HIGH setting — and +## asserts a named, SIGNED statistic moves. A knob that is inert fails the +## build. +## +## The paired set is two `small`-pool maps under both side assignments, i.e. +## four games per setting. The episode seed only chooses maps and sides — the +## world RNG comes from the MAP's own `randomSeed` — so those four games are +## every distinct game these inputs can produce, and adding "more seeds" would +## add duplicates, not information. +## +## The thresholds live in ONE table so tuning is a one-line change, and each is +## set at roughly half the measured delta so a real regression trips it and +## ordinary drift does not. Measured at GameVersion GV04: +## +## knob low -> high measured gate +## opening turtle -> rush adj 0 -> 2 >= 1 +## terraform_start_round 900 -> 100 dirt 761->6286 +150 % +## lattice_radius 3 -> 10 raised 163->380 +60 +## landscaper_count_curve lean -> swarm land 14 -> 43 +50 % +## miner_count_curve lean -> swarm miners 32->100 +50 % +## vaporator_budget 0 -> 6 vap 0 -> 1 >= 1 +## poll 1173->696 -3 +## drone_role carry -> harass drops 0 -> 20 +5 +## net_gun_ring 0 -> 6 guns 0 -> 15 +4 +## rush_trigger 0 -> 220 adj 0 -> 1 >= 1 +## wall_hq_round 0 -> 250 drowned 4 -> 0 +## ring 0 -> 32 +6 +## +## DECLARED DEVIATION from the design note's table: `net_gun_ring` is gated on +## net guns BUILT only, not additionally on enemy drones shot down. The HQ has +## a built-in net gun and shoots on every turn it is ready, so `net_gun_kills` +## barely moves when the ring is added — the counter cannot separate the ring's +## kills from the HQ's, and inventing a second counter to make a gate pass +## would be gating on the instrument rather than on the play. + +import std/strutils +import harness +import battlecode/[baselines, sheet] +import battlecode/years/bc20/[constants, maps, rules, world] +import battlecode/years/bc20/chassis/kit + +const + Maps = ["WateredDown", "ALandDivided"] + Chassis = [ckBowlOfChowder, ckBowlOfChowder] + +type Measured = object + landscapers, miners, drones, netGuns, vaporators: int + dirt, waterDrops, hqDrowned, ringAboveFive, raisedNearHq: int + reachedEnemyHq: int + pollutionAt1000: int + +proc runSet(knobs: string, rounds: int): Measured = + ## The knob under test on seat 0; the all-defaults doctrine on seat 1. + let sheet = parseReply("{\"sheet\":{" & knobs & "}}", YearBc20) + let opponent = baselineSheet("bc20", blBowlOfChowder) + for mapName in Maps: + for sideAslot in [0, 1]: + let spec = loadMap(mapName) + var w = newWorld(spec, rounds) + var sides = newSides([sheet, opponent], Chassis, sideAslot) + let team = if sideAslot == 0: 0 else: 1 + let hqs = spec.hqLocations() + let mine = hqs[team] + let theirs = hqs[1 - team] + var reached = false + for round in 1 .. rounds: + runRound(w, sides, Chassis) + if not reached: + for id, r in w.robotsById: + if r.team == Team(team) and chebyshev(r.loc, theirs) <= 1: + reached = true + break + if round == 1000: + result.pollutionAt1000 += w.globalPollution + if not w.running: break + result.landscapers += w.stats.landscapersBuilt[team] + result.miners += w.stats.minersBuilt[team] + result.drones += w.stats.dronesBuilt[team] + result.netGuns += w.stats.netGunsBuilt[team] + result.vaporators += w.stats.vaporatorsBuilt[team] + result.dirt += w.stats.dirtMoved[team] + result.waterDrops += w.stats.droneWaterDrops[team] + if w.stats.destroyedHq[team]: result.hqDrowned += 1 + if reached: result.reachedEnemyHq += 1 + for l in w.ringTiles(mine): + if w.getDirt(l) >= 5: result.ringAboveFive += 1 + for x in max(0, mine.x - 10) .. min(w.width - 1, mine.x + 10): + for y in max(0, mine.y - 10) .. min(w.height - 1, mine.y + 10): + if float32(w.getDirt(loc(x, y))) > w.waterLevel: + result.raisedNearHq += 1 + +proc upByPercent(name: string, low, high, percent: int) = + check(name & " (" & $low & " -> " & $high & ", gate +" & $percent & "%)", + high >= low + max(1, low * percent div 100)) + +# --- opening ---------------------------------------------------------------- +block: + let low = runSet("\"opening\":\"turtle\"", 700) + let high = runSet("\"opening\":\"rush\"", 700) + check("opening turtle -> rush puts a unit next to the enemy HQ (" & + $low.reachedEnemyHq & " -> " & $high.reachedEnemyHq & ")", + high.reachedEnemyHq >= low.reachedEnemyHq + 1) + +# --- terraform_start_round -------------------------------------------------- +block: + let low = runSet("\"terraform_start_round\":900", 700) + let high = runSet("\"terraform_start_round\":100", 700) + upByPercent("terraform_start_round 900 -> 100 moves more dirt by round 700", + low.dirt, high.dirt, 150) + +# --- lattice_radius --------------------------------------------------------- +block: + let low = runSet("\"lattice_radius\":3", 900) + let high = runSet("\"lattice_radius\":10", 900) + check("lattice_radius 3 -> 10 raises more tiles above the water (" & + $low.raisedNearHq & " -> " & $high.raisedNearHq & ")", + high.raisedNearHq >= low.raisedNearHq + 60) + +# --- landscaper_count_curve ------------------------------------------------- +block: + let low = runSet("\"landscaper_count_curve\":\"lean\"", 900) + let high = runSet("\"landscaper_count_curve\":\"swarm\"", 900) + upByPercent("landscaper_count_curve lean -> swarm builds more landscapers", + low.landscapers, high.landscapers, 50) + +# --- miner_count_curve ------------------------------------------------------ +block: + let low = runSet("\"miner_count_curve\":\"lean\"", 900) + let high = runSet("\"miner_count_curve\":\"swarm\"", 900) + upByPercent("miner_count_curve lean -> swarm builds more miners", + low.miners, high.miners, 50) + +# --- vaporator_budget ------------------------------------------------------- +block: + let low = runSet("\"vaporator_budget\":0", 1050) + let high = runSet("\"vaporator_budget\":6", 1050) + check("vaporator_budget 0 -> 6 builds vaporators (" & $low.vaporators & + " -> " & $high.vaporators & ")", + low.vaporators == 0 and high.vaporators >= 1) + check("and global pollution at round 1000 is lower (" & + $low.pollutionAt1000 & " -> " & $high.pollutionAt1000 & ")", + high.pollutionAt1000 <= low.pollutionAt1000 - 3) + +# --- drone_role ------------------------------------------------------------- +block: + let low = runSet("\"drone_role\":\"carry_landscapers\"", 900) + let high = runSet("\"drone_role\":\"harass\"", 900) + check("drone_role carry_landscapers -> harass drops more enemies in the " & + "water (" & $low.waterDrops & " -> " & $high.waterDrops & ")", + high.waterDrops >= low.waterDrops + 5) + +# --- net_gun_ring ----------------------------------------------------------- +block: + let low = runSet("\"net_gun_ring\":0", 900) + let high = runSet("\"net_gun_ring\":6", 900) + checkEq("net_gun_ring 0 builds no net guns at all", low.netGuns, 0) + check("and net_gun_ring 6 builds them (" & $low.netGuns & " -> " & + $high.netGuns & ")", high.netGuns >= low.netGuns + 4) + +# --- rush_trigger ----------------------------------------------------------- +block: + let low = runSet("\"rush_trigger\":0", 500) + let high = runSet("\"rush_trigger\":220", 500) + check("rush_trigger 0 -> 220 puts a unit next to the enemy HQ by round " & + "500 (" & $low.reachedEnemyHq & " -> " & $high.reachedEnemyHq & ")", + high.reachedEnemyHq >= low.reachedEnemyHq + 1) + +# --- wall_hq_round ---------------------------------------------------------- +block: + let low = runSet("\"wall_hq_round\":0", 1250) + let high = runSet("\"wall_hq_round\":250", 1250) + check("wall_hq_round 0 drowns the HQ in every game (" & $low.hqDrowned & + " of 4)", low.hqDrowned == 4) + check("and wall_hq_round 250 drowns it in none (" & $high.hqDrowned & + " of 4)", high.hqDrowned == 0) + check("and the ring stands well above the round-250 water level (" & + $low.ringAboveFive & " -> " & $high.ringAboveFive & ")", + high.ringAboveFive >= low.ringAboveFive + 6) + +finish("test_bc20_knobs") diff --git a/tests/test_bc20_maps.nim b/tests/test_bc20_maps.nim new file mode 100644 index 0000000..0ac2fc3 --- /dev/null +++ b/tests/test_bc20_maps.nim @@ -0,0 +1,140 @@ +## The converted bc20 maps: every one re-converts identically (checked by +## `tools/ci/check_bc20_maps.sh` in CI, and by shape here), the sizes and +## symmetries match the pinned table, the sim's own per-spawn detector agrees +## with the converter, and every array is `width x height`. + +import std/[algorithm, json, os, strutils, tables] +import harness +import battlecode/years/bc20/[constants, maps, world] + +const Pinned = { + "maptestsmall": (32, 32, "vertical"), + "WateredDown": (32, 32, "horizontal"), + "Infinity": (33, 33, "rotational"), + "Spiral": (33, 33, "rotational"), + "Hourglass": (40, 32, "horizontal"), + "ALandDivided": (41, 32, "vertical"), + "Climb": (40, 40, "rotational"), + "Constriction": (40, 40, "rotational"), + "CentralLake": (41, 41, "rotational"), + "Toothpaste": (45, 37, "rotational"), + "TwoLakeLand": (45, 45, "horizontal"), + "CentralSoup": (48, 48, "rotational"), + "Hills": (60, 60, "rotational"), + "OmgThisIsProcedural": (60, 64, "vertical"), + "Squares": (63, 63, "vertical"), + "BeachFrontProperty": (64, 64, "rotational"), + "Maze": (64, 64, "rotational"), + "TheHighGround": (64, 64, "rotational"), +}.toTable + ## Read out of the real `.map20` flatbuffers, not assumed. + +block: + checkEq("the small pool has six maps", SmallPool.len, 6) + checkEq("the mixed pool has twelve", MixedPool.len, 12) + checkEq("the large pool has six", LargePool.len, 6) + for name in SmallPool: + check(name & " is also in the mixed pool", name in MixedPool) + var all: seq[string] + for pool in [@SmallPool, @MixedPool, @LargePool]: + for name in pool: + if name notin all: all.add(name) + checkEq("eighteen distinct maps are committed", all.len, 18) + check("CowFarm is deliberately excluded", "CowFarm" notin all) + check("and DidAMonkeyMakeThis too", "DidAMonkeyMakeThis" notin all) + checkEq("an unknown pool name is empty, not silently mixed", + poolNames("nonsense").len, 0) + +block: + for name, want in Pinned: + let spec = loadMap(name) + checkEq(name & " has the pinned width", spec.width, want[0]) + checkEq(name & " has the pinned height", spec.height, want[1]) + checkEq(name & " has the pinned symmetry", + ($spec.symmetry).replace("sym", "").toLowerAscii(), want[2]) + let n = spec.width * spec.height + checkEq(name & "'s elevation array is width x height", spec.elevation.len, n) + checkEq(name & "'s water array is width x height", spec.water.len, n) + checkEq(name & "'s pollution array is width x height", + spec.pollution.len, n) + checkEq(name & "'s soup array is width x height", spec.soup.len, n) + + var hqs = 0 + for body in spec.initialBodies: + if body.kind == rtHq: inc hqs + check(name & "'s bodies are on the map", + body.loc.x >= 0 and body.loc.y >= 0 and + body.loc.x < spec.width and body.loc.y < spec.height) + if body.kind == rtCow: + checkEq(name & "'s cows are neutral", body.team, teamNeutral) + else: + check(name & "'s HQs belong to a side", body.team != teamNeutral) + checkEq(name & " has exactly two HQs", hqs, 2) + + ## The spec says every map carries a tile at `MIN_WATER_ELEVATION`. + ## `Infinity` does not — its floor is -12 — and the port follows the FILE + ## rather than the prose (docs/RULES-BC20.md §Divergences item 11). + var lowest = high(int) + for e in spec.elevation: lowest = min(lowest, e) + if name == "Infinity": + checkEq("Infinity's real floor is -12, not MIN_WATER_ELEVATION", + lowest, -12) + else: + checkEq(name & " has a tile at MIN_WATER_ELEVATION", lowest, + MinWaterElevation) + +block: + ## The sim recomputes the symmetry on every NEUTRAL spawn from the CURRENT + ## world; it must land on the committed value for every map, on round 1. + for name, _ in Pinned: + let spec = loadMap(name) + let w = newWorld(spec, 1500) + checkEq("the sim agrees with the converter on " & name, w.symmetry, + spec.symmetry) + +block: + ## The committed JSON is the byte-stable form the converter emits: sorted + ## keys, no spaces, one trailing newline. CI re-converts and diffs; this + ## catches a hand-edit that never went through the converter at all. + for name, _ in Pinned: + let raw = readFile(mapPath(name)) + check(name & "'s file ends in exactly one newline", + raw.endsWith("\n") and not raw.endsWith("\n\n")) + check(name & "'s file is compact JSON", ", " notin raw and ": " notin raw) + let doc = parseJson(raw) + var keys: seq[string] + for key, _ in doc: keys.add(key) + var sorted = keys + sorted.sort() + checkEq(name & "'s keys are sorted", keys, sorted) + +block: + ## The draw is deterministic in the episode seed, distinct, and inside the + ## pool; sides alternate every game. + let drawn = drawMaps("mixed", 871345, 3) + checkEq("three maps are drawn", drawn.len, 3) + check("all distinct", + drawn[0] != drawn[1] and drawn[1] != drawn[2] and drawn[0] != drawn[2]) + for name in drawn: + check(name & " is in the mixed pool", name in MixedPool) + checkEq("and the draw is deterministic", drawMaps("mixed", 871345, 3), drawn) + for g in 0 .. 2: + checkEq("sides alternate at game " & $g, sideAslotFor(871345, g), + sideAslotFor(871345, 0) xor (g and 1)) + +block: + ## The map card is what a doctrine plans against, and both seats see the + ## same numbers on a symmetric map. + let spec = loadMap("CentralLake") + let a = spec.mapCard(0, 0) + let b = spec.mapCard(1, 0) + checkEq("both seats see the same HQ elevation", + a["hq_elevation"].getInt(), b["hq_elevation"].getInt()) + checkEq("and the same separation", + a["hq_separation"].getInt(), b["hq_separation"].getInt()) + checkEq("only the side differs", a["you_are"].getStr(), "A") + checkEq("for the other seat", b["you_are"].getStr(), "B") + check("the card names the soup", a["soup_tiles"].getInt() > 0) + check("and the cows", a["cows"].getInt() >= 0) + +finish("test_bc20_maps") diff --git a/tests/test_bc20_perf.nim b/tests/test_bc20_perf.nim new file mode 100644 index 0000000..08de979 --- /dev/null +++ b/tests/test_bc20_perf.nim @@ -0,0 +1,79 @@ +## THE bc20 PERF GATE. +## +## A full 1499-round game on `CentralSoup` (48x48, the largest map in the +## `bc20` variant's pool) with both scripted chassis must complete in <= 55 s. +## The design note's sanctioned fix if this ever goes red is ONE config value — +## `gamesPerMatch: 3 -> 1` in the `bc20` variant — not a redesign. +## +## The budget arithmetic this protects: the match guard is 320 s for three +## games, so a single game must sit comfortably inside 100 s +## (`perGameBudgetSeconds`) or a hosted episode settles `deadline` instead of +## `complete`. + +import std/[monotimes, times] +import harness +import battlecode/[baselines, sheet] +import battlecode/years/bc20/[maps, rules] + +const Budget = 55.0 + +var largest = "" +var largestTiles = 0 +for name in MixedPool: + let spec = loadMap(name) + if spec.width * spec.height > largestTiles: + largestTiles = spec.width * spec.height + largest = name +echo "largest map in the bc20 pool: ", largest, " (", largestTiles, " tiles)" +checkEq("and it is CentralSoup, as the design note pins", largest, + "CentralSoup") + +proc timeGame(sheets: array[2, Sheet], + chassis: array[2, ChassisKind]): (float, GameOutcome20) = + let started = getMonoTime() + let (w, outcome) = playGame(loadMap(largest), sheets, chassis, 0, 0, 1500, 0) + ((getMonoTime() - started).inMilliseconds.float / 1000.0, outcome) + +let boc = baselineSheet("bc20", blBowlOfChowder) +let scaffold = baselineSheet("bc20", blExamplefuncsplayer) +let ckBoc = parseChassis("bowl-of-chowder") +let ckScaffold = parseChassis("examplefuncsplayer") + +block: + let (seconds, outcome) = timeGame([boc, boc], [ckBoc, ckBoc]) + echo "bowl-of-chowder mirror: ", outcome.roundsPlayed, " rounds in ", + seconds, " s" + check("the mirror plays a full game in <= 55 s (" & $seconds & " s)", + seconds <= Budget) + checkEq("and it really ran to the cap", outcome.roundsPlayed, 1499) + +block: + let (seconds, outcome) = timeGame([scaffold, scaffold], + [ckScaffold, ckScaffold]) + echo "scaffold mirror: ", outcome.roundsPlayed, " rounds in ", seconds, " s" + check("the scaffold mirror is inside the budget (" & $seconds & " s)", + seconds <= Budget) + +block: + ## The mixed pair, which is what `docker-smoke`'s bc20 episode runs. + let (seconds, outcome) = timeGame([boc, scaffold], [ckBoc, ckScaffold]) + echo "bowl-of-chowder vs scaffold: ", outcome.roundsPlayed, " rounds in ", + seconds, " s" + check("the mixed pair is inside the budget (" & $seconds & " s)", + seconds <= Budget) + +block: + ## The `docker-smoke` bc20 episode itself: 300 rounds on the small pool, + ## which must finish in seconds and still outlast the viewer's 10 s soak. + let started = getMonoTime() + let (w, outcome) = playGame(loadMap("maptestsmall"), [boc, scaffold], + [ckBoc, ckScaffold], 0, 0, 300, 0) + let seconds = (getMonoTime() - started).inMilliseconds.float / 1000.0 + echo "the smoke episode: ", outcome.roundsPlayed, " rounds in ", seconds, " s" + check("the smoke game is under 10 s (" & $seconds & " s)", seconds <= 10.0) + ## A 300-round game plays for ~12 s at 24 fps, so the recording outlasts the + ## viewer smoke's 10 s soak (the ecos 2026-08-23 scar). + check("and the recording outlasts the 10 s viewer soak", + outcome.roundsPlayed.float / 24.0 > 10.0) + +finish("test_bc20_perf") diff --git a/tests/test_bc20_pollution.nim b/tests/test_bc20_pollution.nim new file mode 100644 index 0000000..d58fc52 --- /dev/null +++ b/tests/test_bc20_pollution.nim @@ -0,0 +1,105 @@ +## Pollution: the global floor, the one-round refinery effect, the vaporator's +## multiplicative scrub, cow haze, and both coefficients in Java `float`. + +import harness +import battlecode/years/bc20/[constants, pollution, world] + +proc flat(width, height: int): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(0) + result.water.add(false) + result.pollution.add(0) + result.soup.add(0) + +block: + ## The closed forms, in float32, and `Math.round(float)` as `floor(x + 0.5)`. + checkEq("cooldownCoefficient(0)", cooldownCoefficient(0), 1.0'f32) + checkEq("cooldownCoefficient(2000)", cooldownCoefficient(2000), 2.0'f32) + checkEq("sensorCoefficient(0)", sensorCoefficient(0), 1.0'f32) + checkEq("sensorCoefficient(4000)", sensorCoefficient(4000), 0.25'f32) + checkEq("Math.round rounds a half UP", javaRoundF32(0.5'f32), 1) + checkEq("and a negative half toward positive infinity", + javaRoundF32(-0.5'f32), 0) + checkEq("javaRoundF32(2.49)", javaRoundF32(2.49'f32), 2) + checkEq("javaRoundF32(2.5)", javaRoundF32(2.5'f32), 3) + +block: + ## Global pollution FLOORS at zero. + var w = newWorld(flat(9, 9), 1500) + w.addGlobalPollution(3) + checkEq("it goes up", w.globalPollution, 3) + w.addGlobalPollution(-10) + checkEq("and floors at zero", w.globalPollution, 0) + checkEq("and the peak is remembered", w.globalPollutionPeak, 3) + +block: + ## A refinery's local +500 lasts EXACTLY ONE ROUND: it is installed at the + ## end of its own turn and removed at the start of the next one. + var w = newWorld(flat(15, 15), 1500) + let id = w.spawnRobot(rtRefinery, loc(7, 7), teamA) + let r = w.robotsById[id] + r.soupCarrying = 40 + checkEq("no pollution before the turn", w.getPollution(loc(7, 7)), 0) + w.processEndOfTurn(r) + checkEq("the local additive is the type's", + w.getPollution(loc(7, 7)), 500 + w.globalPollution) + checkEq("it refined at most maxSoupProduced", + w.stats.soupRefined[0], RobotSpecs[rtRefinery].maxSoupProduced) + checkEq("and pushed global pollution up by one", w.globalPollution, 1) + ## 7.1 of its OWN next turn removes it — and it re-installs, because it is + ## still carrying soup. + w.resetPollutionForRobot(id) + checkEq("cleared", w.getPollution(loc(7, 7)), w.globalPollution) + ## The radius is the type's, so a tile outside r^2 = 35 never saw it. + w.processEndOfTurn(r) + check("a tile inside r^2 = 35 is polluted", + w.getPollution(loc(7, 12)) > w.globalPollution) + checkEq("a tile outside is not", w.getPollution(loc(0, 0)), + w.globalPollution) + +block: + ## The vaporator: x0.80 locally, -1 globally, +2 soup unconditionally. + var w = newWorld(flat(15, 15), 1500) + w.globalPollution = 1000 + let id = w.spawnRobot(rtVaporator, loc(7, 7), teamA) + let r = w.robotsById[id] + w.processEndOfTurn(r) + checkEq("it prints two soup", w.stats.soup[0], InitialSoup + 2) + checkEq("and scrubs one point of global pollution", w.globalPollution, 999) + checkEq("and multiplies the local reading by 0.80", + w.getPollution(loc(7, 7)), javaRoundF32(float32(999.0'f32 * 0.80'f32))) + +block: + ## Cows: +2000 over r^2 = 15, every round, unconditionally. + var w = newWorld(flat(15, 15), 1500) + let id = w.spawnRobot(rtCow, loc(7, 7), teamNeutral) + w.processEndOfTurn(w.robotsById[id]) + checkEq("the cow's haze is 2000", w.getPollution(loc(7, 7)), 2000) + checkEq("at the edge of r^2 = 15 too", w.getPollution(loc(10, 8)), 2000) + checkEq("and nothing beyond it", w.getPollution(loc(12, 7)), 0) + checkEq("a cow does not touch global pollution", w.globalPollution, 0) + +block: + ## Pollution shrinks the sensed radius and lengthens the cooldown, both in + ## float32. + var w = newWorld(flat(21, 21), 1500) + let id = w.spawnRobot(rtMiner, loc(10, 10), teamA) + let r = w.robotsById[id] + checkEq("a clean miner senses its full radius", + w.currentSensorRadiusSquared(r), RobotSpecs[rtMiner].sensorRadiusSquared) + w.globalPollution = 4000 + checkEq("at 4000 pollution it senses a quarter as far", + w.currentSensorRadiusSquared(r), + javaRoundF32(float32(float32(RobotSpecs[rtMiner].sensorRadiusSquared) * + 0.25'f32))) + r.cooldownTurns = 0.0'f32 + w.addCooldownTurns(r) + checkEq("and its action costs three cooldown turns instead of one", + r.cooldownTurns, 3.0'f32) + +finish("test_bc20_pollution") diff --git a/tests/test_bc20_scoring.nim b/tests/test_bc20_scoring.nim new file mode 100644 index 0000000..2a1f1da --- /dev/null +++ b/tests/test_bc20_scoring.nim @@ -0,0 +1,158 @@ +## Scoring: the points formula with float32 narrowing and truncation, the +## tiebreak ladder in the engine's order with a vector for each rung, the +## round-limit off-by-one, and every `end_reason` value producible. + +import harness +import battlecode/years/bc20/[constants, world] + +proc flat(width, height: int): MapSpec = + result.name = "flat" + result.width = width + result.height = height + result.symmetry = symRotational + result.randomSeed = 4242 + for i in 0 ..< width * height: + result.elevation.add(0) + result.water.add(false) + result.pollution.add(0) + result.soup.add(0) + +proc bare(): World = newWorld(flat(15, 15), 1500) + +# --- the points formula ----------------------------------------------------- +block: + ## Both HQs alive, identical rosters: 30 + 12.5 + 7.5 = 50 apiece. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + let pts = w.gamePoints() + checkEq("a dead heat is 50 apiece", pts, [50, 50]) + check("points are inside [0, 100]", + pts[0] >= 0 and pts[0] <= 100 and pts[1] >= 0 and pts[1] <= 100) + check("and the two seats sum to at most 100", pts[0] + pts[1] <= 100) + +block: + ## One HQ gone: the whole survival term and the whole unit share to the + ## survivor. Both pools still hold their `INITIAL_SOUP`, so the NET WORTH + ## share stays even and each side keeps 7 of the 15 — which is exactly the + ## point of scoring the ladder continuously rather than as a win flag. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + let hqB = w.spawnRobot(rtHq, loc(13, 13), teamB) + w.destroyRobot(hqB) + let pts = w.gamePoints() + checkEq("the survivor takes survival and the unit share", pts, [92, 7]) + check("and the loser keeps only its half of the net-worth share", + pts[1] < 15) + +block: + ## `units` counts BUILDINGS TOO — exactly the QUANTITY rung — and `worth` + ## includes the team pool — exactly the QUALITY rung. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + discard w.spawnRobot(rtNetGun, loc(2, 1), teamA) + checkEq("a building counts toward the unit share", w.livingUnits(teamA), 2) + w.stats.soup[0] = 1000 + checkEq("the pool counts toward net worth", w.netWorth(teamA), + 1000 + RobotSpecs[rtNetGun].cost) + let pts = w.gamePoints() + check("and the richer, larger side scores higher", pts[0] > pts[1]) + +block: + ## The float32 narrowing is load-bearing: the same arithmetic runs natively + ## and in wasm32 and must produce the same integer. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + for i in 0 ..< 3: discard w.spawnRobot(rtMiner, loc(3 + i, 1), teamA) + w.stats.soup[0] = 7 + w.stats.soup[1] = 11 + let survival = 0.5'f32 + let shareU = float32(4) / float32(5) + let worthA = float32(7 + 3 * RobotSpecs[rtMiner].cost) + let worthB = float32(11) + let shareW = worthA / (worthA + worthB) + let want = int(60.0'f32 * survival + 25.0'f32 * shareU + 15.0'f32 * shareW) + checkEq("the formula is reproduced exactly", w.gamePoints()[0], want) + +# --- the tiebreak ladder, one vector per rung ------------------------------- +block: + ## Rung 1: HQ destroyed. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + let hqB = w.spawnRobot(rtHq, loc(13, 13), teamB) + w.destroyRobot(hqB) + w.checkEndOfMatch() + checkEq("rung 1 is hq_destroyed", $w.domination, "hq_destroyed") + checkEq("and the survivor wins", w.winner, teamA) + +block: + ## Rung 2: quantity — more robots alive, buildings included. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + discard w.spawnRobot(rtNetGun, loc(2, 1), teamA) + w.currentRound = w.maxRounds - 1 + w.checkEndOfMatch() + checkEq("rung 2 is quantity", $w.domination, "quantity") + checkEq("and the larger side wins", w.winner, teamA) + +block: + ## Rung 3: quality — equal robots, greater net worth. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + w.stats.soup[1] = w.stats.soup[0] + 50 + w.currentRound = w.maxRounds - 1 + w.checkEndOfMatch() + checkEq("rung 3 is quality", $w.domination, "quality") + checkEq("and the richer side wins", w.winner, teamB) + +block: + ## Rung 4: broadcasts — equal worth, more MINTED transactions. + var w = bare() + discard w.spawnRobot(rtHq, loc(1, 1), teamA) + discard w.spawnRobot(rtHq, loc(13, 13), teamB) + w.stats.blockchainsSent = [3, 1] + w.currentRound = w.maxRounds - 1 + w.checkEndOfMatch() + checkEq("rung 4 is broadcasts", $w.domination, "broadcasts") + checkEq("and the chattier side wins", w.winner, teamA) + +block: + ## Rung 5: the highest living non-neutral robot id. + var w = bare() + discard w.spawnRobot(50_000, rtHq, loc(1, 1), teamA) + discard w.spawnRobot(50_001, rtHq, loc(13, 13), teamB) + discard w.spawnRobot(9, rtCow, loc(7, 7), teamNeutral) + w.currentRound = w.maxRounds - 1 + w.checkEndOfMatch() + checkEq("rung 5 is highest_id", $w.domination, "highest_id") + checkEq("and the higher id wins", w.winner, teamB) + +block: + ## Rung 6: reachable only when NEITHER team has a living robot, and driven + ## by the world RNG rather than `Math.random()`. + var w = bare() + discard w.spawnRobot(9, rtCow, loc(7, 7), teamNeutral) + w.currentRound = w.maxRounds - 1 + w.checkEndOfMatch() + checkEq("rung 6 is coin_flip", $w.domination, "coin_flip") + check("and it is deterministic", (block: + var again = bare() + discard again.spawnRobot(9, rtCow, loc(7, 7), teamNeutral) + again.currentRound = again.maxRounds - 1 + again.checkEndOfMatch() + again.winner == w.winner)) + +block: + ## `roundLimitReached` is the engine's `round >= rounds - 1`, so a + ## 1500-round cap plays 1499. + var w = newWorld(flat(9, 9), 1500) + w.currentRound = 1498 + check("round 1498 has not reached the limit", not w.timeLimitReached()) + w.currentRound = 1499 + check("round 1499 has", w.timeLimitReached()) + +finish("test_bc20_scoring") diff --git a/tests/test_bc20_sheet.nim b/tests/test_bc20_sheet.nim new file mode 100644 index 0000000..325c564 --- /dev/null +++ b/tests/test_bc20_sheet.nim @@ -0,0 +1,160 @@ +## The bc20 doctrine sheet: every one of the ten knobs repaired to its default, +## the recording of every repair, and — the D1 assertion — that a submitted +## `chassis` is recorded as an UNKNOWN FIELD and never honoured. + +import std/[json, strutils, unicode] +import harness +import battlecode/[sheet, sim_types] +import battlecode/years/bc20/knobs + +proc bc20(text: string): Sheet = parseReply(text, YearBc20) + +# --- D1: the chassis is NOT a knob ------------------------------------------ +block: + ## THE D1 ASSERTION. It fails the moment anyone re-adds the knob. + checkEq("the bc20 sheet has exactly ten knobs", KnownKeys20.len, 10) + check("and `chassis` is not one of them", "chassis" notin KnownKeys20) + let s = bc20("""{"sheet":{"chassis":"examplefuncsplayer","opening":"rush"}}""") + check("a submitted chassis is recorded as an UNKNOWN field", + "chassis" in s.unknownFields) + check("and never as a repaired default", + "chassis" notin s.defaultsApplied) + checkEq("the rest of the sheet still applies", s.doctrine20.opening, opRush) + check("and the emitted sheet carries no chassis key", + not s.toJson().hasKey("chassis")) + ## The chassis a seat drives comes from PLAYER_SCRIPTED, so a sheet that + ## names one must not be able to change it. + let other = bc20("""{"sheet":{"chassis":"bowl-of-chowder","opening":"rush"}}""") + checkEq("two sheets differing only in chassis are the same doctrine", + $other.toJson(), $s.toJson()) + +# --- absent / out of range / mistyped, for all ten -------------------------- +block: + let empty = bc20("{}") + let d = empty.doctrine20 + checkEq("opening defaults", d.opening, opPassiveLattice) + checkEq("terraform_start_round defaults", d.terraformStartRound, 300) + checkEq("lattice_radius defaults", d.latticeRadius, 6) + checkEq("landscaper_count_curve defaults", d.landscaperCountCurve, ccSteady) + checkEq("miner_count_curve defaults", d.minerCountCurve, ccSteady) + checkEq("vaporator_budget defaults", d.vaporatorBudget, 2) + checkEq("drone_role defaults", d.droneRole, drHarass) + checkEq("net_gun_ring defaults", d.netGunRing, 2) + checkEq("rush_trigger defaults to NEVER", d.rushTrigger, 0) + checkEq("wall_hq_round defaults", d.wallHqRound, 250) + checkEq("an absent knob is not a repair", empty.defaultsApplied.len, 0) + +block: + ## Out of range: that field alone takes its default AND is recorded. + let cases = { + "terraform_start_round": %0, + "lattice_radius": %99, + "vaporator_budget": %(-1), + "net_gun_ring": %7, + "rush_trigger": %1501, + "wall_hq_round": %(-4), + } + for (key, value) in cases: + var payload = newJObject() + var inner = newJObject() + inner[key] = value + payload["sheet"] = inner + let s = validate(payload, YearBc20) + check(key & " out of range is recorded", key in s.defaultsApplied) + let defaults = defaultDoctrine20() + let s = validate(%*{"sheet": {"lattice_radius": 99}}, YearBc20) + checkEq("and the field keeps its default", s.doctrine20.latticeRadius, + defaults.latticeRadius) + +block: + ## Mistyped: an enum given a number, a number given an object. + let a = validate(%*{"sheet": {"opening": 7}}, YearBc20) + check("a mistyped enum is recorded", "opening" in a.defaultsApplied) + checkEq("and keeps its default", a.doctrine20.opening, opPassiveLattice) + let b = validate(%*{"sheet": {"lattice_radius": {"nope": 1}}}, YearBc20) + check("a mistyped number is recorded", "lattice_radius" in b.defaultsApplied) + let c = validate(%*{"sheet": {"drone_role": "nonsense"}}, YearBc20) + check("an unknown enum VALUE is recorded", "drone_role" in c.defaultsApplied) + +block: + ## Every legal value of every enum knob is honoured. + for value in ["rush", "lattice", "passive_lattice", "turtle"]: + let s = bc20("""{"sheet":{"opening":"""" & value & """"}}""") + checkEq("opening " & value & " is honoured", $s.doctrine20.opening, value) + checkEq("and is not a repair", s.defaultsApplied.len, 0) + for value in ["harass", "wall", "buster", "carry_landscapers"]: + let s = bc20("""{"sheet":{"drone_role":"""" & value & """"}}""") + checkEq("drone_role " & value & " is honoured", $s.doctrine20.droneRole, + value) + for value in ["lean", "steady", "swarm"]: + let s = bc20("""{"sheet":{"landscaper_count_curve":"""" & value & """"}}""") + checkEq("landscaper_count_curve " & value & " is honoured", + $s.doctrine20.landscaperCountCurve, value) + +# --- unknown keys, caps and rune boundaries --------------------------------- +block: + var inner = newJObject() + for i in 0 ..< 30: + inner["nonsense_" & $i] = %i + let s = validate(%*{"sheet": inner}, YearBc20) + check("unknown keys are capped at MaxUnknownFields", + s.unknownFields.len <= MaxUnknownFields) + for field in s.unknownFields: + check("and each is capped at MaxUnknownFieldRunes", + field.runeLen <= MaxUnknownFieldRunes) + +block: + ## Rune-boundary truncation, including astral-plane characters. + let astral = "\u{1F9C0}" ## U+1F9C0, four bytes + var notes = "" + for i in 0 ..< 400: notes.add(astral) + let payload = %*{"sheet": {"opening": "rush"}, "notes": notes, + "motto": notes} + let s = validate(payload, YearBc20) + checkEq("notes are cut to the rune cap", s.notes.runeLen, MaxNoteRunes) + checkEq("motto too", s.motto.runeLen, MaxMottoRunes) + check("and the bytes are still valid UTF-8", (block: + var ok = true + try: discard $(%s.notes) except CatchableError: ok = false + ok)) + +block: + ## The whole-reply cap is BYTES, cut on a rune boundary. + var padding = "" + for i in 0 ..< 20_000: padding.add("\u{1F9C0}") + let reply = """{"sheet":{"opening":"rush"},"notes":"""" & padding & """"}""" + check("an oversized reply is longer than the cap", reply.len > MaxReplyBytes) + let cut = reply.truncateBytes(MaxReplyBytes) + check("the cut is inside the cap", cut.len <= MaxReplyBytes) + checkEq("and lands on a rune boundary", cut.len mod 4, + ("""{"sheet":{"opening":"rush"},"notes":"""").len mod 4) + +# --- plain words ------------------------------------------------------------ +block: + let s = bc20("""{"sheet":{"opening":"rush","rush_trigger":240, + "wall_hq_round":300,"lattice_radius":3, + "landscaper_count_curve":"swarm", + "vaporator_budget":0,"net_gun_ring":0, + "drone_role":"harass"}}""") + let words = s.plainWords().join(" | ") + check("the overlay says it rushes", "rushes" in words) + check("and when", "commits at round 240" in words) + check("and when it walls", "walls at 300" in words) + check("and that it skipped vaporators", "no vaporators" in words) + check("and that it skipped net guns", "no net guns" in words) + check("and what its drones do", "drones harass" in words) + +block: + ## The curve knobs really do change the targets they name. + let lean = bc20("""{"sheet":{"landscaper_count_curve":"lean", + "miner_count_curve":"lean"}}""").doctrine20 + let swarm = bc20("""{"sheet":{"landscaper_count_curve":"swarm", + "miner_count_curve":"swarm"}}""").doctrine20 + check("swarm wants more landscapers than lean", + swarm.landscaperTarget(600) > lean.landscaperTarget(600)) + check("and more miners", swarm.minerTarget(600) > lean.minerTarget(600)) + check("the landscaper target is capped at 40", + swarm.landscaperTarget(100_000) == 40) + check("and the miner target at 25", swarm.minerTarget(100_000) == 25) + +finish("test_bc20_sheet") diff --git a/tests/test_manifest.nim b/tests/test_manifest.nim index 5d983b8..02008cd 100644 --- a/tests/test_manifest.nim +++ b/tests/test_manifest.nim @@ -47,13 +47,47 @@ block: reasons.add(v.getStr()) checkEq("the reason enum is the closed set", reasons, @EpisodeReasons) + ## `games.items.required` is narrowed to the FIVE year-neutral keys, and + ## each year's own statistics are optional siblings — the two sets do not + ## collide. Deliberately not a nested `stats` object: nesting would change + ## the bytes every shipped bc26 replay's `result` block carries. var gameKeys: seq[string] for v in game["results_schema"]["properties"]["games"]["items"]["required"]: gameKeys.add(v.getStr()) - var expectedGameKeys = @GameKeys + var expectedGameKeys = @RequiredGameKeys gameKeys.sort() expectedGameKeys.sort() - checkEq("the per-game key set matches too", gameKeys, expectedGameKeys) + checkEq("the per-game required key set is year-neutral", gameKeys, + expectedGameKeys) + + var gameProps: seq[string] + for key, _ in game["results_schema"]["properties"]["games"]["items"]["properties"]: + gameProps.add(key) + for key in RequiredGameKeys: + check("the schema declares the required game key " & key, key in gameProps) + for key in Bc26GameKeys: + check("the schema keeps bc26's optional game key " & key, key in gameProps) + for key in Bc20GameKeys: + check("the schema declares bc20's optional game key " & key, + key in gameProps) + for key in Bc20GameKeys: + check("and bc20's key does not collide with bc26's", + key notin Bc26GameKeys) + + var endReasons: seq[string] + for v in game["results_schema"]["properties"]["games"]["items"]["properties"]["end_reason"]["enum"]: + endReasons.add(v.getStr()) + var wantEndReasons = @EndReasons + endReasons.sort() + wantEndReasons.sort() + checkEq("end_reason is the union of both years plus abandoned", endReasons, + wantEndReasons) + + var yearEnum: seq[string] + for v in game["config_schema"]["properties"]["year"]["enum"]: + yearEnum.add(v.getStr()) + checkEq("config_schema.year.enum names both years", yearEnum, + @["bc26", "bc20"]) block: ## The third leg: what docker_smoke.sh actually asserts. @@ -65,7 +99,16 @@ block: # --- num_agents ------------------------------------------------------------- block: - checkEq("exactly one variant ships in v1", variants.len, 1) + ## ONE VARIANT PER BATTLECODE YEAR. + checkEq("one variant per registered year", variants.len, 2) + var variantIds: seq[string] + for variant in variants: variantIds.add(variant["id"].getStr()) + checkEq("and they are the registered years", variantIds, @["bc26", "bc20"]) + for variant in variants: + check("variant " & variant["id"].getStr() & " is a registered year", + isRegisteredYear(variant["game_config"]["year"].getStr())) + checkEq("variant " & variant["id"].getStr() & " names its own year", + variant["game_config"]["year"].getStr(), variant["id"].getStr()) check("variants are TOP LEVEL", not game.hasKey("variants")) check("and so is certification", not game.hasKey("certification")) for variant in variants: @@ -159,7 +202,7 @@ block: checkEq("docs.readme is an object", docs["readme"].kind, JObject) check("docs.readme has type and value", docs["readme"].hasKey("type") and docs["readme"].hasKey("value")) - checkEq("three doc pages ship", docs["pages"].len, 3) + checkEq("four doc pages ship", docs["pages"].len, 4) var ids: seq[string] for page in docs["pages"]: ids.add(page["id"].getStr()) @@ -173,7 +216,7 @@ block: toUpperAscii() & ".md" check("the page's file exists: " & target, fileExists(target)) checkEq("the pages are the ones the design note names", ids, - @["rules.md", "replay.md", "parity.md"]) + @["rules.md", "rules-bc20.md", "replay.md", "parity.md"]) # --- the rest of the shape -------------------------------------------------- block: @@ -195,7 +238,13 @@ block: "secret://coworld/battlecode/anthropic_api_key") checkEq("the replay viewer is the STATIC bundle", game["replay_viewer"]["bundle"].getStr(), "static-replay-viewer") - checkEq("both players are declared", manifest["player"].len, 2) + ## Two published baselines per year: `awu`/`scaffold` for bc26 and + ## `bowl-of-chowder`/`examplefuncsplayer` for bc20. + checkEq("all four baselines are declared", manifest["player"].len, 4) + var playerIds: seq[string] + for p in manifest["player"]: playerIds.add(p["id"].getStr()) + checkEq("and they are the four the design note names", playerIds, + @["awu", "scaffold", "bowl-of-chowder", "examplefuncsplayer"]) for p in manifest["player"]: checkEq(p["id"].getStr() & " runs the player entrypoint", p["run"][0].getStr(), "/bin/battlecode-player") @@ -209,7 +258,8 @@ block: # --- the policy set --------------------------------------------------------- block: let policies = parseJson(readFile("tools/ci/policies.json")) - checkEq("four policies ship", policies.len, 4) + ## Four per year: two `PLAYER_PROMPT` champions and two scripted fillers. + checkEq("eight policies ship — four per year", policies.len, 8) var prompts = 0 var scripted = 0 var owned = 0 @@ -232,15 +282,29 @@ block: p["env"]["PLAYER_PROMPT"].getStr().len > 200) if p["env"].hasKey("PLAYER_SCRIPTED"): inc scripted if p.hasKey("player"): inc owned - checkEq("two LLM champions", prompts, 2) - checkEq("two scripted baselines", scripted, 2) - checkEq("champion #2 carries its owning player", owned, 1) - checkEq("and it is the second prompt policy", + checkEq("two LLM champions per year", prompts, 4) + checkEq("two scripted baselines per year", scripted, 4) + checkEq("each year's champion #2 carries its owning player", owned, 2) + checkEq("bc26 champion #2 is the second prompt policy", policies[1]["player"].getStr(), "ply_bac48eb1-662e-44f8-973d-f3e016dccf5d") - check("the two champion prompts differ", + checkEq("bc20 champion #2 is the sixth policy", + policies[5]["player"].getStr(), + "ply_bac48eb1-662e-44f8-973d-f3e016dccf5d") + check("the two bc26 champion prompts differ", policies[0]["env"]["PLAYER_PROMPT"].getStr() != policies[1]["env"]["PLAYER_PROMPT"].getStr()) + check("the two bc20 champion prompts differ", + policies[4]["env"]["PLAYER_PROMPT"].getStr() != + policies[5]["env"]["PLAYER_PROMPT"].getStr()) + checkEq("and the bc20 policies are named for the year", + policies[4]["name"].getStr(), "battlecode-bc20-latticer") + checkEq("bc20 champion #2 is the rusher", + policies[5]["name"].getStr(), "battlecode-bc20-rusher") + checkEq("the bc20 fillers name the two published chassis", + policies[6]["env"]["PLAYER_SCRIPTED"].getStr() & "," & + policies[7]["env"]["PLAYER_SCRIPTED"].getStr(), + "bowl-of-chowder,examplefuncsplayer") # --- compose.yaml service names are load-bearing ---------------------------- block: diff --git a/tools/__pycache__/convert_maps_bc20.cpython-311.pyc b/tools/__pycache__/convert_maps_bc20.cpython-311.pyc deleted file mode 100644 index 8e5d7ab557e649d504a6327466b55a3b1ac1286e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18695 zcmdUX`)?apzTXUA;+uL|b~TNf*ws90W>?Fog7^f^Cj70seWtP-vF zjH5OKcbXGzA9AAoD+9-Ujh_}q?d*vIPn;D`9HQ%86UU7@M_r@tQIF_;7yTQp9<3Rz z9jzPnj@IX&H;ir?Z5(Y9J@2B{(aob<#2w=14^5-ZVu#p@Ym2y36mV@7cZqGdZWTMl zc3g$PHgWqU))T4Dzy;dy6TU*XzAki*`wDpY8b2+3fbn18I(PdcW{x`<3110Fig3cO zD51baL<|Uf_w3yxjP=a;XZP+I6DC7`WqfXOG9bx<5)sBkzvA!3Gr4zsV(*^bBVqqc z;8@Q^IT9Z0vD;4$b&m|48x+nDpM9rC7?}=80U;<0;Ye9Mf)wzJ0jY~pU!PZ|BVoZG z7U}NQx6TPcx|_F4b73VogTgcZsem9WfQJNS8qweks{p;h-9%(&Ca5R@QF!&j`QaX6 zObN(}+)L0${qs>zd&0q)F}oD#p3nv>v-ZT`sEk7HZHqe*OojToMqE9 z6$l3;KOr(dCF8-c7z|GVLy>T3UKo>6e8%5<(SOB1)@3i=%3}9L)$AKLdZW)2u8(|!D|@s zcxiMc-1JP2%VT!G!YY~#hQr9H5gQvF3j1Xl3kS)Q!9a-B!XlXd3-6peH#qXfJHpTf zVfg%rFf=TToEa2O4V@j-dZ1rn8fHOTY$D|OnVEnh&1=;XQtUz2*@3~4!IL9{147`M zpADLDb=5!5dYC6DF#?0}ks_hqlaZ@f=M;Tiid+d|@%2mvlneQm#tsP+ zn6V%zj+JH=MkM62j}dPOPWVG(UBcLOL<&YxjdmwR6u%OTgmHIR2>2(ag+M4c6AUvN z0!rCT1|=C7q(yR-Sb<%R%*}QoW8exaE9`2+6e?|GI)gnT7fC%O1;-8IRQAQ-yB?jc+RM-bv z!Fo|^C+9-oJ)xOESn*8&pqxE~Nt7r2vxvJzOa|s|-X}^xfLav}D88w=uxgx$T&24? zU^Arh{VIQ=(zhYqf3dix^A4AezF2IfQ*iQ|jTQpLcss}onN=)#Uutr}0vO{ykhZK`>E9^|H4q<}Ifg;moq`ZhEyws6=VK2Dh};G40Y3~(@?xh!HOYa{q*RXwG8Geq=SO;#nOU|)bu+GvwJ?R9 zs=Ktp!FL(MUx!1hR-Z2z4k|uh)QKMV6z*Q623!DfE>}~ZFfCdV_)u^1OIvWvx$$M~->>aX$Bf@o+eL^0-0<8ah)Lk^qoLiPVC)YMguVK(vYmg~&dneBTNX=bn1% zvz~3K?aQWzmK8(B^HSFH(n^2Ub0BR!AZwd=WP${BX~I|0x@>eusz| zuwN8;EbjPJk;fu?F~kgd?I;Sp5+#f6pd7WcCV*!b^XO|pY2y1V&^{$?MzqtS^5aq; zVvO)qi#A+FkE-cPC=kXz%vd&KZIn@_`k_f^G)dGObu;`H_oLI4>o@@IM_$^Ga&-;3 zSaS_c$&;U*{`7R}{| zK+?S&P;kkM+GDJvP))JhFCSq()7EZ=`sa2C4nj;=BLfxje*?|j z1y)nIt__eGfbEioPctL(d7FTNXs~=fG;A*zV@9MF*F>sF&E@sSjQW~so*;P{h6~8- zM}kJy45P$Ufnr9;44FJ+bEL^M640_n%&L`?OVZs66kWICMH5Ow`EqV~u0NdO2lw%hOOc%VvDMLDM`d{G~}5OD=j8F zl)jK(@%!I;-@Z2ZxSF8dVT6A@s_5pw1<~= zZAeqRwp0AIP6O4X8H2WglB!u)+XCXOLEYMxE+9GT(iU|dg%Kplq}5{dv@E&#hQ%Ea6ZRoXr%^Q#8cOurX;KY6dnA%zHL-gfu{?_7 zai%*yw;v(=!F@4%EZU~AVnQ=j)N>>hfv_MSL!^-BBdUQmy+6gdwC9NSUG{%$S{k|Y z)}6PLft3G~V77i&rn)m*-C4}RrV4D0tv55Q^-}@eCOJV}EcR|p5MI$tlGZ|B3d%s? z{;#PYq+tCC9a(>Y6?&(2;&=NW)%>>an?BrSJV&y4Xg$JuHQZ^Dm|pFrJ}@nO8^y{E zOUyJ@N~IXOUy*uZ-PF^R$}_HmhDt@4#P;W^Bmo?y1Y9&@2ivqurD*$J+mW}|O(pGM zZC;W#I;$nJUQ2rkyab?D`Fvtz!spZMV*QlP_McZNc9y_-0&fwZO`OfEBvNdO0PW(8 zCK-k`BL!1z2n^sSZ{mqF7Ct%CVA?deYBHHSS2;a!n$10{oF2H%=5|Ue2X2G;;sw+2|v<|9;TIdJplU1Yl}2NpBk>Vey2KFL$hE1+{tik8P|FdwB( ztP3=p5vDL*;6$8MLx9W_--4kLL6A)Ft(ky@0P&Y^tpscU1?hoGsphLe5pHZE8Pzf! z2u@8aFk-;fCJ{Zr-vS?->Ku=V^FDH~`TWXrw-}sYn>`$iiu9hg5Qq{dsKr8{mKb}b zE`q)C#MMOpbAG`rx)ahDn3mkSO97qi5yF^66=xXvNHDrT!pFShA~am!xf z$hdQs?k>?BadIt8DCK#KYx77AG4lpGi$mXAo?Co7W=fd~$S=Rf?E1V5t_^cKD{{JH zuDBuQet&Ds^}ZEXOUxB>YyFq%e?s5BQXHioRpMQAT*6uh0poa0m|y(oAD!2*#4kEx zX2}{ei)Hu?^P0oZ|65c$_GjGJU68z)BR+(48E|Wc=JQ7|M(BV@!Lu#U_C`*BXv@r- zf317Qe-YLxII6?s$|Z-j=0@Ks3z4f~Ary(s!Z8g8bSFE0L8lu0Wfh_>cvoj4&awmWOu05S_)8m{9jRd~(tMHtoAl;oBdgH=~aN*52-Wq!A{2PTM z7*J$kH(;K4twS_c;THv;f0t15_;Jovif)A(0t}B_=mA zR;o!$()(2R2L!GHD~v z-ZPZ*Hs3ve`+TZDBilEdg`fN>bt(MxRBx> zd$y%L+j7IkMR>(37Munude1HT|VVztsZM2IkU}%Lhl(@3_x5-3^FEH3(TAHV>ZA{lT^sNDZI&L^5^73=F=ur5?>Si+*uyK%mCYqmpn zw8g5}wqTE0>AK)taKs!jCrNfpZYslVX<$l2DX#Cqf*Tkw!)+;_vbhwmDC0qy_1kIQ zuuS!aWytrN4;Agyh~}6Z<_k;A16UQS2DHX%CJmD&NWX*33Rk;Orx0__hghAyj+iSi z4R-$^b=YFHpb`5I)r9AcnCtJj`&NAmAMlH(FgDuJ)&zxi_k7ToTJTPB3-v*6p&{m7 z;@>|P^KO9Ajzal0bhZg)NaJ4(P@mQ7R+tdlkAR&&9UJ8e#%?$+m))NS#~!6yqZKa7_?hA94@f`g&1|DJ87QiUj3E zDOF#FxR)|mdP?`PnwUG*@D+I4*Z64xHMbYy8_&YjY_<_VF`E46RSq+^`hsV z144JTW^Iq!4jl?eQbam*DB5_44)fzQ>&b|OgRFR4;F=a=HVr1$!DfMj!jfv6jf8L> z2nT_f$oJfENQl~Tz+1w(X6PC)&-u;`4Qto_w^jRv zd07d}3|B!-~uxas#e^L4ECS9pvB&K9T}>ScJ}z7!7D@O2urM@vcK4q#8lV%<_gA^Re?AszsYWO|LNqMHxB^ z2%0XSXc>;G$OL+I4ZnGrne|npL}Y&nXJkqQFSlVBWopd^QBB*a%G0rwMxlSiPyUw> zaG}mRJU5TuIG%AdXC2M)fv0v?LcHF&*cm^W^VHtFdgJQS!&doypi* zvbL7^iD&NWn=?0N?w`uI+q3TW`03o5gerIZSIM2p-ly)ybkmvC$QSS6qJ1;&p{#o- zZ5_g4wYvvzA50$1)U{{p+EW*P^YhPtzWj6OFu%y`csaY{<;4E@;E&7XZAuQ_I+xnN zy#Lon69-VrQN48F`my-HGl%Qup&N%jp2|2{vyRsIK+fr=V@3C^8RxdFa~rZ&*WR_? zw%$zNN)u%gCR z*rx$<`(G~Ie8=&1NB>9w&s;~LH#wVEt(>>vZr|;``$L(!j%-~={A{jiOZ-%>eFtJy zb(yNIIrru#?zYG7wl5E4+`F^x-3eo^y7@_U`{U~NFVAGEyR+5Z3G;eMo-I#2+n}j^ zxijP0o%QU-!M5$Y(%bhXMsB`){#CM>uy*m`g6|Oo9Awv zdx|5C?YE}yOh0XGxj&KGvCMz5>yw##Gug(jTx0Xo`o?6zf zwQx0ccWt+A_jhKh+p^VdxtfMtZR1mKdunj`z{aN z-Pujun2FC?K5h9gI}c`>`m#-Zx%wt5THl(h_U1MVtDBro%POY4{N7(Mn!y}!B=X}?5{t#Z3un`aSVULnkyBwG$`(H$o zpYwKlje=g|Q_bSs%&ZK5R)#o5X=5al4ZV&dsYw45Jre-p91I-J>cvA#6RDAvi)lxH z#?ha3^#6V!<2aKxonZret{pBQSHlF#6hm=%RNY3yD>wL;jtMA5)wNPYP89{BVmpXb zD(5Nks;mimLx5gbTHy2w(ua^NQf0j;Ri>|{(s!({mDw$$b$FW)8xLMc(cTiIiO>;( z3AHnsh9u~_+%!3tZ3{S~1CiZCCW+g|Mf0LP?k zoWd=Xg`J?6%VeFxV;(dh??8FDgKLjz-=z1ESBtImFTGeyMubgxr%lRH+vZTjk1eh$ zZ>CXA%ua*T`t;@`PUzd{Wj)rgYGdMz61b+|wH9X3$8i^bXb!RtubLTd%8Ail>az4L zn)=@~zH!#cry$2ypW3Pyn?Ih;*tTYETc6lEAKN<9yN_pVuVihnJh8s^*!o(=dM;}{ z_nnb!z`%LYzETH3f)^R*-I?hI@qhq21woI>J zo;IRmJyk3a^^l~xCeUJ5%BZt+2u3tMG<}5@z9wp50qVp?wU{@m<)KB-D1M+0y!9+{ z4{bj>zg=`Zta$&K)xPeEtiT6o`C@U@qALn_6X=@1STv!{x|A{{yP`CXP+TmUs@7Mk zMfeM#jsfSTgSk8$nlQr|v5Nllf*B?{GmIFX`v%s!C1y#LIlT%v2fsn|>*B54qcj$8 zxQnhrJmoIL%CBN|`8d&94QsRZ{u*@?Z>rbfz*L!PsWZM*7|Qfq1qQY@J_hO?}p4^4*R7knH^4! zR^?wZhhzbvBH+Wzi2*Qg3*J|Th9BLn`9OPRX#zHZV7RRPUhRctDS%@O(gaw(f%Xi@ z8;Y!21J~fX^+hg4E!v+FL_IS>xIN)srFZb*dzJ1|-<}X4%8r`Z{}DvH@@fxk2lQV6 zlp8Kvy!H<-XYt(;txqsJ7`lY$j^exKa+UjysuHZc+8r zluZ*&t>nNGFySW;g5ToxQd>>dCS+`FSzBBDM9yUS===xgmtI}={_gZQ&{E<&T_ZeuekQIi~&K>tRf6{!fIX?KzWc~2W zN5dZsC-*IHj}K=|y;)Ol+SCiRkyOR{RnB4F&H(hGn#QDfV{ zleZ#wA{kFd*3&_))Zb}LD#>B`Xze&Wi-(|X+P3HFn(iLGee~AxJ2>jZyV_HePdW}f z?l_R?=*xEWJsNo2@k+Ym6?FTq^R_cNqA4)x>b5^UbJjj_wmf#WB*l!gJ?m^wvv01- z@nLk8t1|aNfySw-iZQ&F+uHuw{HOEF^_i``*{!{hom%(g_8y??LCl`3kK`!wg1Y*i z(Th({t*(#jmfpOz`OfCF`-Jw*SWjlHC)4cv2dgXXKJc*&tuJ~fiYUIz0}sz=v5fU# z)_M?rX`G`?yr^kN5FLrWe7;VnW{BBFbbgHhi;=C29edKuGIRinnOdY6#WoQj_ccyk z-JI@_gLTxL*U;NpzTgG)jguvmjq}hl^zlx6OFV?-~oYuO@QPL=|2$oHw6AIfqzF}g}`qK z`~!g{0%-z_C45UUT8!H9zu(a9J_00G6zrYa3$w>0H}c`j$)5qj^uY70rbgbh$`RPd z@$G4@t^E6p^TgRF$2sEcljGcR_Q`RsIQ!%{dz^h%Ee5xMb&&^gJFFM3hGcuHJ#Fp4 zH*Q{a*YJ3Qb}jgh(*9U_u Date: Fri, 4 Sep 2026 05:29:38 +0000 Subject: [PATCH 3/5] bc20: the appended viewer block, the parity oracle and the CI jobs The viewer: `client/replay_broadcast.html` gains an APPENDED bc20 game block with all-new `#bc20-*` ids, a `data-year` CSS gate and CSS for all ten beat kinds; the D3 dismissible doctrine overlay (close button, Esc, re-open chip) sits above `var(--band)`, never in it. `chrome_common.js`, `broadcast_core.js`, `config.nims` and both `static_replay*.js` are byte-for-byte untouched, so no loader is spliced. The renderer fixture gains a bc20 ROW: three widths x two years, checked with --strict-text-bounds. The oracle: `tools/oracle/Bc20Oracle.java` and `tools/parity_trace_bc20.nim` emit the same 67 559-line vector file from the pinned 2020 engine sources and from the Nim port -- the water table, both pollution coefficients over all 65 536 values, Math.round(float), the IDGenerator streams, java.util.Random, the OVERFLOWING cow seed and Transaction's comparator. They diff clean. `tools/oracle/examplefuncsplayer20/` carries the one-hunk determinism patch. CI: a `parity-oracle-bc20` job (JDK 8, two BLOCKING tiers plus a reported engine-from-source attempt), `docker-smoke` runs TWO episodes, `wasm-viewer` executes the bundle against BOTH replays, and the `test` job re-generates and byte-diffs every bc20 generated artefact against the pinned 2020 checkout. --- .github/workflows/ci.yml | 289 +++++++++-- client/replay_broadcast.html | 469 +++++++++++++++++- tests/test_viewer.nim | 55 ++ tools/ci/check_gameversion.sh | 5 +- tools/ci/docker_smoke.sh | 55 +- tools/ci/renderer_fixture.html | 93 +++- tools/oracle/Bc20Oracle.java | 202 ++++++++ .../examplefuncsplayer20/RobotPlayer.java | 233 +++++++++ .../examplefuncsplayer20/determinism.patch | 35 ++ tools/parity_trace_bc20.nim | 125 +++++ 10 files changed, 1508 insertions(+), 53 deletions(-) create mode 100644 tools/oracle/Bc20Oracle.java create mode 100644 tools/oracle/examplefuncsplayer20/RobotPlayer.java create mode 100644 tools/oracle/examplefuncsplayer20/determinism.patch create mode 100644 tools/parity_trace_bc20.nim diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e04a0da..fd3ab2d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,9 @@ name: CI on: push: - branches: [ main ] + # `bc20-year-module` is the branch the second year module lands on. main's + # trigger semantics are untouched; this only adds a branch. + branches: [ main, bc20-year-module ] pull_request: workflow_dispatch: @@ -40,6 +42,10 @@ env: ENGINE_TAG: "engine.1.2.5" ENGINE_JAR_URL: "https://releases.battlecode.org/maven/org/battlecode/battlecode26-java/1.2.5/battlecode26-java-1.2.5.jar" SCALA_JAR_URL: "https://repo1.maven.org/maven2/org/scala-lang/scala-library/2.11.7/scala-library-2.11.7.jar" + # The Battlecode 2020 sources the `bc20` year module is a port OF. Used at CI + # time by the constants generator, the map converter, the sprite cutter and + # the bc20 parity oracle. There is no JDK in any image stage. + BC20_COMMIT: "7618f6be7d12da39f2e6e25801e578f1fecfbd86" jobs: # --------------------------------------------------------------------- @@ -121,6 +127,29 @@ jobs: echo "BC_ENGINE_DIR=/tmp/engine/battlecode26-${ENGINE_TAG}" >> "$GITHUB_ENV" python3 -m pip install --quiet flatbuffers + # The bc20 year module is generated from a SECOND pinned checkout, and + # every generated artefact is re-generated here and byte-diffed: the + # constants table, all eighteen converted maps and the sprite atlas cut + # from the 2020 client. A hand-edited generated file fails the build. + - name: Fetch the pinned Battlecode 2020 sources + run: | + set -euo pipefail + mkdir -p /tmp/engine20 + curl -fsSL \ + "https://github.com/battlecode/battlecode20/archive/${BC20_COMMIT}.tar.gz" \ + | tar xz -C /tmp/engine20 + echo "BC20_DIR=/tmp/engine20/battlecode20-${BC20_COMMIT}" >> "$GITHUB_ENV" + python3 -m pip install --quiet pillow + + - name: The bc20 generated files match the pinned 2020 sources + run: | + set -euo pipefail + python3 tools/gen_year_constants.py --year bc20 --engine "$BC20_DIR" --check + python3 tools/convert_maps_bc20.py --engine "$BC20_DIR" \ + --out data/maps/bc20 --check + python3 tools/build_sprite_atlas_bc20.py --engine "$BC20_DIR" \ + --out data --check + - name: Run tests # Every tests/*.nim runs twice: debug (range/overflow checks, stack # traces) and -d:release (release-only compile errors and codegen bugs @@ -378,6 +407,174 @@ jobs: if-no-files-found: ignore retention-days: 14 + # --------------------------------------------------------------------- + # THE bc20 PARITY ORACLE. JDK 8 + the pinned 2020 engine SOURCES as a + # CI-only differential test of every piece of arithmetic the Nim port could + # get subtly wrong and never notice: the water table (Math.exp/Math.sin + # intrinsics), both pollution coefficients over the whole integer domain, + # Math.round(float), the IDGenerator streams that decide the highest_id + # tiebreak, java.util.Random, the OVERFLOWING cow seed, Transaction's + # comparator, and the RobotType table. + # + # WHY THIS IS NOT THE WHOLE ENGINE: `engine/build.gradle` depends on + # `net.sf.jsi:jsi:1.1.0-SNAPSHOT`, published only to jcenter (shut down + # 2022) and to the Sonatype OSS SNAPSHOTS repo (expired). Both 404 today and + # `world/ObjectInfo.java` imports `net.sf.jsi` directly, so `:engine:jar` + # cannot be produced from the unmodified upstream build. The last step below + # ATTEMPTS the resolution anyway and reports the failure, so the day the + # artifact comes back the round-loop tier is one step away. docs/PARITY.md + # section bc20 records exactly what each tier proves. + # + # There is no JDK in any image stage -- only here. + # --------------------------------------------------------------------- + parity-oracle-bc20: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v5 + + - name: Cache nimby toolchain and nimcache + uses: actions/cache@v4 + with: + path: | + ~/.nimby + ~/.cache/nim + key: nimby-${{ runner.os }}-${{ env.NIMBY_VERSION }}-${{ env.NIM_VERSION }}-${{ hashFiles('nimby.lock') }} + restore-keys: | + nimby-${{ runner.os }}-${{ env.NIMBY_VERSION }}-${{ env.NIM_VERSION }}- + + - name: Install nimby and Nim + run: | + set -euo pipefail + mkdir -p "$HOME/.local/bin" + curl -fsSL -o "$HOME/.local/bin/nimby" \ + "https://github.com/treeform/nimby/releases/download/${NIMBY_VERSION}/nimby-Linux-X64" + chmod +x "$HOME/.local/bin/nimby" + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + echo "$HOME/.nimby/nim/bin" >> "$GITHUB_PATH" + "$HOME/.local/bin/nimby" use "$NIM_VERSION" + + - name: Sync Nim dependencies + run: nimby --global sync nimby.lock + + - name: Regenerate nim.cfg from the synced package tree + run: | + set -euo pipefail + rm -f nim.cfg + for pkg in "$HOME"/.nimby/pkgs/*; do + [ -e "$pkg" ] || continue + if [ -d "$pkg/src" ]; then + echo "--path:\"$pkg/src\"" >> nim.cfg + else + echo "--path:\"$pkg\"" >> nim.cfg + fi + done + echo '--path:"src"' >> nim.cfg + + # JDK 8: `engine/build.gradle` says `sourceCompatibility = 1.8` and the + # 2020 sources are Java 8. The generated water table was produced under + # a JDK and is byte-diffed against a JDK here, so the version is pinned. + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "8" + + - name: Fetch the pinned Battlecode 2020 sources + run: | + set -euo pipefail + mkdir -p /tmp/engine20 /tmp/oracle/classes + curl -fsSL \ + "https://github.com/battlecode/battlecode20/archive/${BC20_COMMIT}.tar.gz" \ + | tar xz -C /tmp/engine20 + echo "BC20_DIR=/tmp/engine20/battlecode20-${BC20_COMMIT}" >> "$GITHUB_ENV" + + - name: Compile the dependency-free engine sources and the oracle + run: | + set -euo pipefail + S="$BC20_DIR/engine/src/main" + javac -d /tmp/oracle/classes \ + "$S/battlecode/common/GameConstants.java" \ + "$S/battlecode/common/RobotType.java" \ + "$S/battlecode/common/Transaction.java" \ + "$S/battlecode/common/Direction.java" \ + "$S/battlecode/common/Team.java" \ + "$S/battlecode/world/IDGenerator.java" \ + tools/oracle/Bc20Oracle.java + + - name: Tier A (BLOCKING) -- the Java vectors and the Nim vectors agree + run: | + set -euo pipefail + java -cp /tmp/oracle/classes Bc20Oracle > /tmp/oracle/java.vectors + nim c --hints:off -d:release --path:src \ + -o:/tmp/oracle/parity_trace_bc20 tools/parity_trace_bc20.nim + /tmp/oracle/parity_trace_bc20 > /tmp/oracle/nim.vectors + echo "vector lines: $(wc -l < /tmp/oracle/java.vectors)" + if diff -u /tmp/oracle/java.vectors /tmp/oracle/nim.vectors \ + > /tmp/oracle/vectors.diff; then + echo "TIER A: the Nim port is bit-exact with the 2020 engine sources" + else + echo "::error::TIER A: the Nim port diverged from the 2020 engine" + head -60 /tmp/oracle/vectors.diff + exit 1 + fi + + - name: The water-table step (BLOCKING) -- regenerate and byte-diff + run: | + set -euo pipefail + mkdir -p /tmp/oracle/wl + javac -d /tmp/oracle/wl tools/JavaWaterLevels.java + java -cp /tmp/oracle/wl JavaWaterLevels > /tmp/oracle/water_levels.json + if diff -u data/bc20/water_levels.json /tmp/oracle/water_levels.json; then + echo "the committed water table is what this JDK generates" + else + echo "::error::data/bc20/water_levels.json is not what the JDK emits" + exit 1 + fi + + - name: The engine-from-source attempt (reported, not blocking) + continue-on-error: true + run: | + set -uo pipefail + { + echo "## bc20 parity -- the engine-from-source tier" + echo "" + } >> "$GITHUB_STEP_SUMMARY" + cd "$BC20_DIR" + # `settings.gradle` includes a submodule that is not in the archive. + sed -i '/internal-test-bots/d' settings.gradle || true + if timeout 900 ./gradlew --no-daemon :engine:jar > /tmp/oracle/gradle.log 2>&1; then + echo "the 2020 engine BUILT -- the round-loop tier can be promoted" \ + >> "$GITHUB_STEP_SUMMARY" + else + { + echo "the 2020 engine did NOT build. The blocking reason, from" + echo "the Gradle log:" + echo "" + echo '```' + grep -E "Could not (resolve|find)" /tmp/oracle/gradle.log \ + | sort -u | head -8 + echo '```' + echo "" + echo "\`net.sf.jsi:jsi:1.1.0-SNAPSHOT\` was published only to" + echo "jcenter (shut down 2022) and to the Sonatype OSS SNAPSHOTS" + echo "repository (expired). Tiers A and the water table above are" + echo "BLOCKING and cover every piece of arithmetic the port could" + echo "get wrong; the round-loop tier waits on that artifact." + } >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload the parity vectors + if: always() + uses: actions/upload-artifact@v4 + with: + name: parity-vectors-bc20 + path: | + /tmp/oracle/*.vectors + /tmp/oracle/vectors.diff + /tmp/oracle/gradle.log + if-no-files-found: ignore + retention-days: 14 + # --------------------------------------------------------------------- # Packaging smoke: the production image builds, and one real episode runs # end to end in raw docker with the certification fixture's seat mix. @@ -411,14 +608,52 @@ jobs: - name: Install the contract probe's client libraries run: python3 -m pip install --quiet websockets httpx jsonschema - - name: Raw-Docker episode smoke + - name: Raw-Docker episode smoke (bc26, the certification fixture) # SMOKE_SLUG is passed explicitly so the workflow's SLUG is the single # source for the game/player entrypoint names; the script's own # scaffold-substituted default is only a fallback. env: SMOKE_SLUG: ${{ env.SLUG }} + SMOKE_EXPECT_YEAR: bc26 run: ./tools/ci/docker_smoke.sh "${IMAGE}:ci" + # The SECOND episode: the bc20 year module, end to end, in the + # production image. Certification stays on bc26 (the design note's + # §Out of scope), so this is the only thing that proves a bc20 episode + # runs in a container at all -- and its replay is what the wasm-viewer + # job loads in a real browser. + # + # seed 4 draws `Hourglass` from the small pool, which plays the whole + # 300 rounds: ~12.5 s at 24 fps, so the recording outlasts the viewer + # smoke's 10 s soak (the ecos 2026-08-23 scar). + - name: Raw-Docker episode smoke (bc20, scripted vs scripted) + env: + SMOKE_SLUG: ${{ env.SLUG }} + SMOKE_EXPECT_YEAR: bc20 + SMOKE_PLAYER_IDS: bowl-of-chowder,examplefuncsplayer + SMOKE_CONFIG_OVERRIDE: >- + {"year":"bc20","pool":"small","seed":4,"gamesPerMatch":1, + "maxRounds":300,"perGameBudgetSeconds":40,"matchBudgetSeconds":45, + "connectTimeoutMs":15000} + SMOKE_REPLAY_OUT: ${{ github.workspace }}/dist/smoke/replay-bc20.json + # The certification contract probe ran against the bc26 episode a + # moment ago on the same image; running it twice buys nothing and + # doubles the flake surface. + SMOKE_CONTRACT_PROBE: "0" + run: ./tools/ci/docker_smoke.sh "${IMAGE}:ci" + + - name: Both smoke replays are present and are different years + run: | + set -euo pipefail + for f in dist/smoke/replay.json dist/smoke/replay-bc20.json; do + test -s "$f" || { echo "::error::$f is missing"; exit 1; } + done + y26="$(jq -r .year dist/smoke/replay.json)" + y20="$(jq -r .year dist/smoke/replay-bc20.json)" + echo "replay.json year=$y26 replay-bc20.json year=$y20" + test "$y26" = "bc26" || { echo "::error::replay.json is not bc26"; exit 1; } + test "$y20" = "bc20" || { echo "::error::replay-bc20.json is not bc20"; exit 1; } + # The replay this episode produced is the ONLY replay in CI that is # known to be a real, current-format episode of this game. wasm-viewer # downloads it and loads it in a browser, so the viewer is tested @@ -525,18 +760,22 @@ jobs: npm install --no-save playwright@1.55.0 npx --yes playwright@1.55.0 install --with-deps chromium - - name: Load the bundle in a real browser + - name: Load the bundle in a real browser (BOTH years' replays) run: | set -euo pipefail # A for-loop, not `ls … | head -1`: under `set -o pipefail` an unmatched # glob makes ls exit 2 and kills the step before the harness ever runs # (cogame-raid 2026-08-23, run 32623414696). - replay="" - for candidate in dist/smoke/*.replay dist/smoke/replay.json; do - if [ -f "${candidate}" ]; then replay="${candidate}"; break; fi + replays="" + for candidate in dist/smoke/replay.json dist/smoke/replay-bc20.json; do + if [ -f "${candidate}" ]; then replays="${replays} ${candidate}"; fi done - test -n "${replay}" || { + test -n "${replays}" || { echo "::error::docker-smoke uploaded no replay to dist/smoke"; ls -la dist/smoke || true; exit 1; } + # The bundle is EXECUTED once per replay: one year rendering is no + # evidence at all about the other, and the bc20 game block is a + # different set of elements under different CSS. + for replay in ${replays}; do echo "loading ${replay} in dist/static-replay-viewer" # --strict-text-bounds is DELIBERATELY DROPPED here. The board is # pannable and zoomable (#viewpanel is kept, because a 60x60 board @@ -584,28 +823,20 @@ jobs: echo "::error::#endcard is displayed but carries no clan line: ${card}" exit 1 fi - # THE BOARD IS THE PICTURE. The featured match on softmax.com showed - # the doctrine card lying over the rats in two live screenshots a - # minute apart (r2-D3): a panel that opens over the board and never - # closes makes a working replay look like a dead one. The harness - # measures the largest visible absolute/fixed element covering the - # board canvas; after the soak -- playback advancing, t > 0 -- no - # panel may hold more than half of it. The endcard is exempt only - # where it belongs, at the 100 % seek, which is checked above. - obscured_id="$(jq -r '.soak.obscured.id // .obscured.id // "none"' viewer-smoke.json)" - obscured_pct="$(jq -r '.soak.obscured.pct // .obscured.pct // 0' viewer-smoke.json)" - echo "largest overlay over the board after the soak: ${obscured_id} ${obscured_pct}%" - if [ "${obscured_pct}" -gt 50 ]; then - echo "::error::'${obscured_id}' covers ${obscured_pct}% of the board" - echo "::error::while playback is advancing. The rats are the picture;" - echo "::error::a panel that opens over them must dismiss itself." - exit 1 - fi + mv viewer-smoke.json "viewer-smoke-$(basename "${replay}" .json).json" + mv viewer-smoke.png "viewer-smoke-$(basename "${replay}" .json).png" + done # The wasm module itself, under node: wasm32-only failures (int overflow - # traps, address-space exhaustion) are invisible to the native tests. - - name: Smoke the emitted wasm module under node - run: node tools/wasm_replay_smoke.cjs dist/static-replay-viewer "${{ github.workspace }}/dist/smoke/replay.json" + # traps, address-space exhaustion) are invisible to the native tests -- + # and a brand-new year module is exactly where one would live. + - name: Smoke the emitted wasm module under node (both years) + run: | + set -euo pipefail + node tools/wasm_replay_smoke.cjs dist/static-replay-viewer \ + "${{ github.workspace }}/dist/smoke/replay.json" + node tools/wasm_replay_smoke.cjs dist/static-replay-viewer \ + "${{ github.workspace }}/dist/smoke/replay-bc20.json" # The text the CI replay never carries. Every replay CI produces is # SCRIPTED, so its notes and motto are the short strings baselines.nim @@ -654,8 +885,8 @@ jobs: with: name: viewer-smoke path: | - viewer-smoke.png - viewer-smoke.json + viewer-smoke*.png + viewer-smoke*.json dist/fixture/viewer-smoke.png dist/fixture/viewer-smoke.json if-no-files-found: warn diff --git a/client/replay_broadcast.html b/client/replay_broadcast.html index b3cc943..7363d76 100644 --- a/client/replay_broadcast.html +++ b/client/replay_broadcast.html @@ -2639,6 +2639,127 @@ #scorebug .plate-sub { display: none; } #bars .barset { min-width: 44px; } } + +/* ======================================================================== + BC20 additions to the inherited cogame-battlecode chrome + ------------------------------------------------------------------------ + Everything above this banner is the page as it shipped for `bc26`. Nothing + is removed and no existing id is reused for a different purpose: every id + below is new and `#bc20-` prefixed, and the two blocks are switched by ONE + attribute — `document.documentElement.dataset.year`, set from the replay + header — plus the rules right here. `#viewpanel` is KEPT: a 48x48 board + renders 768 px wide against a 360 px featured-match frame. + ======================================================================== */ +html[data-year="bc20"] #coopchip, +html[data-year="bc20"] #bars, +html[data-year="bc20"] #econ, +html[data-year="bc20"] #doctrines { display: none !important; } + +html:not([data-year="bc20"]) #bc20-flood, +html:not([data-year="bc20"]) #bc20-soup, +html:not([data-year="bc20"]) #bc20-units, +html:not([data-year="bc20"]) #bc20-doctrines, +html:not([data-year="bc20"]) #bc20-doctrines-toggle, +html:not([data-year="bc20"]) #bc20-chain { display: none !important; } + +#bc20-flood { + position: absolute; top: 6px; left: 50%; transform: translateX(-50%); + display: flex; gap: 10px; align-items: center; + max-width: calc(100% - 16px); box-sizing: border-box; overflow: hidden; + padding: 2px 10px; border-radius: 999px; font-size: 11px; + letter-spacing: .06em; background: rgba(11,7,4,.72); color: #7fb2e8; + border: 1px solid #7fb2e8; z-index: 6; white-space: nowrap; +} +#bc20-flood > span { flex: 0 1 auto; min-width: 0; overflow: hidden; } +#bc20-flood.rising { color: #e0523a; border-color: #e0523a; } +#bc20-flood .gauge { + width: 64px; height: 4px; border-radius: 2px; + background: rgba(242,232,216,.14); +} +#bc20-flood .gauge > i { + display: block; height: 100%; border-radius: 2px; background: #2f6fb0; +} + +/* Both readouts sit ABOVE the reserved transport band -- nothing this block + adds is ever overlaid inside it. */ +#bc20-soup { + position: absolute; right: 8px; bottom: calc(var(--band, 0px) + 8px); + display: grid; grid-template-columns: auto auto auto; gap: 1px 8px; + font-size: 10px; color: #cbbfae; background: rgba(11,7,4,.6); + padding: 5px 8px; border-radius: 6px; z-index: 5; pointer-events: none; +} +#bc20-soup b { color: #f2e8d8; font-weight: 600; } + +#bc20-units { + position: absolute; right: 8px; bottom: calc(var(--band, 0px) + 74px); + font-size: 10px; color: #cbbfae; background: rgba(11,7,4,.6); + padding: 5px 8px; border-radius: 6px; z-index: 5; pointer-events: none; + font-variant-numeric: tabular-nums; +} +#bc20-units b { color: #f2e8d8; font-weight: 600; } +#bc20-units .hqdirt { color: #e0523a; } + +/* D3, and THE BOARD IS THE PICTURE. Both doctrines are worth reading before + the flood starts and worth nothing at all if they are still lying over the + landscapers a minute later. Same discipline as the bc26 panel: it opens + with the replay, it CLOSES ITSELF the moment playback advances (or after + six seconds for a viewer who never presses play), a close control, Escape + and a re-open chip drive it by hand, and its body is capped and scrolls so + even opened over a 360 px frame it can never own the board. It sits above + the board area -- never inside var(--band). */ +#bc20-doctrines { + position: absolute; left: 8px; bottom: calc(var(--band, 0px) + 8px); + max-width: 46%; font-size: 10px; color: #cbbfae; + background: rgba(11,7,4,.72); padding: 5px 22px 5px 8px; + border-radius: 6px; z-index: 7; +} +#bc20-doctrines.dismissed { display: none !important; } +#bc20-doctrines-body { + max-height: calc(30vh - var(--band, 0px)); overflow-y: auto; +} +#bc20-doctrines .dline { margin-bottom: 3px; } +#bc20-doctrines .dname { color: #f2e8d8; font-weight: 600; } +#bc20-doctrines .dfall { color: #e0523a; } +#bc20-doctrines-close { + position: absolute; top: 2px; right: 4px; width: 16px; height: 16px; + line-height: 14px; padding: 0; border: 0; border-radius: 4px; cursor: pointer; + background: rgba(242,232,216,.14); color: #f2e8d8; font-size: 12px; +} +#bc20-doctrines-close:hover { background: rgba(242,232,216,.3); } +#bc20-doctrines-toggle { + font-size: 10px; padding: 1px 8px; border-radius: 999px; cursor: pointer; + border: 1px solid rgba(242,232,216,.3); color: #cbbfae; + background: rgba(11,7,4,.6); margin-top: 3px; +} +#bc20-doctrines-toggle[hidden] { display: none !important; } + +#bc20-chain { margin-top: 6px; font-size: 11px; color: #cbbfae; } +#bc20-chain b { color: #f2e8d8; } +#bc20-chain .msg { opacity: .8; } + +/* CSS for EVERY beat kind this year emits. A kind with no rule is an + invisible marker. */ +.beat-marker.flood { background: #2f6fb0; } +.beat-marker.build { background: #8fbf6a; } +.beat-marker.wall { background: #c9a227; } +.beat-marker.rush { background: #e0523a; width: 3px; } +.beat-marker.drop { background: #7fb2e8; } +.beat-marker.bury { background: #e0523a; width: 3px; } +.beat-marker.drown { background: #2f6fb0; width: 3px; } + +/* At the 360 px featured-match width the flood chip keeps the two numbers a + spectator needs -- the water level and how much of the map is under it -- + and drops the per-clan HQ elevations, which the #bc20-units panel repeats + as the HQ dirt load anyway. A chip that keeps everything is a chip that + runs off both ends of the frame. */ +@media (max-width: 760px) { + #bc20-soup, #bc20-units, #bc20-doctrines { font-size: 9px; } + #bc20-units { bottom: calc(var(--band, 0px) + 66px); } + #bc20-flood { gap: 5px; font-size: 10px; } + #bc20-flood .gauge { width: 34px; } + #bc20-flood .lbl, #bc20-flood .hqelev { display: none; } + #bc20-doctrines { max-width: 62%; } +} @@ -2673,6 +2794,9 @@
0:00
▸▸
In the locker room
+ + @@ -2724,6 +2848,17 @@
+ +
+
+
+
+ +
+
+
@@ -2762,6 +2897,9 @@
+ +
@@ -2775,6 +2913,305 @@ + + +