From ed156838e6152616094286f7f0fe3334ab8772a3 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Tue, 18 Aug 2020 09:07:06 +0300 Subject: [PATCH] examples: add 2048 game --- examples/2048/.gitignore | 2 + examples/2048/LICENSE | 21 ++ examples/2048/README.md | 20 ++ examples/2048/assets/1.png | Bin 0 -> 8548 bytes examples/2048/assets/1024.png | Bin 0 -> 2158 bytes examples/2048/assets/128.png | Bin 0 -> 2290 bytes examples/2048/assets/16.png | Bin 0 -> 1664 bytes examples/2048/assets/2.png | Bin 0 -> 1534 bytes examples/2048/assets/2048.png | Bin 0 -> 2686 bytes examples/2048/assets/256.png | Bin 0 -> 2675 bytes examples/2048/assets/32.png | Bin 0 -> 1961 bytes examples/2048/assets/4.png | Bin 0 -> 1218 bytes examples/2048/assets/512.png | Bin 0 -> 1891 bytes examples/2048/assets/64.png | Bin 0 -> 1898 bytes examples/2048/assets/8.png | Bin 0 -> 1534 bytes examples/2048/assets/victory.png | Bin 0 -> 9827 bytes examples/2048/main.v | 468 +++++++++++++++++++++++++++++++ examples/2048/v.mod | 7 + 18 files changed, 518 insertions(+) create mode 100644 examples/2048/.gitignore create mode 100644 examples/2048/LICENSE create mode 100644 examples/2048/README.md create mode 100644 examples/2048/assets/1.png create mode 100644 examples/2048/assets/1024.png create mode 100644 examples/2048/assets/128.png create mode 100644 examples/2048/assets/16.png create mode 100644 examples/2048/assets/2.png create mode 100644 examples/2048/assets/2048.png create mode 100644 examples/2048/assets/256.png create mode 100644 examples/2048/assets/32.png create mode 100644 examples/2048/assets/4.png create mode 100644 examples/2048/assets/512.png create mode 100644 examples/2048/assets/64.png create mode 100644 examples/2048/assets/8.png create mode 100644 examples/2048/assets/victory.png create mode 100644 examples/2048/main.v create mode 100644 examples/2048/v.mod diff --git a/examples/2048/.gitignore b/examples/2048/.gitignore new file mode 100644 index 000000000..e324642a4 --- /dev/null +++ b/examples/2048/.gitignore @@ -0,0 +1,2 @@ +2048 +main diff --git a/examples/2048/LICENSE b/examples/2048/LICENSE new file mode 100644 index 000000000..7897d4df1 --- /dev/null +++ b/examples/2048/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Delyan Angelov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/2048/README.md b/examples/2048/README.md new file mode 100644 index 000000000..d5371def9 --- /dev/null +++ b/examples/2048/README.md @@ -0,0 +1,20 @@ +# V 2048 + +This is a simple [2048 game](https://play2048.co/), written in [the V programming language](https://vlang.io/). +![2048 Game Screenshot](https://url4e.com/gyazo/images/1ad829cf.png) + +## Description: +Merge tiles by moving them. +After each move, a new random tile is added (2 or 4). +The goal of the game is to create a tile with a value of 2048. + +## Keys: +Escape - exit the game +Backspace - undo last move +n - restart the game + +UP,LEFT,DOWN,RIGHT or W,A,S,D - move the tiles + +## Running instructions: +Compile & run the game with `./v run examples/2048` + diff --git a/examples/2048/assets/1.png b/examples/2048/assets/1.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cd6d7497204e529630ff46ee80e8ff466fecc5 GIT binary patch literal 8548 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 Vzk@?xe||tvlvR_dmij07zX5v9<)Q!p literal 0 HcmV?d00001 diff --git a/examples/2048/assets/1024.png b/examples/2048/assets/1024.png new file mode 100644 index 0000000000000000000000000000000000000000..2b8da8d5b2234f069959614d13c9ad301da4695f GIT binary patch literal 2158 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 K(XNE(2mS|IQr^M< literal 0 HcmV?d00001 diff --git a/examples/2048/assets/2.png b/examples/2048/assets/2.png new file mode 100644 index 0000000000000000000000000000000000000000..e1de4367f03c0e3da526bc32b795f077517d45bb GIT binary patch literal 1534 zcmbW1c`(~)7{`B+NaDy+Vk_8Um0Hn~P;|GBC5Sez#uZ1KsB=l9#8p?=?zXHd)zZdM zU6f^uYU?N^rS5ZS-I9nSv+9VWSVwI-o$3F(^UU)+-}yX$yffcAj{LrZ1-~IDIbzJ) z=|o41taJ^H_y7nhMZva6l@#Sc=Pr1NNFt#r&-F&Cha62ePqldVJXS8+-cR5#W3FVH z46K^)&+ysH?wc8ISS$n%#1}|vil%_1c!vx6gHV02BROY4k+32_+ZvNP};88ecmf$gt`cTg~cud~qhjf%cBIsQ!;$B9#@=))yXU z&f+5s7Lcs^^}=&a8}>oeasmjET3wVDQ2m~r7N z5v~lawpODuK->R@PMSe9K@EYN^r?-!7rP+qu5~F z<7EpWg?gIdXP8vuO*W7*6M>7V9A~5h=Kn)zYNzh?a4RY{iY^2W{e7zc;Q-*^9IouRF zdw5K#$9wj12kH4(rN$r*`#`cMyUC(;#8`&qljYoGX?=F4o8#B3#T}WvLjKF!bhx_% z6IvwIQyoAvZOy`~(Hck6x2O91!-6Xgll)yp--40uQi9IHXVHFbBJHVt)9+TN55RKI zM&}Nissk^NkJnu!nmr2PdlAPX3(^cZUhQ@7t>?Xl`sxm>Tq?Ub`ZY>t%bd+rGwdQ~ z){q~fV<$6+guERmHs+}hPOgB=%!xm)%w2R;&-Rr0NsjA1VV~O-6FYP_NS%>0{&9xg z*l`r%R|*S3f3pTLw2#8_-J4507di?*-1R);B*&r(jn0o+T5eP{e)r;D1)*t8L`cfA zXuQnZIiYD{Cm&b@bSaItv&IgQ`S@I4(!5nfFef{VAb3P_8g=oWtbv5cu8k>cU#ntH z-{SCJt(OO(QM#;YRjeH&a~bnBdrzDbw@2*9l#fD4Yg6WG$KaTAjMf`=*o2kea*gt1 z`YtKQGxvVhyA`e9D-Zh0K$#NXc7C6|=3m(XL%d9qsxcktCCT0@yJ-clLQk}SB`(fS z)zd3GDis_FttC2jAJ=r$vJ(*=+aVld9X{dXj##14RCecFgFAzu(YmlZAZEOOtsfJ) zb7EsWI|vnSoBT&PW!ny?=`;0ZJU_o@hD2+(|dsM z^STsRVKBccoZx$IhBmrktCclfV?Av%2NWBN_bdjUaHT`sF`*|*@|1?l0Xq^LFEXlO zI_R|<<_yHL$!c5}6REd-H;%PBxApCMlZs(hYB1oU6)(yWJn0S>+@ayq)YUQ>TK+48 zevXuMjfPs{=(D+P4~7Izp0^46e9zR|eD#`ZGhS87iq@P_jLn|Yso16F*i^03g=ZPC Mv9!Zgn3I$K1>wH19smFU literal 0 HcmV?d00001 diff --git a/examples/2048/assets/2048.png b/examples/2048/assets/2048.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc1469cee115dc8f398f93558f931c8faa946c2 GIT binary patch literal 2686 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 BaaaHV literal 0 HcmV?d00001 diff --git a/examples/2048/assets/4.png b/examples/2048/assets/4.png new file mode 100644 index 0000000000000000000000000000000000000000..faa6476104781adf2512d0c12377b2a0ba5426cf GIT binary patch literal 1218 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmPV2QM!>&sh`A=|CaL64!_l=ltB<)VvY~=c3falGGH1 z^30M91$R&1fbd2>aRvq!El(H6kcwMx=lW)ZgiEyDFE%uusn%n^B|tn@J{6g|3u` z{F5$}oSAF?ykz2RBV+S(5*u19pI4Sy&Xk<-zV@BveM{prrd4igi!P+8N_vKRih7oM zhHbd>#na2%>uBW}FR61+&Th8Z{<@hfFyr#1G>+Zr29v^$i@$uPr&RJM-H_|8$=5fT z*E~*@PB>umc9r=guPf&{*&g4l-gr~Nu)vlpH!`Sqm*sW!FW$Fqs5NHp|FpX0{T7y$ z+h!_cY;XKkhQ{+v|Yu5aH7u>ZuFOmspi+r&3dWnwdO%5LwnSLB~dX}cdmFq0? z=|@}V_5TXqKjM;9Q&m~pqJG$NhpcSg9-_)B`s#tMsHfvxs|xSfAP43b<;oHPZ2KJ< zSjdA7a!IZ4SyzN*t(s>2-{#qaQ{n;#T%~&3?C$D|2D^ec4>a%Y{=Fep=j!}R3x$x}qnY%Xn%Qg1=3(Pxuwuaq*W4o?CEh@ib^Q)z6zV*MIX2`}EllJHQ z>0KpdMuv4yo~*c>{)a>1<-se;wOgM*Kk}np`TfhU=H?sNOClT8{iUpSy;i@Py>@^5 zzf-%m-YPDUKDk@(z{}+Nm$#lK6}_@IT~(sy`bm$wV7J9mdHy**J@rLDl-J(>qO}sJ zO#Jrb<+VE(mfqU-zE&vz@JaoaJDPr~$AGQ~t6aD0r}a~v-b?q3b?bL;sWu3om8blQ zE86Ji|F2(Tf3*q3b1h{DM@<>yxq^TH-h0=b+pYh;X5IRBhSMiL-&?lx&q6_O23rON zAl$(HL29~nEz5^Jm(w{hQk;n9dn6l;r^Q1qitxFVqW%sD+YCibp`ESxB zkqBTi3JWrjHfMei&-^3)+fj`zm#-Yr2Bz)QPihmEaDDyI=XKdfA&t>~ecj5JDhAU} z&UVatY}4%fa689_3!>iwmD68#8|US-MemSn-KMkBZSNfJ{qdKMEn6M$f16Qk(FG|_ z)mP8XY{^&6&Et;0+sBZ>b2saU*y6{>5AO82$8$vgP-5WwghyxCcD1Zu`ov1G)lAxc zq11cj*WD(f8*E-zM}1EJ9rjth=5TCN{Db&iXMWDu?(dlZELs>mUHx3vIVCg!072pd A6aWAK literal 0 HcmV?d00001 diff --git a/examples/2048/assets/512.png b/examples/2048/assets/512.png new file mode 100644 index 0000000000000000000000000000000000000000..ed25482b394c8f7c8f24734b1c274018239c189b GIT binary patch literal 1891 zcmbtVX*|>m7yb`pFw7(~##kF<-wJHO|g=XpMy^XZ(ENV2tnL8YMp0KhEI6C8Mz z`2#`_UVd%j(a$RoPiqSTaP%YcTA!!#9AWx-w_pGeLHqy+$jnCbOu-OK8=~N>5DX@$ zOLKJnn`cRd5M4v?G%D5SdI*3I^6?1q@y3MvhxlPEENw_GF>py<_kK%)xzmlYU|bU}{WdwB|(CxZF03g`kfCI+pw z&VTN99u^`Tn`CB2baYoBoRZKIfPP!C8fT17tjCeTjPae;=9QzJ<*Mpn#^%27w~mNlPBrm zJRqJ=3~HtZ&r;8 zFM{MhV;58F+LSNozx-O;PLys575)!>4r;sc##cO!X|p$&1l&DSKD=wBSr7$s%yx^O3?X*T~Drr%N5c3EIV#kiMjun1#o%&W0 zswgbyUf8>yxvBttj|hstlWzq-_ky2PfXeG~W*!fazoXt5nReorr5u)on-6-z_G0cU zFiBj^?6v{Q5$~?OG%8mXnhfN#Wg;DDk#uGK1~w`n_vnt)iXsK>L}nS4yly-|?g*7{ z4|FUJzj~BbIxLeNEW+t)8Bb!qr|iKP!J1zdWBu$RR4+kOm<#2y7?sejVh}={kEwz5 zBQ<|4DJ?1+Ipfr~fH^BUc$^$|L(q0HJ+QCkj#@}V9>&5?jZ}pl{aI9`W<6t0K9NGi zN9RwnT;O3J-Z&6`DXvG(D3r% z4el>g*%8p8bs?p*f5fD>@XK;TK%IBU8s1lCxHih`gQi4FN~o!T-vfN2(ZBB27ZQ8) ze&Pm}7FO*d^!sK-jI;QH#hmt$7%9^w*p2P6NtWp0qd8-)R+NVSllj(S*J#ovVwMUmIn{w*1T$@yJYRKlm2I8CA5cITQt5a z4Ria4Ot?0_GIQrlI>Hl;`k%I|lRWwhod38EnmTqvc(Zn0PG`R%KIw0_W8}78BgCZS zk@j-j6q!ImjrmIL*S1N07SFiGSffhjX_}IRVq9)HYWII!lpHeRUS(;j!KmgHf$IKG zaz^dQk7+lp!uX2^jLbH=Q}~wlG{hION>p^nH@U{~-h1QhD)#T`jrT}%jY`EMeZ8HL z9i3dax1ufz@WP21JOL2gK#jK@XcqHv{^V@jjHvZEAZP)0V*=4GCa|W2x^~L9b89x zxw8X>5^QM?R?B*44VA~ks3Ps5)?DNWU2DshP+BHdUv`F$H+StJ1xG4yDi1C0h8jRz zJW{$Jl{(|+uUtvp%J>9%>}|dzu4zBtAA+euE48jkzo?pjPWckg7m_*?p!P6Nvw5AjTP*X3(r+^kVxGiTM8l~M;`LJ+4tEx0BnzM*!;kW`gT;YprEq4IFi`Z}=iy0WC+%{d zp-wQk0d4`B-#*L&=Ya=BOj6A@j$HJEk#yTtnhRjX&StAhFcr6sI>N00tM~13Q|cf! e+>z)$>=1H`gqC;bdc5QPKERS_OL&PR$NmE&y*s}E literal 0 HcmV?d00001 diff --git a/examples/2048/assets/64.png b/examples/2048/assets/64.png new file mode 100644 index 0000000000000000000000000000000000000000..28d5d4af3668710be17cc592ec2575ce1e84826d GIT binary patch literal 1898 zcmb_d`#;nBAOD!lIHs@b7MA5M6zPlPeqUIlg@ZDpGs-x~Wac)dEDpJwF;21$$u*>D zh;ELJLd<1Sgxv3Pa@PNF9+o-^HLEq2dbS002NyPIm4BO#B%T zupoC?_`eeXIKbJ_4mkK}3|3{fKoJXf`ZW>&ev$bZLO@Y5Tp)@@p^q*@lIN1`(nF+lV0lsGvY3F*pj3bVRvgJd-40g6;z-J8Nvr=pvnj!Cq8) z^@8YU)~Ob^f?0o@#k^afkXCg2mYlGLwrvP_9#TO`=P0cjWPHUJebfqhkE|GhdmHAZ zWi!>$VqP}MDdK5x8%PV=;(ON{DdKWH|1T2pMd^XbYzc|8v>eHqZYIKnJR;_;C{lK( zQtVpAhLkk*SNiml+5EH3hd{#1V=54d_DE->{!X7>(!H?MNY#}k;*y&cZiVgxLJRd- zhzM$e2?zt&2th=UvWfozxiem#x%UfKaf2H|V+=3gQlw5uF5}}0YBhx^o%f3? zNB^j&b?-!RKue$!HB-90j*%;AzGHZ<(W~i(DJ(oV#KQh{!e)4J3;J1;l9!*(qRk$^kiBBZMq#x6y^AcMg zq2wutffL6)B@iZ(JkL`61!V==2d7c#EA9j;LPP}b-}ledOW4fzM@~#Y^v1l0daSH? z5N&UDccP+Ym(z4L=yml4= za^T;hp_u3fuSUY`uud;{QfZ{s(O`WV>AUy+`QWfce5`VPy`jrn&l67BE*XAnAne6_ z-Zx+3VAGD4wya$4u(#RP(Bi4;NrT}B`}Eov2`GFa{3g&QXBEcfZN#O!hE(&8ivHP; zUoEMl3~I1iufGp`N3p>bORl9XR*Y#OtN)W=D< zdjxehM-uy^E$J@CKJgXRfOas|_3iXSjWRz@KA-vVF`%bT2vCvl7$59h=f z@AULbCDEg6C8)xLQvZUjXceU+ZFG?7&E3L4{?V5M$-3cec>gm+IrVZ?c9&pt_J2A+ z@!pdMv77X(3A}}+4uZ(n8QyR?fMD&v{4&Sw)*#>USvyDL-Tr$cc&nKeZxYyi!Rp23 zBqUnbip%!Y8=8EYsmZK<*8ebz0{SRL*{77bx26r1Hm_tlq2u%L^n_BR!zJl9pcOPF z6JWpa-q6Tu+$+qzhQPFqb*RU;8JtWtoIi{kK7Z)qAQl-AeZGHBRK^+z-`rED-bEhX z-g@oC5{DV8zU!*mdaAo|^z-X{xiJy5;|McyroQ>e^<*$`!jF}>TczPmZr^P%Dun)+ zhGb$bPS<~T;eW1;xuBz0njZa0#&aAxP$% z&qgHV2L;_lr3dwpoQ)PHmdU^}OGY;lQ>*$vY#^|t6wv>~t+aqL;6Eg7HUOY~;H4IP z_Lv(1V!Z literal 0 HcmV?d00001 diff --git a/examples/2048/assets/8.png b/examples/2048/assets/8.png new file mode 100644 index 0000000000000000000000000000000000000000..6e69a1e954719907ef49d7d8644bec7b7ed42d38 GIT binary patch literal 1534 zcmb_c`!~}K9R6CvnnXi$OUtbd&HZ|qX=YZ@Xs@U+jHH>BVefJox#s0Kbdx2Q*ZoC} zMY+xWQu20^x4ErCH;1Uin#((X!~34kc|Ol`o^w7wJ?Hskkvs{CP)#TR0E$F+S8pkE zb^(!-?p{pLGbzahpCq^fJG)cTQB@#y_A=c4q5(ibWfwBQJ+8XclxGq>@bdEzMMZgK zp>@m~sinch`!aD+k&&TvCV;yd8pI5xBH}JF!x03c2kC6`emDTgh7ny|$gIgF?s*b9 zMtiX9BHR}){A0ml8bqZgm%8YaOmd(k6zEUl@bIPl&lQEIEchg>J1Sj+r^7ZHI6|&1 zq~jbk?}lSs5iLxBxcsTF-U};KnA$T;UKX}GF^L{=h(2ycaihV{^G!3p zo^#RfwXv-?D_R5_1_*T_r#g8C%6D0z*$6Z7$Zag;+pS~CH)cv{;MuJa9a*qppb*A@ zl76WQ+5&|Pa0!qKQifuYK5k(}v= z=jzovVpK-qwO(@=`@VZ86Np0w=NA+DUad0LzNI4| zyk#w6k^zmz1goWqtI@`3{j}n(>1rRn#E*4Db}V&=4lUU{U0j0G&`HleO4L~U6l486 zA#uT;

