From 4aaeaa433157b9e8735d7022f27ff3b1b52614d1 Mon Sep 17 00:00:00 2001 From: spaceface777 Date: Fri, 11 Sep 2020 12:19:38 +0200 Subject: [PATCH] examples/2048: some updates and improvements (#6343) --- examples/2048/2048.v | 742 ++++++++++++++++++++----------- examples/2048/README.md | 6 +- examples/2048/assets/1.png | Bin 8548 -> 0 bytes examples/2048/assets/1024.png | Bin 2158 -> 0 bytes examples/2048/assets/128.png | Bin 2290 -> 0 bytes examples/2048/assets/16.png | Bin 1664 -> 0 bytes examples/2048/assets/2.png | Bin 1534 -> 0 bytes examples/2048/assets/2048.png | Bin 2686 -> 0 bytes examples/2048/assets/256.png | Bin 2675 -> 0 bytes examples/2048/assets/32.png | Bin 1961 -> 0 bytes examples/2048/assets/4.png | Bin 1218 -> 0 bytes examples/2048/assets/512.png | Bin 1891 -> 0 bytes examples/2048/assets/64.png | Bin 1898 -> 0 bytes examples/2048/assets/8.png | Bin 1534 -> 0 bytes examples/2048/assets/victory.png | Bin 9827 -> 0 bytes examples/2048/demo.png | Bin 0 -> 25512 bytes 16 files changed, 484 insertions(+), 264 deletions(-) delete mode 100644 examples/2048/assets/1.png delete mode 100644 examples/2048/assets/1024.png delete mode 100644 examples/2048/assets/128.png delete mode 100644 examples/2048/assets/16.png delete mode 100644 examples/2048/assets/2.png delete mode 100644 examples/2048/assets/2048.png delete mode 100644 examples/2048/assets/256.png delete mode 100644 examples/2048/assets/32.png delete mode 100644 examples/2048/assets/4.png delete mode 100644 examples/2048/assets/512.png delete mode 100644 examples/2048/assets/64.png delete mode 100644 examples/2048/assets/8.png delete mode 100644 examples/2048/assets/victory.png create mode 100644 examples/2048/demo.png diff --git a/examples/2048/2048.v b/examples/2048/2048.v index f03455ced9..ab6065f5e6 100644 --- a/examples/2048/2048.v +++ b/examples/2048/2048.v @@ -3,11 +3,125 @@ import gx import os import rand import sokol.sapp +import time -struct Tile { - id int - points int - picname string +struct App { +mut: + gg &gg.Context = 0 + touch TouchInfo + ui Ui + theme &Theme = themes[0] + theme_idx int + board Board + undo []Board + atickers [4][4]int + state GameState = .play + tile_format TileFormat = .normal + moves int + perf &Perf = 0 +} + +struct Ui { +mut: + tile_size int + border_size int + padding_size int + header_size int + font_size int + window_width int + window_height int + x_padding int + y_padding int +} + +struct Theme { + bg_color gx.Color + padding_color gx.Color + text_color gx.Color + game_over_color gx.Color + victory_color gx.Color + tile_colors []gx.Color +} + +const ( + themes = [ + &Theme { + bg_color: gx.rgb(250, 248, 239) + padding_color: gx.rgb(143, 130, 119) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.black + tile_colors: [ + gx.rgb(205, 193, 180) // Empty / 0 tile + gx.rgb(238, 228, 218) // 2 + gx.rgb(237, 224, 200) // 4 + gx.rgb(242, 177, 121) // 8 + gx.rgb(245, 149, 99) // 16 + gx.rgb(246, 124, 95) // 32 + gx.rgb(246, 94, 59) // 64 + gx.rgb(237, 207, 114) // 128 + gx.rgb(237, 204, 97) // 256 + gx.rgb(237, 200, 80) // 512 + gx.rgb(237, 197, 63) // 1024 + gx.rgb(237, 194, 46) // 2048 + ] + }, + &Theme { + bg_color: gx.rgb(55, 55, 55) + padding_color: gx.rgb(68, 60, 59) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.white + tile_colors: [ + gx.rgb(123, 115, 108) + gx.rgb(142, 136, 130) + gx.rgb(142, 134, 120) + gx.rgb(145, 106, 72) + gx.rgb(147, 89, 59) + gx.rgb(147, 74, 57) + gx.rgb(147, 56, 35) + gx.rgb(142, 124, 68) + gx.rgb(142, 122, 58) + gx.rgb(142, 120, 48) + gx.rgb(142, 118, 37) + gx.rgb(142, 116, 27) + ] + }, + &Theme { + bg_color: gx.rgb(38, 38, 66) + padding_color: gx.rgb(58, 50, 74) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.white + tile_colors: [ + gx.rgb(92, 86, 140) + gx.rgb(106, 99, 169) + gx.rgb(106, 97, 156) + gx.rgb(108, 79, 93) + gx.rgb(110, 66, 76) + gx.rgb(110, 55, 74) + gx.rgb(110, 42, 45) + gx.rgb(106, 93, 88) + gx.rgb(106, 91, 75) + gx.rgb(106, 90, 62) + gx.rgb(106, 88, 48) + gx.rgb(106, 87, 35) + ] + }, + ] + window_title = 'V 2048' + default_window_width = 544 + default_window_height = 560 + animation_length = 10 // frames +) + +// Used for performance monitoring when `-d showfps` is passed, unused / optimized out otherwise +struct Perf { +mut: + frame int + frame_old int + frame_sw time.StopWatch = time.new_stopwatch({}) + second_sw time.StopWatch = time.new_stopwatch({}) } struct Pos { @@ -15,74 +129,6 @@ struct Pos { y int = -1 } -struct ImageLabel { - pos Pos - dim Pos -} - -struct TextLabel { - text string - pos Pos - cfg gx.TextCfg -} - -const ( - window_title = 'V 2048' - window_width = 562 - window_height = 580 - points_label = TextLabel{ - text: 'Points: ' - pos: Pos{10, 5} - cfg: gx.TextCfg{ - align: .left - size: 24 - color: gx.rgb(0, 0, 0) - } - } - moves_label = TextLabel{ - text: 'Moves: ' - pos: Pos{window_width - 160, 5} - cfg: gx.TextCfg{ - align: .left - size: 24 - color: gx.rgb(0, 0, 0) - } - } - game_over_label = TextLabel{ - text: 'Game Over' - pos: Pos{80, 220} - cfg: gx.TextCfg{ - align: .left - size: 100 - color: gx.rgb(0, 0, 255) - } - } - victory_image_label = ImageLabel{ - pos: Pos{80, 220} - dim: Pos{430, 130} - } - all_tiles = [ - Tile{0, 0, '1.png'}, - Tile{1, 2, '2.png'}, - Tile{2, 4, '4.png'}, - Tile{3, 8, '8.png'}, - Tile{4, 16, '16.png'}, - Tile{5, 32, '32.png'}, - Tile{6, 64, '64.png'}, - Tile{7, 128, '128.png'}, - Tile{8, 256, '256.png'}, - Tile{9, 512, '512.png'}, - Tile{10, 1024, '1024.png'}, - Tile{11, 2048, '2048.png'}, - ] -) - -struct TileImage { - tile Tile -mut: - image gg.Image -} - struct Board { mut: field [4][4]int @@ -90,36 +136,6 @@ mut: shifts int } -fn new_board(sb []string) Board { - mut b := Board{} - for y := 0; y < 4; y++ { - for x := 0; x < 4; x++ { - b.field[y][x] = sb[y][x] - 64 - } - } - return b -} - -fn (b Board) transpose() Board { - mut res := b - for y := 0; y < 4; y++ { - for x := 0; x < 4; x++ { - res.field[y][x] = b.field[x][y] - } - } - return res -} - -fn (b Board) hmirror() Board { - mut res := b - for y := 0; y < 4; y++ { - for x := 0; x < 4; x++ { - res.field[y][x] = b.field[y][4 - x - 1] - } - } - return res -} - struct TileLine { ypos int mut: @@ -128,6 +144,78 @@ mut: shifts int } +struct TouchInfo { +mut: + start_pos Pos +} + +enum TileFormat { + normal + log + exponent + shifts + none_ + end_ // To know when to wrap around +} + +enum GameState { + play + over + victory +} + +enum LabelKind { + points + moves + tile + victory + game_over +} + +enum Direction { + up + down + left + right +} + +// Utility functions, to avoid importing `math` +[inline] +fn min(a, b int) int { + if a < b { return a } else { return b } +} + +[inline] +fn max(a, b int) int { + if a > b { return a } else { return b } +} + +[inline] +fn abs(a int) int { + if a < 0 { return -a } else { return a } +} + +fn (b Board) transpose() Board { + mut res := b + for y in 0..4 { + for x in 0..4 { + res.field[y][x] = b.field[x][y] + } + } + return res +} + +fn (b Board) hmirror() Board { + mut res := b + for y in 0..4 { + for x in 0..4 { + res.field[y][x] = b.field[y][3 - x] + } + } + return res +} + +// GCC optimization bug; inlining fails when compiled with -prod [no_inline] fn (t TileLine) to_left() TileLine { right_border_idx := 5 @@ -135,12 +223,8 @@ fn (t TileLine) to_left() TileLine { mut zeros := 0 mut nonzeros := 0 // gather meta info about the line: - for x := 0; x < 4; x++ { - if res.field[x] == 0 { - zeros++ - } else { - nonzeros++ - } + for x in res.field { + if x == 0 { zeros++ } else { nonzeros++ } } if nonzeros == 0 { // when all the tiles are empty, there is nothing left to do @@ -171,7 +255,7 @@ fn (t TileLine) to_left() TileLine { } res.shifts++ res.field[x]++ - res.points += all_tiles[res.field[x]].points + res.points += 1 << res.field[x] } } return res @@ -179,65 +263,24 @@ fn (t TileLine) to_left() TileLine { fn (b Board) to_left() Board { mut res := b - for y := 0; y < 4; y++ { - mut hline := TileLine{ - ypos: y - } - for x := 0; x < 4; x++ { + for y in 0..4 { + mut hline := TileLine{ypos: y} + for x in 0..4 { 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++ { + for x in 0..4 { 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++ { + for y in 0..4 { + for x in 0..4 { mut old := app.atickers[y][x] if old > 0 { old-- @@ -247,57 +290,10 @@ fn (mut app App) update_tickers() { } } -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++ { + for y in 0..4 { + for x in 0..4 { app.board.field[y][x] = 0 app.atickers[y][x] = 0 } @@ -310,8 +306,8 @@ fn (mut app App) new_game() { } fn (mut app App) check_for_victory() { - for y := 0; y < 4; y++ { - for x := 0; x < 4; x++ { + for y in 0..4 { + for x in 0..4 { fidx := app.board.field[y][x] if fidx == 11 { app.victory() @@ -324,23 +320,17 @@ fn (mut app App) check_for_victory() { 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++ { + for y in 0..4 { + for x in 0..4 { 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] { + if (x > 0 && fidx == app.board.field[y][x - 1]) + || (x < 4 - 1 && fidx == app.board.field[y][x + 1]) + || (y > 0 && fidx == app.board.field[y - 1][x]) + || (y < 4 - 1 && fidx == app.board.field[y + 1][x]) { remaining_merges++ } } @@ -353,11 +343,12 @@ fn (mut app App) check_for_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++ { + for y in 0..4 { + for x in 0..4 { fidx := app.board.field[y][x] if fidx == 0 { etiles[empty_tiles_max] = Pos{x, y} + app.atickers[y][x] = 0 empty_tiles_max++ } } @@ -365,9 +356,10 @@ fn (mut app App) new_random_tile() { 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) + // 1/8 chance of creating a `4` tile + random_value := if rand.intn(8) == 0 { 2 } else { 1 } app.board.field[empty_pos.y][empty_pos.x] = random_value - app.atickers[empty_pos.y][empty_pos.x] = 30 + app.atickers[empty_pos.y][empty_pos.x] = animation_length } app.check_for_victory() app.check_for_game_over() @@ -381,11 +373,14 @@ fn (mut app App) game_over() { app.state = .over } -type BoardMoveFN = fn (b Board) Board - -fn (mut app App) move(move_fn BoardMoveFN) { +fn (mut app App) move(d Direction) { old := app.board - new := move_fn(old) + new := match d { + .left { old.to_left() } + .right { old.hmirror().to_left().hmirror() } + .up { old.transpose().to_left().transpose() } + .down { old.transpose().hmirror().to_left().hmirror().transpose() } + } if old.shifts != new.shifts { app.moves++ app.board = new @@ -394,78 +389,301 @@ fn (mut app App) move(move_fn BoardMoveFN) { } } +fn (app &App) label_format(kind LabelKind) gx.TextCfg { + match kind { + .points { + return { + color: app.theme.text_color + align: .left + size: app.ui.font_size / 2 + } + } .moves { + return { + color: app.theme.text_color + align: .right + size: app.ui.font_size / 2 + } + } .tile { + return { + color: app.theme.text_color + align: .center + vertical_align: .middle + size: app.ui.font_size + } + } .victory { + return { + color: app.theme.victory_color + align: .center + vertical_align: .middle + size: app.ui.font_size * 2 + } + } .game_over { + return { + color: app.theme.game_over_color + align: .center + vertical_align: .middle + size: app.ui.font_size * 2 + } + } + } +} + +fn (mut app App) set_theme(idx int) { + theme := themes[idx] + app.theme_idx = idx + app.theme = theme + app.gg.set_bg_color(theme.bg_color) +} + +fn (mut app App) resize() { + mut s := sapp.dpi_scale() || 1 + w := int(sapp.width() / s) + h := int(sapp.height() / s) + m := f32(min(w, h)) + + app.ui.window_width = w + app.ui.window_height = h + app.ui.padding_size = int(m / 38) + app.ui.header_size = app.ui.padding_size + app.ui.border_size = app.ui.padding_size * 2 + app.ui.tile_size = int((m - app.ui.padding_size * 5 - app.ui.border_size * 2) / 4) + app.ui.font_size = int(m / 10) + + // If the window's height is greater than its width, center the board vertically. + // If not, center it horizontally + if w > h { + app.ui.y_padding = 0 + app.ui.x_padding = (app.ui.window_width - app.ui.window_height) / 2 + } else { + app.ui.y_padding = (app.ui.window_height - app.ui.window_width - app.ui.header_size) / 2 + app.ui.x_padding = 0 + } +} + +fn (app &App) draw() { + ww := app.ui.window_width + wh := app.ui.window_height + labelx := app.ui.x_padding + app.ui.border_size + labely := app.ui.y_padding + app.ui.border_size / 2 + + app.draw_tiles() + app.gg.draw_text(labelx, labely, 'Points: $app.board.points', app.label_format(.points)) + app.gg.draw_text(ww - labelx, labely, 'Moves: $app.moves', app.label_format(.moves)) + + // TODO: Make transparency work in `gg` + if app.state == .over { + app.gg.draw_rect(0, 0, ww, wh, gx.rgba(15, 0, 0, 44)) + app.gg.draw_text(ww / 2, wh / 2, 'Game Over', app.label_format(.game_over)) + } + if app.state == .victory { + app.gg.draw_rect(0, 0, ww, wh, gx.rgba(0, 15, 0, 44)) + app.gg.draw_text(ww / 2, wh / 2, 'Victory!', app.label_format(.victory)) + } +} + +fn (app &App) draw_tiles() { + xstart := app.ui.x_padding + app.ui.border_size + ystart := app.ui.y_padding + app.ui.border_size + app.ui.header_size + + toffset := app.ui.tile_size + app.ui.padding_size + tiles_size := min(app.ui.window_width, app.ui.window_height) - app.ui.border_size * 2 + + // Draw the padding around the tiles + app.gg.draw_rect(xstart, ystart, tiles_size, tiles_size, app.theme.padding_color) + // Draw the actual tiles + for y in 0..4 { + for x in 0..4 { + tidx := app.board.field[y][x] + tile_color := if tidx < app.theme.tile_colors.len { + app.theme.tile_colors[tidx] + } else { + // If there isn't a specific color for this tile, reuse the last color available + app.theme.tile_colors.last() + } + anim_size := animation_length - app.atickers[y][x] + tw := int(f64(app.ui.tile_size) / animation_length * anim_size) + th := tw // square tiles, w == h + xoffset := xstart + app.ui.padding_size + x * toffset + (app.ui.tile_size - tw) / 2 + yoffset := ystart + app.ui.padding_size + y * toffset + (app.ui.tile_size - th) / 2 + app.gg.draw_rect(xoffset, yoffset, tw, th, tile_color) + + if tidx != 0 { // 0 == blank spot + xpos := xoffset + tw / 2 + ypos := yoffset + th / 2 + mut fmt := app.label_format(.tile) + fmt = { fmt | size: int(f32(fmt.size - 1) / animation_length * anim_size) } + + match app.tile_format { + .normal { + app.gg.draw_text(xpos, ypos, '${1 << tidx}', fmt) + } .log { + app.gg.draw_text(xpos, ypos, '$tidx', fmt) + } .exponent { + app.gg.draw_text(xpos, ypos, '2', fmt) + fs2 := int(f32(fmt.size) * 0.67) + app.gg.draw_text(xpos + app.ui.tile_size / 10, ypos - app.ui.tile_size / 8, + '$tidx', { fmt | size: fs2, align: gx.HorizontalAlign.left }) + } .shifts { + fs2 := int(f32(fmt.size) * 0.6) + app.gg.draw_text(xpos, ypos, '2<<${tidx - 1}', { fmt | size: fs2 }) + } .none_ {} // Don't draw any text here, colors only + .end_ {} // Should never get here + } + } + } + } +} + +fn (mut app App) handle_swipe(start, end Pos) { + min_swipe_distance := min(app.ui.window_width, app.ui.window_height) / 10 + dx := end.x - start.x + dy := end.y - start.y + adx := abs(dx) + ady := abs(dy) + dmax := max(adx, ady) + dmin := min(adx, ady) + + if dmax < min_swipe_distance { return } // Swipe was too short + if dmax / dmin < 2 { return } // Swiped diagonally + + if adx > ady { + if dx < 0 { app.move(.left) } else { app.move(.right) } + } else { + if dy < 0 { app.move(.up) } else { app.move(.down) } + } +} + fn (mut app App) on_key_down(key sapp.KeyCode) { // these keys are independent from the game state: match key { .escape { exit(0) - } - .n { + } .n { app.new_game() - } - // .t {/* fast setup for a victory situation: */ app.board = new_board(['JJ@@', '@@@@', '@@@@', '@@@@'])} - .backspace { + } .backspace { if app.undo.len > 0 { app.state = .play app.board = app.undo.pop() app.moves-- return } - } - else {} + } .enter { + app.tile_format = int(app.tile_format) + 1 + if app.tile_format == .end_ { + app.tile_format = .normal + } + } .j { + app.game_over() + } .t { + app.set_theme(if app.theme_idx == themes.len - 1 { 0 } else { app.theme_idx + 1 }) + } 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() - }) } + .w, .up { app.move(.up) } + .a, .left { app.move(.left) } + .s, .down { app.move(.down) } + .d, .right { app.move(.right) } else {} } } } -// fn on_event(e &sapp.Event, mut app App) { - if e.typ == .key_down { - app.on_key_down(e.key_code) + match e.typ { + .key_down { + app.on_key_down(e.key_code) + } .resized, .restored, .resumed { + app.resize() + } .touches_began { + if e.num_touches > 0 { + t := e.touches[0] + app.touch.start_pos = { x: int(t.pos_x), y: int(t.pos_y) } + } + } .touches_ended { + if e.num_touches > 0 { + t := e.touches[0] + end_pos := Pos{ x: int(t.pos_x), y: int(t.pos_y) } + app.handle_swipe(app.touch.start_pos, end_pos) + } + } else {} } } + fn frame(mut app App) { - app.update_tickers() + $if showfps? { app.perf.frame_sw.restart() } app.gg.begin() + app.update_tickers() app.draw() app.gg.end() + $if showfps? { app.showfps() } +} + +fn init(mut app App) { + app.resize() + $if showfps? { + app.perf.frame_sw.restart() + app.perf.second_sw.restart() + } +} + +fn (mut app App) showfps() { + println(app.perf.frame_sw.elapsed().microseconds()) + app.perf.frame++ + f := app.perf.frame + if (f & 127) == 0 { + last_frame_us := app.perf.frame_sw.elapsed().microseconds() + ticks := f64(app.perf.second_sw.elapsed().milliseconds()) + fps := f64(app.perf.frame - app.perf.frame_old) * ticks / 1000 / 4.5 + last_fps := 128_000.0 / ticks + eprintln('frame ${f:-5} | avg. fps: ${fps:-5.1f} | avg. last 128 fps: ${last_fps:-5.1f} | last frame time: ${last_frame_us:-4}µs') + app.perf.second_sw.restart() + app.perf.frame_old = f + } +} + +// TODO: Move this somewhere else (vlib?) once Android support is merged +$if android { + #include + #define LOG_TAG "v_logcat_test" + #define printf(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) + #define fprintf(a, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) } fn main() { - mut app := &App{ - gg: 0 - state: .play - } + mut app := &App{} app.new_game() + + mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) + $if android { + font_path = 'assets/RobotoMono-Regular.ttf' + } + + mut window_title := 'V 2048' + // TODO: Make emcc a real platform ifdef + $if emscripten? { + // in emscripten, sokol uses `window_title` as the selector to the canvas it'll render to, + // and since `document.querySelector('V 2048')` isn't valid JS, we use `canvas` instead + window_title = 'canvas' + } + + $if showfps? { + app.perf = &Perf{} + } + app.gg = gg.new_context({ - bg_color: gx.white - width: window_width - height: window_height - use_ortho: true + bg_color: app.theme.bg_color + width: default_window_width + height: default_window_height create_window: true window_title: window_title frame_fn: frame event_fn: on_event + init_fn: init user_data: app - font_path: os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf') + font_path: font_path }) - app.load_tiles() - app.victory_image = app.new_image('victory.png') app.gg.run() -} +} \ No newline at end of file diff --git a/examples/2048/README.md b/examples/2048/README.md index d5371def92..e5cd4844e0 100644 --- a/examples/2048/README.md +++ b/examples/2048/README.md @@ -1,7 +1,7 @@ # 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) +![screenshot](demo.png) ## Description: Merge tiles by moving them. @@ -12,8 +12,10 @@ The goal of the game is to create a tile with a value of 2048. Escape - exit the game Backspace - undo last move n - restart the game +t - toggle the UI theme +Enter - toggle the tile text format -UP,LEFT,DOWN,RIGHT or W,A,S,D - move the tiles +UP,LEFT,DOWN,RIGHT / W,A,S,D / touchscreen swipes - 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 deleted file mode 100644 index e4cd6d7497204e529630ff46ee80e8ff466fecc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8548 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 Vzk@?xe||tvlvR_dmij07zX5v9<)Q!p diff --git a/examples/2048/assets/1024.png b/examples/2048/assets/1024.png deleted file mode 100644 index 2b8da8d5b2234f069959614d13c9ad301da4695f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2158 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 K(XNE(2mS|IQr^M< diff --git a/examples/2048/assets/2.png b/examples/2048/assets/2.png deleted file mode 100644 index e1de4367f03c0e3da526bc32b795f077517d45bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 diff --git a/examples/2048/assets/2048.png b/examples/2048/assets/2048.png deleted file mode 100644 index 5cc1469cee115dc8f398f93558f931c8faa946c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2686 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 BaaaHV diff --git a/examples/2048/assets/4.png b/examples/2048/assets/4.png deleted file mode 100644 index faa6476104781adf2512d0c12377b2a0ba5426cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 diff --git a/examples/2048/assets/512.png b/examples/2048/assets/512.png deleted file mode 100644 index ed25482b394c8f7c8f24734b1c274018239c189b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 diff --git a/examples/2048/assets/64.png b/examples/2048/assets/64.png deleted file mode 100644 index 28d5d4af3668710be17cc592ec2575ce1e84826d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 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 diff --git a/examples/2048/assets/8.png b/examples/2048/assets/8.png deleted file mode 100644 index 6e69a1e954719907ef49d7d8644bec7b7ed42d38..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1534 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 NP}jXzq4qfR-vE4yrXm0U literal 0 HcmV?d00001