├── .gitignore ├── LICENSE ├── README.md ├── assets ├── 1.png ├── 1.xcf ├── 1024.png ├── 1024.xcf ├── 128.png ├── 128.xcf ├── 16.png ├── 16.xcf ├── 2.png ├── 2.xcf ├── 2048.png ├── 2048.xcf ├── 256.png ├── 256.xcf ├── 32.png ├── 32.xcf ├── 4.png ├── 4.xcf ├── 4096.png ├── 4096.xcf ├── 512.png ├── 512.xcf ├── 64.png ├── 64.xcf ├── 8.png ├── 8.xcf ├── 8196.png ├── 8196.xcf ├── RobotoMono-Regular.ttf ├── all.xcf ├── victory.png └── victory.xcf ├── main.v └── v.mod /.gitignore: -------------------------------------------------------------------------------- 1 | 2048 2 | .gdb_history 3 | main 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Delyan Angelov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # v2048 2 | 3 | This is a simple [2048 game](https://play2048.co/), written in [the V programming language](https://vlang.io/). 4 | ![2048 Game Screenshot](https://url4e.com/gyazo/images/1ad829cf.png) 5 | 6 | ## Notes 7 | This game is tested to work with V 0.1.29 b2fee21, but probably can be compiled with a more recent V compiler too. 8 | 9 | Update (2022/08/10): it works with V 0.3.0 c752e5e. 10 | 11 | Note: a much improved version of this game is available in the main V repository. 12 | See ![2048.v](https://github.com/vlang/v/blob/master/examples/2048/2048.v) . 13 | It has an AI (autosolver) mode, multiple cleaner looks of the tiles. 14 | It can also run on Android and on the ![web](https://v2048.vercel.app/) , unlike this one. 15 | -------------------------------------------------------------------------------- /assets/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/1.png -------------------------------------------------------------------------------- /assets/1.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/1.xcf -------------------------------------------------------------------------------- /assets/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/1024.png -------------------------------------------------------------------------------- /assets/1024.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/1024.xcf -------------------------------------------------------------------------------- /assets/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/128.png -------------------------------------------------------------------------------- /assets/128.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/128.xcf -------------------------------------------------------------------------------- /assets/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/16.png -------------------------------------------------------------------------------- /assets/16.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/16.xcf -------------------------------------------------------------------------------- /assets/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/2.png -------------------------------------------------------------------------------- /assets/2.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/2.xcf -------------------------------------------------------------------------------- /assets/2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/2048.png -------------------------------------------------------------------------------- /assets/2048.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/2048.xcf -------------------------------------------------------------------------------- /assets/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/256.png -------------------------------------------------------------------------------- /assets/256.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/256.xcf -------------------------------------------------------------------------------- /assets/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/32.png -------------------------------------------------------------------------------- /assets/32.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/32.xcf -------------------------------------------------------------------------------- /assets/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/4.png -------------------------------------------------------------------------------- /assets/4.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/4.xcf -------------------------------------------------------------------------------- /assets/4096.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/4096.png -------------------------------------------------------------------------------- /assets/4096.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/4096.xcf -------------------------------------------------------------------------------- /assets/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/512.png -------------------------------------------------------------------------------- /assets/512.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/512.xcf -------------------------------------------------------------------------------- /assets/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/64.png -------------------------------------------------------------------------------- /assets/64.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/64.xcf -------------------------------------------------------------------------------- /assets/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/8.png -------------------------------------------------------------------------------- /assets/8.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/8.xcf -------------------------------------------------------------------------------- /assets/8196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/8196.png -------------------------------------------------------------------------------- /assets/8196.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/8196.xcf -------------------------------------------------------------------------------- /assets/RobotoMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/RobotoMono-Regular.ttf -------------------------------------------------------------------------------- /assets/all.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/all.xcf -------------------------------------------------------------------------------- /assets/victory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/victory.png -------------------------------------------------------------------------------- /assets/victory.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spytheman/v2048/fb9a0ddbbf5716b8c579d469c5134af0f482b1fe/assets/victory.xcf -------------------------------------------------------------------------------- /main.v: -------------------------------------------------------------------------------- 1 | import gg 2 | import gx 3 | import os 4 | import rand 5 | 6 | struct Tile { 7 | id int 8 | points int 9 | picname string 10 | } 11 | 12 | struct Pos { 13 | x int = -1 14 | y int = -1 15 | } 16 | 17 | struct TextLabel { 18 | text string 19 | pos Pos 20 | cfg gx.TextCfg 21 | } 22 | 23 | struct ImageLabel { 24 | pos Pos 25 | dim Pos 26 | } 27 | 28 | const window_title = 'v2048' 29 | const window_width = 562 30 | const window_height = 562 31 | const points_label = TextLabel{ 32 | text: 'Points: ' 33 | pos: Pos{10, 5} 34 | cfg: gx.TextCfg{ 35 | align: gx.align_left 36 | size: 24 37 | color: gx.rgb(0, 0, 0) 38 | } 39 | } 40 | const moves_label = TextLabel{ 41 | text: 'Moves: ' 42 | pos: Pos{window_width - 160, 5} 43 | cfg: gx.TextCfg{ 44 | align: gx.align_left 45 | size: 24 46 | color: gx.rgb(0, 0, 0) 47 | } 48 | } 49 | const game_over_label = TextLabel{ 50 | text: 'Game Over' 51 | pos: Pos{80, 220} 52 | cfg: gx.TextCfg{ 53 | align: gx.align_left 54 | size: 100 55 | color: gx.rgb(0, 0, 255) 56 | } 57 | } 58 | const victory_image_label = ImageLabel{ 59 | pos: Pos{80, 220} 60 | dim: Pos{430, 130} 61 | } 62 | const all_tiles = [ 63 | Tile{0, 0, '1.png'}, 64 | Tile{1, 2, '2.png'}, 65 | Tile{2, 4, '4.png'}, 66 | Tile{3, 8, '8.png'}, 67 | Tile{4, 16, '16.png'}, 68 | Tile{5, 32, '32.png'}, 69 | Tile{6, 64, '64.png'}, 70 | Tile{7, 128, '128.png'}, 71 | Tile{8, 256, '256.png'}, 72 | Tile{9, 512, '512.png'}, 73 | Tile{10, 1024, '1024.png'}, 74 | Tile{11, 2048, '2048.png'}, 75 | Tile{12, 4096, '4096.png'}, 76 | Tile{13, 8196, '8196.png'}, 77 | ] 78 | 79 | struct TileImage { 80 | tile Tile 81 | mut: 82 | image gg.Image 83 | } 84 | 85 | // TODO: remove the .str() method here. It is just to prevent C compilation errors 86 | // about sg_image_str 87 | fn (t &TileImage) str() string { 88 | return 'TileImage{ Tile{id: ${t.tile.id}, points: ${t.tile.points}, picname: ${t.tile.picname} } }' 89 | } 90 | 91 | // 92 | struct Board { 93 | mut: 94 | field [4][4]int 95 | points int 96 | shifts int 97 | } 98 | 99 | fn (b Board) transpose() Board { 100 | mut res := b 101 | for y := 0; y < 4; y++ { 102 | for x := 0; x < 4; x++ { 103 | res.field[y][x] = b.field[x][y] 104 | } 105 | } 106 | return res 107 | } 108 | 109 | fn (b Board) hmirror() Board { 110 | mut res := b 111 | for y := 0; y < 4; y++ { 112 | for x := 0; x < 4; x++ { 113 | res.field[y][x] = b.field[y][4 - x - 1] 114 | } 115 | } 116 | return res 117 | } 118 | 119 | struct TileLine { 120 | ypos int 121 | mut: 122 | field [5]int 123 | points int 124 | shifts int 125 | } 126 | 127 | fn no_newlines(s string) string { 128 | return s.replace('\n', ' ') 129 | } 130 | 131 | // 132 | fn (t TileLine) to_left() TileLine { 133 | right_border_idx := 5 134 | mut res := t 135 | mut zeros := 0 136 | mut nonzeros := 0 137 | // gather meta info about the line: 138 | for x := 0; x < 4; x++ { 139 | if res.field[x] == 0 { 140 | zeros++ 141 | } else { 142 | nonzeros++ 143 | } 144 | } 145 | if nonzeros == 0 { 146 | // when all the tiles are empty, there is nothing left to do 147 | return res 148 | } 149 | if zeros > 0 { 150 | // we have some 0s, do shifts to compact them: 151 | mut remaining_zeros := zeros 152 | for x := 0; x < right_border_idx - 1; x++ { 153 | for res.field[x] == 0 && remaining_zeros > 0 { 154 | res.shifts++ 155 | for k := x; k < right_border_idx - 1; k++ { 156 | res.field[k] = res.field[k + 1] 157 | } 158 | remaining_zeros-- 159 | } 160 | } 161 | } 162 | // At this point, the non 0 tiles are all on the left, with no empty spaces 163 | // between them. we can safely merge them, when they have the same value: 164 | for x := 0; x < right_border_idx - 1; x++ { 165 | if res.field[x] == 0 { 166 | break 167 | } 168 | if res.field[x] == res.field[x + 1] { 169 | for k := x; k < right_border_idx - 1; k++ { 170 | res.field[k] = res.field[k + 1] 171 | } 172 | res.shifts++ 173 | res.field[x]++ 174 | res.points += all_tiles[res.field[x]].points 175 | } 176 | } 177 | // eprintln('TileLine.to_left shifts: $res.shifts | zeros: $zeros | nonzeros: $nonzeros\n${no_newlines(t.str())}\n${no_newlines(res.str())}\n-----------------------------------') 178 | return res 179 | } 180 | 181 | fn (b Board) to_left() Board { 182 | mut res := b 183 | for y := 0; y < 4; y++ { 184 | mut hline := TileLine{ 185 | ypos: y 186 | } 187 | for x := 0; x < 4; x++ { 188 | hline.field[x] = b.field[y][x] 189 | } 190 | reshline := hline.to_left() 191 | res.shifts += reshline.shifts 192 | res.points += reshline.points 193 | for x := 0; x < 4; x++ { 194 | res.field[y][x] = reshline.field[x] 195 | } 196 | } 197 | return res 198 | } 199 | 200 | // 201 | enum GameState { 202 | play 203 | over 204 | victory 205 | } 206 | 207 | struct App { 208 | mut: 209 | gg &gg.Context = unsafe { nil } 210 | tiles []TileImage 211 | victory_image gg.Image 212 | // 213 | board Board 214 | undo []Board 215 | atickers [4][4]int 216 | state GameState = .play 217 | moves int 218 | } 219 | 220 | fn (mut app App) new_tile(t Tile) TileImage { 221 | mut timage := TileImage{ 222 | tile: t 223 | } 224 | timage.image = app.gg.create_image(os.resource_abs_path(os.join_path('assets', t.picname))) or { 225 | panic(err) 226 | } 227 | return timage 228 | } 229 | 230 | fn (mut app App) load_tiles() { 231 | for t in all_tiles { 232 | app.tiles << app.new_tile(t) 233 | } 234 | // eprintln('tiles: $app.tiles') 235 | } 236 | 237 | fn (mut app App) update_tickers() { 238 | for y := 0; y < 4; y++ { 239 | for x := 0; x < 4; x++ { 240 | mut old := app.atickers[y][x] 241 | if old > 0 { 242 | old-- 243 | app.atickers[y][x] = old 244 | } 245 | } 246 | } 247 | } 248 | 249 | fn (app &App) draw() { 250 | app.draw_tiles() 251 | app.gg.draw_text(points_label.pos.x, points_label.pos.y, '${points_label.text} ${app.board.points:08}', 252 | points_label.cfg) 253 | app.gg.draw_text(moves_label.pos.x, moves_label.pos.y, '${moves_label.text} ${app.moves:5d}', 254 | moves_label.cfg) 255 | if app.state == .over { 256 | app.gg.draw_text(game_over_label.pos.x, game_over_label.pos.y, game_over_label.text, 257 | game_over_label.cfg) 258 | } 259 | if app.state == .victory { 260 | app.gg.draw_image(victory_image_label.pos.x, victory_image_label.pos.y, victory_image_label.dim.x, 261 | victory_image_label.dim.y, app.victory_image) 262 | } 263 | } 264 | 265 | fn (app &App) draw_tiles() { 266 | border := 10 267 | xstart := 10 268 | ystart := 30 269 | tsize := 128 270 | for y := 0; y < 4; y++ { 271 | for x := 0; x < 4; x++ { 272 | tidx := app.board.field[y][x] 273 | tile := app.tiles[tidx] 274 | tw := tsize - 10 * app.atickers[y][x] 275 | th := tsize - 10 * app.atickers[y][x] 276 | app.gg.draw_image(xstart + x * (tsize + border) + (tsize - tw) / 2, ystart + 277 | y * (tsize + border) + (tsize - th) / 2, tw, th, tile.image) 278 | } 279 | } 280 | } 281 | 282 | fn (mut app App) new_game() { 283 | app.board = Board{} 284 | for y := 0; y < 4; y++ { 285 | for x := 0; x < 4; x++ { 286 | app.board.field[y][x] = 0 287 | app.atickers[y][x] = 0 288 | } 289 | } 290 | app.state = .play 291 | app.undo = [] 292 | app.moves = 0 293 | app.new_random_tile() 294 | app.new_random_tile() 295 | } 296 | 297 | fn (mut app App) check_for_victory() { 298 | for y := 0; y < 4; y++ { 299 | for x := 0; x < 4; x++ { 300 | fidx := app.board.field[y][x] 301 | if fidx == 11 { 302 | app.victory() 303 | return 304 | } 305 | } 306 | } 307 | } 308 | 309 | fn (mut app App) check_for_game_over() { 310 | mut zeros := 0 311 | mut remaining_merges := 0 312 | for y := 0; y < 4; y++ { 313 | for x := 0; x < 4; x++ { 314 | fidx := app.board.field[y][x] 315 | if fidx == 0 { 316 | zeros++ 317 | continue 318 | } 319 | if x > 0 && fidx == app.board.field[y][x - 1] { 320 | remaining_merges++ 321 | } 322 | if x < 4 - 1 && fidx == app.board.field[y][x + 1] { 323 | remaining_merges++ 324 | } 325 | if y > 0 && fidx == app.board.field[y - 1][x] { 326 | remaining_merges++ 327 | } 328 | if y < 4 - 1 && fidx == app.board.field[y + 1][x] { 329 | remaining_merges++ 330 | } 331 | } 332 | } 333 | if remaining_merges == 0 && zeros == 0 { 334 | app.game_over() 335 | } 336 | } 337 | 338 | fn (mut app App) new_random_tile() { 339 | mut etiles := [16]Pos{} 340 | mut empty_tiles_max := 0 341 | for y := 0; y < 4; y++ { 342 | for x := 0; x < 4; x++ { 343 | fidx := app.board.field[y][x] 344 | if fidx == 0 { 345 | etiles[empty_tiles_max] = Pos{x, y} 346 | empty_tiles_max++ 347 | } 348 | } 349 | } 350 | if empty_tiles_max > 0 { 351 | new_random_tile_index := rand.intn(empty_tiles_max) or { 0 } 352 | empty_pos := etiles[new_random_tile_index] 353 | random_value := 1 + rand.intn(2) or { 0 } 354 | app.board.field[empty_pos.y][empty_pos.x] = random_value 355 | app.atickers[empty_pos.y][empty_pos.x] = 30 356 | // eprintln('>>>>> new_random_tile, app.board.points: $app.board.points | random_value: $random_value at ${no_newlines(empty_pos.str())}') 357 | } 358 | app.check_for_victory() 359 | app.check_for_game_over() 360 | } 361 | 362 | fn (mut app App) victory() { 363 | app.state = .victory 364 | } 365 | 366 | fn (mut app App) game_over() { 367 | app.state = .over 368 | } 369 | 370 | type BoardMoveFN = fn (b Board) Board 371 | 372 | fn (mut app App) move(move_fn BoardMoveFN) { 373 | old := app.board 374 | new := move_fn(old) 375 | if old.shifts != new.shifts { 376 | app.moves++ 377 | app.board = new 378 | app.undo << old 379 | app.new_random_tile() 380 | } 381 | } 382 | 383 | fn (mut app App) on_key_down(key gg.KeyCode) { 384 | // these keys are independent from the game state: 385 | match key { 386 | .escape { 387 | exit(0) 388 | } 389 | .space { 390 | app.new_random_tile() 391 | } 392 | .n { 393 | app.new_game() 394 | } 395 | .backspace { 396 | if app.undo.len > 0 { 397 | app.state = .play 398 | app.board = app.undo.pop() 399 | app.moves-- 400 | return 401 | } 402 | } 403 | else {} 404 | } 405 | if app.state == .play { 406 | match key { 407 | .up, .w { 408 | app.move(fn (b Board) Board { 409 | return b.transpose().to_left().transpose() 410 | }) 411 | } 412 | .left, .a { 413 | app.move(fn (b Board) Board { 414 | return b.to_left() 415 | }) 416 | } 417 | .down, .s { 418 | app.move(fn (b Board) Board { 419 | return b.transpose().hmirror().to_left().hmirror().transpose() 420 | }) 421 | } 422 | .right, .d { 423 | app.move(fn (b Board) Board { 424 | return b.hmirror().to_left().hmirror() 425 | }) 426 | } 427 | else {} 428 | } 429 | } 430 | // eprintln('app.board.points: $app.board.points') 431 | } 432 | 433 | // 434 | fn on_event(e &gg.Event, mut app App) { 435 | if e.typ == .key_down { 436 | app.on_key_down(e.key_code) 437 | } 438 | } 439 | 440 | fn frame(mut app App) { 441 | app.update_tickers() 442 | app.gg.begin() 443 | app.draw() 444 | app.gg.end() 445 | } 446 | 447 | fn main() { 448 | mut app := &App{ 449 | state: .play 450 | } 451 | app.new_game() 452 | app.gg = gg.new_context( 453 | bg_color: gx.white 454 | width: window_width 455 | height: window_height 456 | use_ortho: true 457 | create_window: true 458 | window_title: window_title 459 | frame_fn: frame 460 | event_fn: on_event 461 | user_data: app 462 | font_path: os.resource_abs_path(os.join_path('assets', 'RobotoMono-Regular.ttf')) 463 | ) 464 | app.load_tiles() 465 | app.victory_image = app.gg.create_image(os.resource_abs_path(os.join_path('assets', 466 | 'victory.png')))! 467 | app.gg.run() 468 | } 469 | -------------------------------------------------------------------------------- /v.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'v2048', 3 | description: 'A simple 2048 game written in V.', 4 | version: '0.0.1', 5 | repo_url: 'https://github.com/spytheman/v2048', 6 | dependencies: [] 7 | } 8 | --------------------------------------------------------------------------------