ktOU6Hl%S|gykm7X%cG5M?=Wcqkc`&%q$V<`eV`StSpI`PNTSXP?4dQN2 z(;XwnFxrv|jR*c;dkOqw?jlL<%;IIwThVZY0o`gMd%!#HYkw{`rLZf8ZA+zo3>pdU zZW+80Iq^BXdJcl9vE~fxYfd#US{E(sZKO{6pCJK-CF?)cQ0rb70*8EnKwM@xja4EF z6KA^H0~=|nUI6MqQ9fF;&SAS|^xI+lhzunm;;D&*_*mX0&MkfQSHvE}qlUDBmarwl zg@I>(e_m79I)wMY+jpW-yx;46s(O!lEctf64}u~L8n)vP5CT|qPl7;+R>0-vNk%WF z4q&=b*of397OdZz5Ak2$X#bK?FEaBFtQe}<8a!K< z5<26|D+amI_m}*@9*juZNSuxQ%fk~N_A)~nNhRQ2)>yChWDIltkuw24J%OVyBkdA& zFc#X1RFq)856lbJGanZj^Qx5aZa;&-ZmvCkRlOan=1jg-d70T^r+*Tg{01!%$_95D z1;n}qO-jufe;=cJnp7q_2xAkJ-^k6Q>?CH~yLj;x zG^j$*-{_e&5Ie8aZ2^ZDnddZF1~`z-8e>rcSXo^K(JCy{?_vvmCB(e!(ThqsC_=XK z!A~{6ADbdvaqN2qdr}#2GIcSAA3Jwwt;?v^nTq0cEKO#m;96}l=%?50!~waiw(i}# zi8uCZl8#axVV0KHdGR5+Yi;H>4k@LbI^*|i`npH2d}`I*oZ;iEn(FV{pPMUf4<522^6I8p^#of4MMOX? z8#C(2q@68x>$C{6ee{L|!$Ck8fDCsIj43LS+A1qKENT}Jx53JeTv_4|D=62kixaKdEz-XWOC zNqvWT`|tSGUXt`KL3WbXc7=gK#s2Svg~`Ywco!nN$;e9}?jU{s$U*qw4`J)Oh|o+}O?BjNH@O&5B$~MqWuX2m>Dmh8#xbyQrGi5;WV>D_MQ+Z@@Hd z`*d_5oLo}TlG6NRmHwy7T=VG1$S`wz#(ZTaMwTcoIuy96l&vrN-#FdGJ||)!VTqwr zgxlU8kS6@1Mhe~_9YOacgY&~>v1JGe z&FBLazWQ&}mV@>65cQ+RYbt}+ewI0{mXn&;1fS~`IsVXT_KBisQP`Pe8UF~cDR+BX z6{4Zzs3{fS?`ouBdQDC#{WAG|3v8n-c=!Zt+V@>2cDhw=t`1T`)(bu-?u|FL4<_)@ z>r&^VY^J5dEtdd5FVk3kr=NQoLTxehxOQFv-F|?k;AT%P7&5&wjh0{!p#O3A3%#ae zLtq+s;<@}9*Q2!Fn)R*)J6tnL4u=IH_@N^=_RrEP2cMLROcfqzW{2IdsYEIJgI->H z#O4Xq2fL+rZ~R)3T(_(~8-2R*H}^yLxcZzfbXQ*1!2y>)F*Yfd#7obX5Bq_#ZbQ{M zvio0P8g#mt-}segjT(&En@4?`Z_=%QS)U2# zrlcFxTUk&PX?`lLtT5EaKHJL6qW_plX1a*zmIw}tdbQb=g+vM-T@tYB-CHFN$;T$w zhno9$aOM*CK$*NX8{A+r`;=!adXJ$HK8gCIydXUSGn!0m3{)v?6PzF zCfF2hiec{xBkNJDa2UAbHiR`TOagUw)@o)sP+k@mPT*1#YP)QxhpC;A@m1mFonMP> zDh9UG1^2>Pw}A)_v;I#+tJq)R2wh{z;#Z0^S~BSNL_}6bNtuDY_46&gUDE2Ce!l)@ zmYfw62;~SBgpcgVdfgH;-zwCIKJz3r%I{nNa}eNDgSNg~RXF*it_iW2!kE%_Q_6-x zD{~uTah*?J{c$keXJ5BU{a}wWG~L&5tnz<{$PT8A#H_QvDm7V^Y5)a)EYn!+{g{YfvQGM469eWIl>b~IA+B`UIwBZss{B2P*vXh+ zoPm{_yOy;EKiB`BxJDCU!7(iJWpj$WiJpK3x$Nz3iu}iINXf|KN{jFBB_I0bH8=N2 zT!YI95d>HYEFzV+H%oYKh9wfGBdw4QQDJB$CaV#>@=IpxzBJ-gW#PI2 zl5yr@a@5vEL=NWzcfttiP}x+>Lx?!j^cofzj*;kHCK*aH{1I&)ru%FxdS}ni0altH zdRDFc!aj;i^)!bm%Z-%oWw+aS8)(6%f?Ro*5Kx!2n~z*LS@rErTtzsgbchF0c~pq3 z?m_KFq$%b6Ct#K@m-_XC&dxrG(FgJkmGL$CJ)hR0jivc@4=e`;-mhmqAJQUE?9C^b zX^LOEx+_TKeF=??+C*juZT@J^eAYYpa&7bpEhy!6VkuufoQhUy@#{4#dN!IfBs-OX z&w`WIT{hIp;Ie7agh3GjFac;PZ(rMv+Sw5(kB((Nkp>L)^66-(M_d>QpiUAl<+i&x zbNijmvuYJVt|)IkP-}$j3N%F+$sXn%|F|9L9A$AvHIFC(wBBUwK+A<-?W>C)8zo^?KnNqt*)25r}rgYf$32F2Ja%vHS1}&45%v-hcOhg-7lbp?m$W#OFChs#;y^AlmJ#{t`46xrxVS$kCFT(qtP1N za+0HOv>lHqe|G90k53=Zl2}Y7j<_D}`0~5G8%P9lP~V_(mDqohgViiEAu4*GA-tv* z4vtM-j8yr0tJ9tS=KHm&o~yHy*zTnUetO1b;n>f0!LecTP=`3%;HuxN0L&P41K`n< z4VD2whk5ney6;XpUDHeL-eJ}DaK0|z#HUVfHv?#qJCeJgidd9U@Pq~B+x ziEA<_=2!P5ecR(UP60PL_c6JwxlbsH5=SFkT%e zE?Mx%lc_x=IB2K?r9=X?U)S6*Y#`Qx=Dd9hH|hzM3%G;JtcyY!_A%~wcAE|MINZZ610 zKYTZ%Kv427;6UZbU7K$s7F3;!Ph0_k;eI)m;wDug^bh~2y8Sos^{F^ z#M5h}4aIHbl3dD2xLl}v)0j1bOPy;+w=Z15k?35dVCpPjzb5?t{Fe@}?65s1C)k;h zVNXOq5N*CV2*Y8BWO*=;xBE0dnGz|Wh@79{qwimcei?2M0@~0ZwuYY_;y2KiE-#MM z#s)=2A~v^th}xrzR}M*%R>3ZAOWh9#J%w;jJ(lw|UMH>r2`fC-Z?b{1mQLzE3(GDG z!D*M)RGK4ywy!rY*!Ia(9>GU6*iAs3X5uC^*fx$YaKkzxQR2j^79tAu$QEOgX~dh_ z*-8!anszWXA~=1e$dd7P1?Rs)dd5`OBw{?YQ4oBpC#^!rT*2PDc!_uG<4uE0yVYtg zBSB0P=2q2wLj_$P!-+G1f}tLOX83BZV}C`Xi>ZOZfM`(&3AC4Pj6%(ut<&Azh)$6S zV>^1E$j4df<>yq{*yT1G_P|M;{3z>4;0s zetPg{Efyg2xXeC#k+-gPG;f0#!Ml8zT*aaBk71cI@zB(#Nt9z7G!#o9;Q*_wSs9CE zA-5E01!Bn9j|-n_Nq=Fya+42Es+!SJi+rj!dH(9&uT{A4WkeNCPYAAiu~jf_wvh+o z`eH79=w|s4zdmh7SAaW-ZsCA%aZt*Zzs@yLe{Q)<7W{Bt(*cpE^a%%11`^YWFgxAs z6yVU!xP1+e3?8OfDrETwX9{!mZYJ)z8lcgbPS_+?QbNARk`yA!{IRLbs4AclUXzMb zb%sG!#_ zTY*#?Sf_>K#7=!VP$tw&B@;=B#DbtA^q@BOuc17zyPyddxWm73xgt_+muRrQ7s!I2 zcjT6@HCWexbJjt&6f+rg%fFcKMG3C_OBlqF>dqi8-;5c)$3xBQAau9BBH z;T%!=J@+R+`{WtXDV`6v$>&ygn-58PhaUBVPIT6ydf1I|$fAF>{Xz$g!96oeMWQg$ z+$S>euj&BbT5Xyjob|%}J{3QNTGi{@Z2i~-@yoAH1eCQSEDFuG!=#W7@OrneVwX~& z+4Q>l1P}t0wVHuKXIq&DR`5v)iB)hveei@ft(lX>@~WwoFGKh_%k*ydZ-Si>Yhh(h z*VKHu%sgB575UaxLrTTR z{n9IcpGfB9YihhHT&aIH7nVnuA==L;Kn{$eoEu>|R|?a+e!C2zq!3dSFX)XoZ4D{o z;JDa9gw=0C^<_a3dUyZIhcfCi*Mb3A2l36wxgE_NY@?wsR7|N83fgY%b!oO|HM_+r z2t%%84N0BZGifUIRsQVC+(`G56}?@_=nc-lTjyL5Zt>AKjvMdjkIN;mNNUng$Wm%+ z)N;cmuac!q|5Zy%QaBOPL(zRp>G(;&SYB-Cy_&01+%rMWru~+a-4B zkv)t}a&|Pwi!5>4#^T_OQNK>;v1(?seOdpVw5DI>#%1h#Klarko^;}`9N|YW>b=K^ zu3I$I_G`(Ew?6Gsgo|sg*c{EVW2U7`qEYea$@q|`MWU|05D{ROxSGD zcHQ-5La(~xNKf;4VBWd4`KEGhF$`X&NK8{`X=j{~?62}DwYb*P9baK;sm;hY^(GHP z7b5`wq~7i%?~Zq+%Tj!eh7BL_Fz^=U-<)<^BV!!+tOb$n z34Qi5&-cNAqpkq4X)%!iqv0j(;(kAa%4HT&3ejmb5mt1d}H2 z9SIgQkFeV6oqfk{I7|z^-gxOcUTpJj>edD-8j_c0(#2061$KiBdjvOCEd=D&r(b7S zzG$(1A&RFF3eu5}TNK+<#7J}#426J(3d-UzH%GsMx4)y|xQ7@!tTO(%jIb-zug>S- zq>pgi2$9KQTsP)OEcwk{n3WQE)R0%yQn=F^m)>Cgx>4AP)WzzI znzlLH>hn;JRHd0-e!Ft5|D;F9`c9Y>+I(?*f$L8Cw-G6-)m0qu$v)dVp6(p42<=Jc6=_gLp7h$zNP2dp^hRbvrUSIg6oGD5xdLu5C zU-e|>qk|#)szhc9jX#%M>(R(U9ATtCKzB z>Jz6`b|1MRyr&j@D2{aNV+uuiY5e6YP*H-DO5y?rLxSks=P8~DHEwAtQk=$XhH}}R z=z_Jxo_Nx#DysDojm0=HhVdnS&WcEy5bX@3PebP#M0y|C zft{WL>Ru;7RZft<597#j1L{I6W`ffjRG^O%iEWhQFbun z%WuEik>qlKnd0}5v8}d~u;bP3>()+93~ud2J6hFxt$d1~VQ(@K44W(+O&_$N9uOW& znB0JacFT%br_O4Nk!i#E_kcbcGz_dZ*>JgSaTq(yN_&F(7EWXTXjAZg)jt__>ytf2btp{s8Y&Dvt`kWmet(+*&V0DfBd$etR_jo?1 zhdKjx>W<>O2hvx})sspeNoWgUbv4jyH9Dh_Xi$@vwlsXrH*+fod(cZ7YHp6}wqHk( zfrapn#M7tb^aesBslz*`DIb@0^1;F@t5m6{DCmg4vU*~kyG!=g%rRE_>vyE+uV*3n z-=4$5QSCi6+6sgM`}*RHk2>3*y&Oh2)A(Af1SQN_ugOd$PSRMe9pgM3$Ra& zfn#=;?ggJU{HN@WH3@gs(QOyp4elpKNA}f zozjxmykyoqT&jp^^#%Dke;b83=^^v1oIu@m{pjPT*ybxgb6n@j5pI{uChlbxen$&q zUccPhM6xpFlKQRXPd)91hAT(PX}rSYQb!IcmjtsYvozM8SXe`t&~XgG8vT8#XM39- zva<9>4!>ZY1_x?+YmBaM4Z1y{9(!x0r)F|Ilc7I0vWI7gNOrGxLj2y{xp$J{s7T*{ zKdE%@Gv-k7v2`X*EvAEoemJXr&kxn@fp4=QZ#~s#yGn1}o|Ojc<8-3J$uVPED2 zfLx}{GKi&bVGv2QQ|xroALosDElB1HGtAfi_FHZT8YG|CkE;5GWe6f}t+sVsvSVmO zVel0FHCF-#qb?Q3&phhpSXhI_Km0|{)Q?{*b zR@Iz%HW81beRBg1fuTl|nWQT}_K<(6IDH1%0f}7&3N0k`M<4w_a&C;tMh{Uy*ZUOX zyVmWQF~-RGhtU-4OkR5Suvt|r4r$)IXYIQvRp)u&di*XNkWB$2RnHrXIs zfn_-~9Qs0zlxIPFXZgbHu(Q(0tU+_&Y|*~$)(;aCN%^esbshCxRLcM(RrV}42W!jj z!;<#G=rP|#v%RCKhsvdC+Xb!H!pEQegCOsv;)E&vILRfVk?(nZVFT*$0S!0Ql%r~o zwmz7;BzJ74WliqLdiXAX2bUZ*Y*8$=GNmy(D_RzK14qv)$@x^t%#-Q(BZhd_d4or@ zGom)NoUS#e)r}wD^NI`w+@_vW?S}&xZ2ACo=$XXjXyf~Mt&7#(M&_B~x{+H!fv`C! zkU!q_eUj|TBi<}aQns_9eg7&AX3{^b6t?TFu?ZaKXt;X&xFViEMEJO=ga?%~&LpRm zb@9zGG~xqy4CG(5?_%R3li)W(qvG713c+9_O_XoSC-&I|F0W4kJ_zgWMw_A{;93n8 zO^4l*g8$9;zOObR$2a-&@B|SOSF5G!>>QTEsnuRTEpyD&weZU#uQh7i>Z72{Iu_J? zttN1}{5!yJhrI%yNnWM7BAb=A)Z;2xn`fwXxiM>Kh#Wf%sxAxJoF+YWOgB_Ug3Ytx zz!UmVEb|pMMvboM7jbFq4TK57CT>QQr=gb5PYF%WUvI8&EdNh=`b$vdy^`bd*V@iW zZ#~pJ2(~$~Ug+9Jhar9QY#-*^1f*!IYv>f{CmtW!#&+spx!HpT^#I>|<7UDFT_;F^ zN+*D_i8_2-Oue-2UHh)rgM&e0YAu85f0DqX`m8%vQp_Y zQ|!Y}Tbd`TH8AFlW#Aa@k3R)^fK@V3{l3?<-bUi%Pra2F z-_MOmYM3Ui=Uq7~5SC!8a+OR}B>*ow${2d{9NxK#+!?Qns}i;M&71rVo@Tp+M_UT= z%sSX%0KP=>R}NI9%e5p{TQac->lsIf{4S>~*(-Ym@A_N+nbF031K3+34Ao#R1ZHuz z%=IhF<53$=W{Ri>)%{q|g+-J2=B&~pES%F#%Es&M^hVtYPUe{A z)BG>dTY00rgh4xCdc25o{aOF* zsgxAom_|3r)v7C0ePfYDJ;<(2IMGy~s&Ew}f{UcYxvn#v7y-fQYK9JjG|FTr)1&+Z zAxqfRiF+ylzwg{jAd~D5TQQ>E`e!k*V_L8A8|y`*K$4-t@7PlD6~|Xkogb8+SkPLP z!~(ne=Jmw}4G8<5YAPH&Y-w))Vim_-gxPajnDnt$erX?)HqH`;)cc#mxy~KYcP5~W z>Mj$O%HV*m!Tl#P#_-_Cze+;r{kfytbG0wObMBD#j@huos?>&bw2Q1M$K|PZJt+L- zx!N5rfUMf~(BExYbBK&y-u}21{$DR7C2_68&dtzce(Qi3N>c(LU)hUG(lH%sug)ku z6d>if$<8@Ta8dsvKKyC)!5XRF{G=xchktNNNdNZu;e{p50f6qlD*aF!jB!>rgwyBq z#x{M^;R3kVWFFKlkL1^W^M@Lr8+j6o=OArlJXKa+Z46@0cx=|SHjwBXM%wEP*jxkf z10yffbi&%@S%Jb;>91Sfn|OC6gr?W&mI}`|!}qhZ-C%g#z!SDX@9C;Bi5-px z?|G#v2Q^DqO~7ZnzB4m1i$6GDkgFpcZKBt?k;eJy{NMhcYj0G32G((7cA;v)eFxxDRaf2{eI`;&C=DZCj(ZkAIAx5lq~(%h~E| z$bCzdSg$@y0Q`0@tV$=wTBwB3X@8`p?*BB%_xxA;v#Lqd20W!SA~dpQL~W7HliP)) z1+z>~7Ol(Llvn{`R)2bl^0@Cl)aE@(nKu~d+A`AU*vUo#wP2tF3@2rHu??d3TR(Qx z%2R&SMyA5y3Hv+yzB_1ZW?A_Anh)#y0(pg`Run!tb;y1G&qoUWg1hJY^R!YUl{q7Q zs<*ZeJIZu+qaq1$WyRSimFB@^0payWeI5*wvYutYWE~mpEz}2oj(^a(MRi~P!J+cl zuk=Gm`vvn!X)k|R?XkU-`h@e=KrgCs!UJE;jo1}V16Y|_+yLfmT zd{w5z`7`n^bHOE;MiimS>o}dt;tl&BOGNp5Mq>u(RFeb ziqo@b64EYg^#|R&{BiL=F%f)%hRBD^Vq=zLzb|OJ7rajCIGA8T5s7-!K50Ys(r+Ca zOOf5*oFy36y?sf%Nf<5MH=qJ&c8vCD5hmujm)i1(P?(;5){6w+`UibMZ)$Nl zR)Wh+Oe%vOvq`)f7Q};m)&5~j{A~8gXWl<-A`!VU{`*^px1BdoENb!hn=mwP_c9xO zwex_yxOI>=pX5P31(NLU-sKjKEe!!f`v_omP7?v8Es7X{kU9DIo%X?IHN2v@{O9O3 z!hup@*ce@eNcT0OLF?f!@f`U537X!lsD`Kg(S9%Kzlku8e~qJ60u_%&IK`#THP&-~ z82YuR;{3A745vv>bC%3jJ`Fr9?2RfNja%}FSk67V;-^z`8X_;Y!+~SyLSM}nruG4Q%tpQRq)n#8az}GL*$Gv>Ay<{|{AWT9 zIVB}h3r4NcjdCuf-LEQoc3*hbfqm>EQJ=r?K^iC*TOBB#dYu&SGNz;j(^R{3kZ7uI zH$Ti*{2Fds$2otByv?q&;wcT}7H6=Y9F{ny!{0P^U$8U!Bc@*kYY7gjRl(+az>70q zqK}H-Ip4cAE79v89HDg+{$eo8jhe^Nf($TRkAs~jO0NP{Gls^KDHtEM6`$f?*J~$8 zMf|0{Px)v>4^xLd!p)}k{0xXyvJJ^#D~-4;08*_3_6HNpzME>_P4u8XEo42w~GY z8ToLcSl7GWsxaHavpPeV{Y=O@!5>&ev3wPfU|E->TlI9ydS>+>j?5=>oLENw_jY$K zb>oLAzqUCS(MmsS!d8VSgpY^L$iW+wB-{ZV_o_&%0TV4hdWPmmH9249Ci9$x(O^gy zbZN`~BMD`2NF6%-1f-H91v^br1j)V2e*S%D+>j%AQ=tC*DC+cHw?4rd;gM=P4)suX z+wXxD!AfNy!dnrY+I)XXOswkzx{DIXPUYA{c!!E-R;q;c0Ue{D*EVRlH(tBlE8sld zZjmMfx3K?RD+@vCwb%x~EY{UNhVj3FL9_iI8Xx~>kA>}WWi-8A0_n+r2P%% zP{6$3Q?woSKmHF5X`0VxoBXl2=oWsbSpqs%o&-aqy@LxF1v}fW$%Yr+v~$=ZP@tXH i`#k>dH+ 0 { + // we have some 0s, do shifts to compact them: + mut remaining_zeros := zeros + for x := 0; x < right_border_idx - 1; x++ { + for res.field[x] == 0 && remaining_zeros > 0 { + res.shifts++ + for k := x; k < right_border_idx; k++ { + res.field[k] = res.field[k + 1] + } + remaining_zeros-- + } + } + } + // At this point, the non 0 tiles are all on the left, with no empty spaces + // between them. we can safely merge them, when they have the same value: + for x := 0; x < right_border_idx - 1; x++ { + if res.field[x] == 0 { + break + } + if res.field[x] == res.field[x + 1] { + for k := x; k < right_border_idx; k++ { + res.field[k] = res.field[k + 1] + } + res.shifts++ + res.field[x]++ + res.points += all_tiles[res.field[x]].points + } + } + return res +} + +fn (b Board) to_left() Board { + mut res := b + for y := 0; y < 4; y++ { + mut hline := TileLine{y} + for x := 0; x < 4; x++ { + hline.field[x] = b.field[y][x] + } + reshline := hline.to_left() + res.shifts += reshline.shifts + res.points += reshline.points + for x := 0; x < 4; x++ { + res.field[y][x] = reshline.field[x] + } + } + return res +} + +// +enum GameState { + play + over + victory +} + +struct App { +mut: + gg &gg.Context + tiles []TileImage + victory_image gg.Image + // + board Board + undo []Board + atickers [4][4]int + state GameState = .play + moves int +} + +fn (mut app App) new_image(imagename string) gg.Image { + ipath := os.resource_abs_path(os.join_path('assets', imagename)) + return app.gg.create_image(ipath) +} + +fn (mut app App) new_tile(t Tile) TileImage { + mut timage := TileImage{ + tile: t + } + timage.image = app.new_image(t.picname) + return timage +} + +fn (mut app App) load_tiles() { + for t in all_tiles { + app.tiles << app.new_tile(t) + } +} + +fn (mut app App) update_tickers() { + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + mut old := app.atickers[y][x] + if old > 0 { + old-- + app.atickers[y][x] = old + } + } + } +} + +fn (app &App) draw() { + app.draw_background() + app.draw_tiles() + plabel := '$points_label.text ${app.board.points:08}' + mlabel := '$moves_label.text ${app.moves:5d}' + app.gg.draw_text(points_label.pos.x, points_label.pos.y, plabel, points_label.cfg) + app.gg.draw_text(moves_label.pos.x, moves_label.pos.y, mlabel, moves_label.cfg) + if app.state == .over { + app.gg.draw_text(game_over_label.pos.x, game_over_label.pos.y, game_over_label.text, + game_over_label.cfg) + } + if app.state == .victory { + app.gg.draw_image(victory_image_label.pos.x, victory_image_label.pos.y, victory_image_label.dim.x, + victory_image_label.dim.y, app.victory_image) + } +} + +fn (app &App) draw_background() { + tw, th := 128, 128 + for y := 30; y <= window_height; y+=tw { + for x := 0; x <= window_width; x+=th { + app.gg.draw_image(x, y, tw, th, app.tiles[0].image) + } + } +} + +fn (app &App) draw_tiles() { + border := 10 + xstart := 10 + ystart := 30 + tsize := 128 + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + tidx := app.board.field[y][x] + if tidx == 0 { + continue + } + tile := app.tiles[tidx] + tw := tsize - 10 * app.atickers[y][x] + th := tsize - 10 * app.atickers[y][x] + tx := xstart + x * (tsize + border) + (tsize - tw) / 2 + ty := ystart + y * (tsize + border) + (tsize - th) / 2 + app.gg.draw_image(tx, ty, tw, th, tile.image) + } + } +} + +fn (mut app App) new_game() { + app.board = Board{} + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + app.board.field[y][x] = 0 + app.atickers[y][x] = 0 + } + } + app.state = .play + app.undo = [] + app.moves = 0 + app.new_random_tile() + app.new_random_tile() +} + +fn (mut app App) check_for_victory() { + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + fidx := app.board.field[y][x] + if fidx == 11 { + app.victory() + return + } + } + } +} + +fn (mut app App) check_for_game_over() { + mut zeros := 0 + mut remaining_merges := 0 + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + fidx := app.board.field[y][x] + if fidx == 0 { + zeros++ + continue + } + if x > 0 && fidx == app.board.field[y][x - 1] { + remaining_merges++ + } + if x < 4 - 1 && fidx == app.board.field[y][x + 1] { + remaining_merges++ + } + if y > 0 && fidx == app.board.field[y - 1][x] { + remaining_merges++ + } + if y < 4 - 1 && fidx == app.board.field[y + 1][x] { + remaining_merges++ + } + } + } + if remaining_merges == 0 && zeros == 0 { + app.game_over() + } +} + +fn (mut app App) new_random_tile() { + mut etiles := [16]Pos{} + mut empty_tiles_max := 0 + for y := 0; y < 4; y++ { + for x := 0; x < 4; x++ { + fidx := app.board.field[y][x] + if fidx == 0 { + etiles[empty_tiles_max] = Pos{x, y} + empty_tiles_max++ + } + } + } + if empty_tiles_max > 0 { + new_random_tile_index := rand.intn(empty_tiles_max) + empty_pos := etiles[new_random_tile_index] + random_value := 1 + rand.intn(2) + app.board.field[empty_pos.y][empty_pos.x] = random_value + app.atickers[empty_pos.y][empty_pos.x] = 30 + } + app.check_for_victory() + app.check_for_game_over() +} + +fn (mut app App) victory() { + app.state = .victory +} + +fn (mut app App) game_over() { + app.state = .over +} + +type BoardMoveFN fn(b Board) Board +fn (mut app App) move(move_fn BoardMoveFN) { + old := app.board + new := move_fn(old) + if old.shifts != new.shifts { + app.moves++ + app.board = new + app.undo << old + app.new_random_tile() + } +} + +fn (mut app App) on_key_down(key sapp.KeyCode) { + // these keys are independent from the game state: + match key { + .escape { + exit(0) + } + .n { + app.new_game() + } + //.t {/* fast setup for a victory situation: */ app.board = new_board(['JJ@@', '@@@@', '@@@@', '@@@@'])} + .backspace { + if app.undo.len > 0 { + app.state = .play + app.board = app.undo.pop() + app.moves-- + return + } + } + else {} + } + if app.state == .play { + match key { + .up, .w { app.move(fn (b Board) Board { + return b.transpose().to_left().transpose() + }) } + .left, .a { app.move(fn (b Board) Board { + return b.to_left() + }) } + .down, .s { app.move(fn (b Board) Board { + return b.transpose().hmirror().to_left().hmirror().transpose() + }) } + .right, .d { app.move(fn (b Board) Board { + return b.hmirror().to_left().hmirror() + }) } + else {} + } + } +} + +// +fn on_event(e &sapp.Event, mut app App) { + if e.typ == .key_down { + app.on_key_down(e.key_code) + } +} + +fn frame(mut app App) { + app.update_tickers() + app.gg.begin() + app.draw() + app.gg.end() +} + +fn main() { + mut app := &App{ + gg: 0 + state: .play + } + app.new_game() + app.gg = gg.new_context({ + bg_color: gx.white + width: window_width + height: window_height + use_ortho: true + create_window: true + window_title: window_title + frame_fn: frame + event_fn: on_event + user_data: app + font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf') + }) + app.load_tiles() + app.victory_image = app.new_image('victory.png') + app.gg.run() +} diff --git a/examples/2048/v.mod b/examples/2048/v.mod new file mode 100644 index 000000000..5c146f8d4 --- /dev/null +++ b/examples/2048/v.mod @@ -0,0 +1,7 @@ +Module { + name: 'v2048', + description: 'A simple 2048 game written in V.', + version: '0.0.2', + repo_url: 'https://github.com/spytheman/v2048', + dependencies: [] +} -- 2.30.2