mirror of
https://github.com/vlang/v.git
synced 2025-08-03 17:57:59 -04:00
136 lines
3.2 KiB
V
136 lines
3.2 KiB
V
module main
|
|
|
|
import gg
|
|
import gx
|
|
import rand
|
|
import time
|
|
|
|
const cover = gx.rgba(85, 200, 85, 255)
|
|
const csize = 120 // cell size in pixels
|
|
const letters = 'AABBOOCCVVXXYYZZMMKKHHTT'.split('')
|
|
const header_size = 30
|
|
|
|
struct Cell {
|
|
mut:
|
|
is_open bool
|
|
letter string
|
|
}
|
|
|
|
struct Game {
|
|
mut:
|
|
ctx &gg.Context = unsafe { nil }
|
|
cells []Cell
|
|
card1_idx ?int
|
|
card2_idx ?int
|
|
size int // in cells
|
|
remaining int
|
|
sw time.StopWatch = time.new_stopwatch()
|
|
revert_sw time.StopWatch = time.new_stopwatch(auto_start: false)
|
|
}
|
|
|
|
fn (mut g Game) restart() {
|
|
ncells := g.size * g.size
|
|
g.remaining = ncells
|
|
g.cells = []Cell{len: ncells, init: Cell{
|
|
letter: letters[index % letters.len]
|
|
}}
|
|
rand.shuffle(mut g.cells) or {}
|
|
g.sw = time.new_stopwatch()
|
|
g.card1_idx = none
|
|
g.card2_idx = none
|
|
}
|
|
|
|
fn (mut g Game) draw_cell(i int, cell Cell) {
|
|
x, y := i % g.size, i / g.size
|
|
rect_x, rect_y := x * csize, header_size + y * csize
|
|
if g.cells[i].is_open || g.sw.elapsed().milliseconds() <= 1000 {
|
|
lsize := 96
|
|
g.ctx.draw_rect_empty(rect_x + 6, rect_y + 6, csize - 10, csize - 10, gx.light_gray)
|
|
g.ctx.draw_text(rect_x + csize / 2 - lsize / 3, rect_y + csize / 2 - lsize / 2,
|
|
g.cells[i].letter, color: gx.yellow, size: lsize)
|
|
} else {
|
|
g.ctx.draw_rect_filled(rect_x + 6, rect_y + 6, csize - 10, csize - 10, cover)
|
|
}
|
|
}
|
|
|
|
fn on_frame(mut g Game) {
|
|
ws := gg.window_size()
|
|
g.ctx.begin()
|
|
message := '(r)estart (esc)ape | remaining: ${g.remaining:02} | time: ${f64(g.sw.elapsed().milliseconds()) / 1000.0:06.1f}s'
|
|
g.ctx.draw_text(ws.width / 2, 7, message, color: gx.light_gray, size: 22, align: .center)
|
|
for i, cell in g.cells {
|
|
g.draw_cell(i, cell)
|
|
}
|
|
if g.revert_sw.elapsed().milliseconds() > 750 {
|
|
g.revert_sw = time.new_stopwatch(auto_start: false)
|
|
if g.card1_idx != none {
|
|
if g.card2_idx != none {
|
|
g.cells[g.card1_idx].is_open = false
|
|
g.cells[g.card2_idx].is_open = false
|
|
g.card1_idx = none
|
|
g.card2_idx = none
|
|
g.remaining = g.cells.count(!it.is_open)
|
|
}
|
|
}
|
|
}
|
|
g.ctx.end()
|
|
}
|
|
|
|
fn on_event(e &gg.Event, mut g Game) {
|
|
if e.typ == .key_down {
|
|
match e.key_code {
|
|
.escape { g.ctx.quit() }
|
|
.r { g.restart() }
|
|
else {}
|
|
}
|
|
return
|
|
}
|
|
if e.typ != .mouse_down {
|
|
return
|
|
}
|
|
x, y := int(e.mouse_x / csize), int((e.mouse_y - header_size) / csize)
|
|
if e.mouse_button == .left && g.card2_idx == none {
|
|
if g.remaining == 0 {
|
|
g.restart()
|
|
return
|
|
}
|
|
i := y * g.size + x
|
|
if !g.cells[i].is_open {
|
|
g.cells[i].is_open = true
|
|
if g.card1_idx == none {
|
|
g.card1_idx = i
|
|
} else {
|
|
g.card2_idx = i
|
|
if g.cells[g.card1_idx].letter == g.cells[i].letter {
|
|
g.card1_idx = none
|
|
g.card2_idx = none
|
|
} else {
|
|
// start a timer, that will be checked in the on_frame callback
|
|
g.revert_sw.start()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
g.remaining = g.cells.count(!it.is_open)
|
|
if g.remaining == 0 {
|
|
g.sw.stop()
|
|
}
|
|
}
|
|
|
|
mut g := &Game{
|
|
// the CLI argument should be number of pairs, so `size` is even, and the puzzle can be solved:
|
|
size: arguments()[1] or { '3' }.int() * 2
|
|
}
|
|
g.restart()
|
|
g.ctx = gg.new_context(
|
|
bg_color: gx.black
|
|
width: g.size * csize
|
|
height: header_size + g.size * csize
|
|
window_title: 'V Memory ${g.size} x ${g.size}'
|
|
user_data: g
|
|
frame_fn: on_frame
|
|
event_fn: on_event
|
|
sample_count: 2
|
|
)
|
|
g.ctx.run()
|