examples/2048: run `v -w fmt 2048.v`

pull/6490/head
Delyan Angelov 2020-09-27 22:47:26 +03:00
parent f9ce7f3542
commit 71e1ca72ec
1 changed files with 257 additions and 205 deletions

View File

@ -55,18 +55,18 @@ const (
game_over_color: gx.rgb(190, 50, 50) game_over_color: gx.rgb(190, 50, 50)
text_color: gx.black text_color: gx.black
tile_colors: [ tile_colors: [
gx.rgb(205, 193, 180) // Empty / 0 tile gx.rgb(205, 193, 180), // Empty / 0 tile
gx.rgb(238, 228, 218) // 2 gx.rgb(238, 228, 218), // 2
gx.rgb(237, 224, 200) // 4 gx.rgb(237, 224, 200), // 4
gx.rgb(242, 177, 121) // 8 gx.rgb(242, 177, 121), // 8
gx.rgb(245, 149, 99) // 16 gx.rgb(245, 149, 99), // 16
gx.rgb(246, 124, 95) // 32 gx.rgb(246, 124, 95), // 32
gx.rgb(246, 94, 59) // 64 gx.rgb(246, 94, 59), // 64
gx.rgb(237, 207, 114) // 128 gx.rgb(237, 207, 114), // 128
gx.rgb(237, 204, 97) // 256 gx.rgb(237, 204, 97), // 256
gx.rgb(237, 200, 80) // 512 gx.rgb(237, 200, 80), // 512
gx.rgb(237, 197, 63) // 1024 gx.rgb(237, 197, 63), // 1024
gx.rgb(237, 194, 46) // 2048 gx.rgb(237, 194, 46), // 2048
] ]
}, },
&Theme{ &Theme{
@ -76,18 +76,18 @@ const (
game_over_color: gx.rgb(190, 50, 50) game_over_color: gx.rgb(190, 50, 50)
text_color: gx.white text_color: gx.white
tile_colors: [ tile_colors: [
gx.rgb(123, 115, 108) gx.rgb(123, 115, 108),
gx.rgb(142, 136, 130) gx.rgb(142, 136, 130),
gx.rgb(142, 134, 120) gx.rgb(142, 134, 120),
gx.rgb(145, 106, 72) gx.rgb(145, 106, 72),
gx.rgb(147, 89, 59) gx.rgb(147, 89, 59),
gx.rgb(147, 74, 57) gx.rgb(147, 74, 57),
gx.rgb(147, 56, 35) gx.rgb(147, 56, 35),
gx.rgb(142, 124, 68) gx.rgb(142, 124, 68),
gx.rgb(142, 122, 58) gx.rgb(142, 122, 58),
gx.rgb(142, 120, 48) gx.rgb(142, 120, 48),
gx.rgb(142, 118, 37) gx.rgb(142, 118, 37),
gx.rgb(142, 116, 27) gx.rgb(142, 116, 27),
] ]
}, },
&Theme{ &Theme{
@ -97,18 +97,18 @@ const (
game_over_color: gx.rgb(190, 50, 50) game_over_color: gx.rgb(190, 50, 50)
text_color: gx.white text_color: gx.white
tile_colors: [ tile_colors: [
gx.rgb(92, 86, 140) gx.rgb(92, 86, 140),
gx.rgb(106, 99, 169) gx.rgb(106, 99, 169),
gx.rgb(106, 97, 156) gx.rgb(106, 97, 156),
gx.rgb(108, 79, 93) gx.rgb(108, 79, 93),
gx.rgb(110, 66, 76) gx.rgb(110, 66, 76),
gx.rgb(110, 55, 74) gx.rgb(110, 55, 74),
gx.rgb(110, 42, 45) gx.rgb(110, 42, 45),
gx.rgb(106, 93, 88) gx.rgb(106, 93, 88),
gx.rgb(106, 91, 75) gx.rgb(106, 91, 75),
gx.rgb(106, 90, 62) gx.rgb(106, 90, 62),
gx.rgb(106, 88, 48) gx.rgb(106, 88, 48),
gx.rgb(106, 87, 35) gx.rgb(106, 87, 35),
] ]
}, },
] ]
@ -203,17 +203,29 @@ enum Direction {
// Utility functions // Utility functions
[inline] [inline]
fn min(a, b int) int { fn min(a, b int) int {
if a < b { return a } else { return b } if a < b {
return a
} else {
return b
}
} }
[inline] [inline]
fn max(a, b int) int { fn max(a, b int) int {
if a > b { return a } else { return b } if a > b {
return a
} else {
return b
}
} }
[inline] [inline]
fn abs(a int) int { fn abs(a int) int {
if a < 0 { return -a } else { return a } if a < 0 {
return -a
} else {
return a
}
} }
[inline] [inline]
@ -250,7 +262,11 @@ fn (t TileLine) to_left() TileLine {
mut nonzeros := 0 mut nonzeros := 0
// gather meta info about the line: // gather meta info about the line:
for x in res.field { for x in res.field {
if x == 0 { zeros++ } else { nonzeros++ } if x == 0 {
zeros++
} else {
nonzeros++
}
} }
if nonzeros == 0 { if nonzeros == 0 {
// when all the tiles are empty, there is nothing left to do // when all the tiles are empty, there is nothing left to do
@ -290,7 +306,9 @@ fn (t TileLine) to_left() TileLine {
fn (b Board) to_left() Board { fn (b Board) to_left() Board {
mut res := b mut res := b
for y in 0 .. 4 { for y in 0 .. 4 {
mut hline := TileLine{ypos: y} mut hline := TileLine{
ypos: y
}
for x in 0 .. 4 { for x in 0 .. 4 {
hline.field[x] = b.field[y][x] hline.field[x] = b.field[y][x]
} }
@ -330,10 +348,10 @@ fn (mut b Board) is_game_over() bool {
// there are remaining zeros // there are remaining zeros
return false return false
} }
if (x > 0 && fidx == b.field[y][x - 1]) if (x > 0 && fidx == b.field[y][x - 1]) ||
|| (x < 4 - 1 && fidx == b.field[y][x + 1]) (x < 4 - 1 && fidx == b.field[y][x + 1]) ||
|| (y > 0 && fidx == b.field[y - 1][x]) (y > 0 && fidx == b.field[y - 1][x]) ||
|| (y < 4 - 1 && fidx == b.field[y + 1][x]) { (y < 4 - 1 && fidx == b.field[y + 1][x]) {
// there are remaining merges // there are remaining merges
return false return false
} }
@ -425,7 +443,9 @@ fn (mut app App) new_random_tile() {
if random_value > 0 { if random_value > 0 {
app.atickers[empty_pos.y][empty_pos.x] = animation_length app.atickers[empty_pos.y][empty_pos.x] = animation_length
} }
if app.state != .freeplay { app.check_for_victory() } if app.state != .freeplay {
app.check_for_victory()
}
app.check_for_game_over() app.check_for_game_over()
} }
@ -469,17 +489,23 @@ fn (mut app App) ai_move() {
for _ in 0 .. predictions_per_move { for _ in 0 .. predictions_per_move {
mut cboard := app.board mut cboard := app.board
cboard, is_valid = cboard.move(move) cboard, is_valid = cboard.move(move)
if !is_valid || cboard.is_game_over() { continue } if !is_valid || cboard.is_game_over() {
continue
}
mpoints += cboard.points mpoints += cboard.points
cboard.place_random_tile() cboard.place_random_tile()
mut cmoves := 0 mut cmoves := 0
for !cboard.is_game_over() { for !cboard.is_game_over() {
nmove := possible_moves[rand.intn(possible_moves.len)] nmove := possible_moves[rand.intn(possible_moves.len)]
cboard, is_valid = cboard.move(nmove) cboard, is_valid = cboard.move(nmove)
if !is_valid { continue } if !is_valid {
continue
}
cboard.place_random_tile() cboard.place_random_tile()
cmoves++ cmoves++
if cmoves > prediction_depth { break } if cmoves > prediction_depth {
break
}
} }
mpoints += cboard.points mpoints += cboard.points
mshifts += cboard.shifts mshifts += cboard.shifts
@ -489,7 +515,9 @@ fn (mut app App) ai_move() {
predictions[move_idx].mcmoves = f64(mcmoves) / predictions_per_move predictions[move_idx].mcmoves = f64(mcmoves) / predictions_per_move
} }
think_time := think_watch.elapsed().milliseconds() think_time := think_watch.elapsed().milliseconds()
mut bestprediction := Prediction{mpoints:-1} mut bestprediction := Prediction{
mpoints: -1
}
for move_idx in 0 .. possible_moves.len { for move_idx in 0 .. possible_moves.len {
if bestprediction.mpoints < predictions[move_idx].mpoints { if bestprediction.mpoints < predictions[move_idx].mpoints {
bestprediction = predictions[move_idx] bestprediction = predictions[move_idx]
@ -501,47 +529,48 @@ fn (mut app App) ai_move() {
fn (app &App) label_format(kind LabelKind) gx.TextCfg { fn (app &App) label_format(kind LabelKind) gx.TextCfg {
match kind { match kind {
.points { .points { return {
return { color: if app.state in [.over, .victory] {
color: if app.state in [.over, .victory] { gx.white } else { app.theme.text_color } gx.white
} else {
app.theme.text_color
}
align: .left align: .left
size: app.ui.font_size / 2 size: app.ui.font_size / 2
} }
.moves { return {
color: if app.state in [.over, .victory] {
gx.white
} else {
app.theme.text_color
} }
} .moves {
return {
color: if app.state in [.over, .victory] { gx.white } else { app.theme.text_color }
align: .right align: .right
size: app.ui.font_size / 2 size: app.ui.font_size / 2
} } }
} .tile { .tile { return {
return {
color: app.theme.text_color color: app.theme.text_color
align: .center align: .center
vertical_align: .middle vertical_align: .middle
size: app.ui.font_size size: app.ui.font_size
} } }
} .victory { .victory { return {
return {
color: app.theme.victory_color color: app.theme.victory_color
align: .center align: .center
vertical_align: .middle vertical_align: .middle
size: app.ui.font_size * 2 size: app.ui.font_size * 2
} } }
} .game_over { .game_over { return {
return {
color: app.theme.game_over_color color: app.theme.game_over_color
align: .center align: .center
vertical_align: .middle vertical_align: .middle
size: app.ui.font_size * 2 size: app.ui.font_size * 2
} } }
} .score_end { .score_end { return {
return {
color: gx.white color: gx.white
align: .center align: .center
vertical_align: .middle vertical_align: .middle
size: app.ui.font_size * 3 / 4 size: app.ui.font_size * 3 / 4
} } }
}
} }
} }
@ -555,11 +584,12 @@ fn (mut app App) set_theme(idx int) {
fn (mut app App) resize() { fn (mut app App) resize() {
mut s := sapp.dpi_scale() mut s := sapp.dpi_scale()
if s == 0.0 { s = 1.0 } if s == 0.0 {
s = 1.0
}
w := int(sapp.width() / s) w := int(sapp.width() / s)
h := int(sapp.height() / s) h := int(sapp.height() / s)
m := f32(min(w, h)) m := f32(min(w, h))
app.ui.dpi_scale = s app.ui.dpi_scale = s
app.ui.window_width = w app.ui.window_width = w
app.ui.window_height = h app.ui.window_height = h
@ -568,7 +598,6 @@ fn (mut app App) resize() {
app.ui.border_size = app.ui.padding_size * 2 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.tile_size = int((m - app.ui.padding_size * 5 - app.ui.border_size * 2) / 4)
app.ui.font_size = int(m / 10) app.ui.font_size = int(m / 10)
// If the window's height is greater than its width, center the board vertically. // If the window's height is greater than its width, center the board vertically.
// If not, center it horizontally // If not, center it horizontally
if w > h { if w > h {
@ -587,29 +616,28 @@ fn (app &App) draw() {
m := min(ww, wh) m := min(ww, wh)
labelx := xpad + app.ui.border_size labelx := xpad + app.ui.border_size
labely := ypad + app.ui.border_size / 2 labely := ypad + app.ui.border_size / 2
app.draw_tiles() app.draw_tiles()
// TODO: Make transparency work in `gg` // TODO: Make transparency work in `gg`
if app.state == .over { if app.state == .over {
app.gg.draw_rect(0, 0, ww, wh, gx.rgba(10, 0, 0, 180)) app.gg.draw_rect(0, 0, ww, wh, gx.rgba(10, 0, 0, 180))
app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Game Over', app.label_format(.game_over)) app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Game Over', app.label_format(.game_over))
f := app.label_format(.tile) f := app.label_format(.tile)
msg := $if android { 'Tap to restart' } $else { 'Press `r` to restart' } msg := $if android { 'Tap to restart' } $else { 'Press `r` to restart' }
app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg, { f | color: gx.white, size: f.size * 3 / 4 }) app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg, {
f |
color: gx.white
size: f.size * 3 / 4
})
} }
if app.state == .victory { if app.state == .victory {
app.gg.draw_rect(0, 0, ww, wh, gx.rgba(0, 10, 0, 180)) app.gg.draw_rect(0, 0, ww, wh, gx.rgba(0, 10, 0, 180))
app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Victory!', app.label_format(.victory)) app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Victory!', app.label_format(.victory))
// f := app.label_format(.tile) // f := app.label_format(.tile)
msg1 := $if android { 'Tap to continue' } $else { 'Press `space` to continue' } msg1 := $if android { 'Tap to continue' } $else { 'Press `space` to continue' }
msg2 := $if android { 'Tap to restart' } $else { 'Press `r` to restart' } msg2 := $if android { 'Tap to restart' } $else { 'Press `r` to restart' }
app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg1, app.label_format(.score_end)) app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg1, app.label_format(.score_end))
app.gg.draw_text(ww / 2, (m * 8 / 10) + ypad, msg2, app.label_format(.score_end)) app.gg.draw_text(ww / 2, (m * 8 / 10) + ypad, msg2, app.label_format(.score_end))
} }
// Draw at the end, so that it's on top of the victory / game over overlays // Draw at the end, so that it's on top of the victory / game over overlays
app.gg.draw_text(labelx, labely, 'Points: $app.board.points', app.label_format(.points)) 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)) app.gg.draw_text(ww - labelx, labely, 'Moves: $app.moves', app.label_format(.moves))
@ -618,10 +646,8 @@ fn (app &App) draw() {
fn (app &App) draw_tiles() { fn (app &App) draw_tiles() {
xstart := app.ui.x_padding + app.ui.border_size xstart := app.ui.x_padding + app.ui.border_size
ystart := app.ui.y_padding + app.ui.border_size + app.ui.header_size ystart := app.ui.y_padding + app.ui.border_size + app.ui.header_size
toffset := app.ui.tile_size + app.ui.padding_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 tiles_size := min(app.ui.window_width, app.ui.window_height) - app.ui.border_size * 2
// Draw the padding around the tiles // Draw the padding around the tiles
app.gg.draw_rect(xstart, ystart, tiles_size, tiles_size, app.theme.padding_color) app.gg.draw_rect(xstart, ystart, tiles_size, tiles_size, app.theme.padding_color)
// Draw the actual tiles // Draw the actual tiles
@ -640,27 +666,39 @@ fn (app &App) draw_tiles() {
xoffset := xstart + app.ui.padding_size + x * toffset + (app.ui.tile_size - tw) / 2 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 yoffset := ystart + app.ui.padding_size + y * toffset + (app.ui.tile_size - th) / 2
app.gg.draw_rect(xoffset, yoffset, tw, th, tile_color) app.gg.draw_rect(xoffset, yoffset, tw, th, tile_color)
if tidx != 0 { // 0 == blank spot if tidx != 0 { // 0 == blank spot
xpos := xoffset + tw / 2 xpos := xoffset + tw / 2
ypos := yoffset + th / 2 ypos := yoffset + th / 2
mut fmt := app.label_format(.tile) mut fmt := app.label_format(.tile)
fmt = { fmt | size: int(f32(fmt.size - 1) / animation_length * anim_size) } fmt = {
fmt |
size: int(f32(fmt.size - 1) / animation_length * anim_size)
}
match app.tile_format { match app.tile_format {
.normal { .normal {
app.gg.draw_text(xpos, ypos, '${1<<tidx}', fmt) app.gg.draw_text(xpos, ypos, '${1<<tidx}', fmt)
} .log { }
.log {
app.gg.draw_text(xpos, ypos, '$tidx', fmt) app.gg.draw_text(xpos, ypos, '$tidx', fmt)
} .exponent { }
.exponent {
app.gg.draw_text(xpos, ypos, '2', fmt) app.gg.draw_text(xpos, ypos, '2', fmt)
fs2 := int(f32(fmt.size) * 0.67) fs2 := int(f32(fmt.size) * 0.67)
app.gg.draw_text(xpos + app.ui.tile_size / 10, ypos - app.ui.tile_size / 8, app.gg.draw_text(xpos + app.ui.tile_size / 10, ypos - app.ui.tile_size / 8,
'$tidx', { fmt | size: fs2, align: gx.HorizontalAlign.left }) '$tidx', {
} .shifts { fmt |
size: fs2
align: gx.HorizontalAlign.left
})
}
.shifts {
fs2 := int(f32(fmt.size) * 0.6) fs2 := int(f32(fmt.size) * 0.6)
app.gg.draw_text(xpos, ypos, '2<<${tidx - 1}', { fmt | size: fs2 }) app.gg.draw_text(xpos, ypos, '2<<${tidx-1}', {
} .none_ {} // Don't draw any text here, colors only fmt |
size: fs2
})
}
.none_ {} // Don't draw any text here, colors only
.end_ {} // Should never get here .end_ {} // Should never get here
} }
} }
@ -671,7 +709,6 @@ fn (app &App) draw_tiles() {
fn (mut app App) handle_touches() { fn (mut app App) handle_touches() {
s, e := app.touch.start, app.touch.end s, e := app.touch.start, app.touch.end
adx, ady := abs(e.pos.x - s.pos.x), abs(e.pos.y - s.pos.y) adx, ady := abs(e.pos.x - s.pos.x), abs(e.pos.y - s.pos.y)
if max(adx, ady) < 10 { if max(adx, ady) < 10 {
app.handle_tap() app.handle_tap()
} else { } else {
@ -685,13 +722,15 @@ fn (mut app App) handle_tap() {
m := min(w, h) m := min(w, h)
s, e := app.touch.start, app.touch.end s, e := app.touch.start, app.touch.end
avgx, avgy := avg(s.pos.x, e.pos.x), avg(s.pos.y, e.pos.y) avgx, avgy := avg(s.pos.x, e.pos.x), avg(s.pos.y, e.pos.y)
// TODO: Replace "touch spots" with actual buttons // TODO: Replace "touch spots" with actual buttons
// bottom left -> change theme // bottom left -> change theme
if avgx < 200 && h - avgy < 200 { app.next_theme() } if avgx < 200 && h - avgy < 200 {
app.next_theme()
}
// bottom right -> change tile format // bottom right -> change tile format
if w - avgx < 200 && h - avgy < 200 { app.next_tile_format() } if w - avgx < 200 && h - avgy < 200 {
app.next_tile_format()
}
if app.state == .victory { if app.state == .victory {
if avgy > (m / 2) + ypad { if avgy > (m / 2) + ypad {
if avgy < (m * 7 / 10) + ypad { if avgy < (m * 7 / 10) + ypad {
@ -712,8 +751,9 @@ fn (mut app App) handle_tap() {
fn (mut app App) handle_swipe() { fn (mut app App) handle_swipe() {
// Currently, swipes are only used to move the tiles. // Currently, swipes are only used to move the tiles.
// If the user's not playing, exit early to avoid all the unnecessary calculations // If the user's not playing, exit early to avoid all the unnecessary calculations
if app.state !in [.play, .freeplay] { return } if app.state !in [.play, .freeplay] {
return
}
s, e := app.touch.start, app.touch.end s, e := app.touch.start, app.touch.end
w, h := app.ui.window_width, app.ui.window_height w, h := app.ui.window_width, app.ui.window_height
dx, dy := e.pos.x - s.pos.x, e.pos.y - s.pos.y dx, dy := e.pos.x - s.pos.x, e.pos.y - s.pos.y
@ -721,23 +761,38 @@ fn (mut app App) handle_swipe() {
dmin := if min(adx, ady) > 0 { min(adx, ady) } else { 1 } dmin := if min(adx, ady) > 0 { min(adx, ady) } else { 1 }
dmax := if max(adx, ady) > 0 { max(adx, ady) } else { 1 } dmax := if max(adx, ady) > 0 { max(adx, ady) } else { 1 }
tdiff := int(e.time.unix_time_milli() - s.time.unix_time_milli()) tdiff := int(e.time.unix_time_milli() - s.time.unix_time_milli())
// TODO: make this calculation more accurate (don't use arbitrary numbers) // TODO: make this calculation more accurate (don't use arbitrary numbers)
min_swipe_distance := int(math.sqrt(min(w, h) * tdiff / 60)) + 20 min_swipe_distance := int(math.sqrt(min(w, h) * tdiff / 60)) + 20
if dmax < min_swipe_distance {
if dmax < min_swipe_distance { return } // Swipe was too short return
if dmax / dmin < 2 { return } // Swiped diagonally }
// Swipe was too short
if dmax / dmin < 2 {
return
}
// Swiped diagonally
if adx > ady { if adx > ady {
if dx < 0 { app.move(.left) } else { app.move(.right) } if dx < 0 {
app.move(.left)
} else { } else {
if dy < 0 { app.move(.up) } else { app.move(.down) } app.move(.right)
}
} else {
if dy < 0 {
app.move(.up)
} else {
app.move(.down)
}
} }
} }
[inline] [inline]
fn (mut app App) next_theme() { fn (mut app App) next_theme() {
app.set_theme(if app.theme_idx == themes.len - 1 { 0 } else { app.theme_idx + 1 }) app.set_theme(if app.theme_idx == themes.len - 1 {
0
} else {
app.theme_idx + 1
})
} }
[inline] [inline]
@ -758,27 +813,18 @@ fn (mut app App) undo() {
} }
} }
fn (mut app App) on_key_down(key sapp.KeyCode) { fn (mut app App) on_key_down(key sapp.KeyCode) {
// these keys are independent from the game state: // these keys are independent from the game state:
match key { match key {
.a { .a { app.is_ai_mode = !app.is_ai_mode }
app.is_ai_mode = !app.is_ai_mode .escape { exit(0) }
} .escape { .n, .r { app.new_game() }
exit(0) .backspace { app.undo() }
} .n, .r { .enter { app.next_tile_format() }
app.new_game() .j { app.state = .over }
} .backspace { .t { app.next_theme() }
app.undo() else {}
} .enter {
app.next_tile_format()
} .j {
app.state = .over
} .t {
app.next_theme()
} else {}
} }
if app.state in [.play, .freeplay] { if app.state in [.play, .freeplay] {
match key { match key {
.w, .up { app.move(.up) } .w, .up { app.move(.up) }
@ -788,9 +834,10 @@ fn (mut app App) on_key_down(key sapp.KeyCode) {
else {} else {}
} }
} }
if app.state == .victory { if app.state == .victory {
if key == .space { app.state = .freeplay } if key == .space {
app.state = .freeplay
}
} }
} }
@ -798,54 +845,62 @@ fn on_event(e &sapp.Event, mut app App) {
match e.typ { match e.typ {
.key_down { .key_down {
app.on_key_down(e.key_code) app.on_key_down(e.key_code)
} .resized, .restored, .resumed { }
.resized, .restored, .resumed {
app.resize() app.resize()
} .touches_began { }
.touches_began {
if e.num_touches > 0 { if e.num_touches > 0 {
t := e.touches[0] t := e.touches[0]
app.touch.start = { app.touch.start = {
pos: { pos: {
x: int(t.pos_x / app.ui.dpi_scale), x: int(t.pos_x / app.ui.dpi_scale)
y: int(t.pos_y / app.ui.dpi_scale) y: int(t.pos_y / app.ui.dpi_scale)
}, }
time: time.now() time: time.now()
} }
} }
} .touches_ended { }
.touches_ended {
if e.num_touches > 0 { if e.num_touches > 0 {
t := e.touches[0] t := e.touches[0]
app.touch.end = { app.touch.end = {
pos: { pos: {
x: int(t.pos_x / app.ui.dpi_scale), x: int(t.pos_x / app.ui.dpi_scale)
y: int(t.pos_y / app.ui.dpi_scale) y: int(t.pos_y / app.ui.dpi_scale)
}, }
time: time.now() time: time.now()
} }
app.handle_touches() app.handle_touches()
} }
} .mouse_down { }
.mouse_down {
app.touch.start = { app.touch.start = {
pos: { pos: {
x: int(e.mouse_x / app.ui.dpi_scale), x: int(e.mouse_x / app.ui.dpi_scale)
y: int(e.mouse_y / app.ui.dpi_scale) y: int(e.mouse_y / app.ui.dpi_scale)
}, }
time: time.now() time: time.now()
} }
} .mouse_up { }
.mouse_up {
app.touch.end = { app.touch.end = {
pos: { pos: {
x: int(e.mouse_x / app.ui.dpi_scale), x: int(e.mouse_x / app.ui.dpi_scale)
y: int(e.mouse_y / app.ui.dpi_scale) y: int(e.mouse_y / app.ui.dpi_scale)
}, }
time: time.now() time: time.now()
} }
app.handle_touches() app.handle_touches()
} else {} }
else {}
} }
} }
fn frame(mut app App) { fn frame(mut app App) {
$if showfps? { app.perf.frame_sw.restart() } $if showfps ? {
app.perf.frame_sw.restart()
}
app.gg.begin() app.gg.begin()
app.update_tickers() app.update_tickers()
app.draw() app.draw()
@ -853,7 +908,9 @@ fn frame(mut app App) {
if app.is_ai_mode && app.state in [.play, .freeplay] && app.perf.frame % frames_per_ai_move == 0 { if app.is_ai_mode && app.state in [.play, .freeplay] && app.perf.frame % frames_per_ai_move == 0 {
app.ai_move() app.ai_move()
} }
$if showfps? { app.showfps() } $if showfps ? {
app.showfps()
}
app.gg.end() app.gg.end()
} }
@ -872,7 +929,7 @@ fn (mut app App) showfps() {
last_frame_us := app.perf.frame_sw.elapsed().microseconds() last_frame_us := app.perf.frame_sw.elapsed().microseconds()
ticks := f64(app.perf.second_sw.elapsed().milliseconds()) ticks := f64(app.perf.second_sw.elapsed().milliseconds())
fps := f64(app.perf.frame - app.perf.frame_old) * ticks / 1000 / 4.5 fps := f64(app.perf.frame - app.perf.frame_old) * ticks / 1000 / 4.5
last_fps := 128_000.0 / ticks last_fps := 128000.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') 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.second_sw.restart()
app.perf.frame_old = f app.perf.frame_old = f
@ -886,16 +943,13 @@ $if android {
#define printf(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #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__) #define fprintf(a, ...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
} }
fn main() { fn main() {
mut app := &App{} mut app := &App{}
app.new_game() app.new_game()
mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf'))
$if android { $if android {
font_path = 'fonts/RobotoMono-Regular.ttf' font_path = 'fonts/RobotoMono-Regular.ttf'
} }
mut window_title := 'V 2048' mut window_title := 'V 2048'
// TODO: Make emcc a real platform ifdef // TODO: Make emcc a real platform ifdef
$if emscripten ? { $if emscripten ? {
@ -903,9 +957,7 @@ fn main() {
// and since `document.querySelector('V 2048')` isn't valid JS, we use `canvas` instead // and since `document.querySelector('V 2048')` isn't valid JS, we use `canvas` instead
window_title = 'canvas' window_title = 'canvas'
} }
app.perf = &Perf{} app.perf = &Perf{}
app.gg = gg.new_context({ app.gg = gg.new_context({
bg_color: app.theme.bg_color bg_color: app.theme.bg_color
width: default_window_width width: default_window_width