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 e4cd6d7497..0000000000 Binary files a/examples/2048/assets/1.png and /dev/null differ diff --git a/examples/2048/assets/1024.png b/examples/2048/assets/1024.png deleted file mode 100644 index 2b8da8d5b2..0000000000 Binary files a/examples/2048/assets/1024.png and /dev/null differ diff --git a/examples/2048/assets/128.png b/examples/2048/assets/128.png deleted file mode 100644 index 811a5b967e..0000000000 Binary files a/examples/2048/assets/128.png and /dev/null differ diff --git a/examples/2048/assets/16.png b/examples/2048/assets/16.png deleted file mode 100644 index 7b77193cc7..0000000000 Binary files a/examples/2048/assets/16.png and /dev/null differ diff --git a/examples/2048/assets/2.png b/examples/2048/assets/2.png deleted file mode 100644 index e1de4367f0..0000000000 Binary files a/examples/2048/assets/2.png and /dev/null differ diff --git a/examples/2048/assets/2048.png b/examples/2048/assets/2048.png deleted file mode 100644 index 5cc1469cee..0000000000 Binary files a/examples/2048/assets/2048.png and /dev/null differ diff --git a/examples/2048/assets/256.png b/examples/2048/assets/256.png deleted file mode 100644 index afe02713c5..0000000000 Binary files a/examples/2048/assets/256.png and /dev/null differ diff --git a/examples/2048/assets/32.png b/examples/2048/assets/32.png deleted file mode 100644 index 7d02f06e74..0000000000 Binary files a/examples/2048/assets/32.png and /dev/null differ diff --git a/examples/2048/assets/4.png b/examples/2048/assets/4.png deleted file mode 100644 index faa6476104..0000000000 Binary files a/examples/2048/assets/4.png and /dev/null differ diff --git a/examples/2048/assets/512.png b/examples/2048/assets/512.png deleted file mode 100644 index ed25482b39..0000000000 Binary files a/examples/2048/assets/512.png and /dev/null differ diff --git a/examples/2048/assets/64.png b/examples/2048/assets/64.png deleted file mode 100644 index 28d5d4af36..0000000000 Binary files a/examples/2048/assets/64.png and /dev/null differ diff --git a/examples/2048/assets/8.png b/examples/2048/assets/8.png deleted file mode 100644 index 6e69a1e954..0000000000 Binary files a/examples/2048/assets/8.png and /dev/null differ diff --git a/examples/2048/assets/victory.png b/examples/2048/assets/victory.png deleted file mode 100644 index 6d0f49b585..0000000000 Binary files a/examples/2048/assets/victory.png and /dev/null differ diff --git a/examples/2048/demo.png b/examples/2048/demo.png new file mode 100644 index 0000000000..4aed56ef46 Binary files /dev/null and b/examples/2048/demo.png differ