Skip to content

Commit d7061cb

Browse files
authored
examples: add a small memory game (#24643)
1 parent 366f3d6 commit d7061cb

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed

examples/gg/memory.v

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
module main
2+
3+
import gg
4+
import gx
5+
import rand
6+
import time
7+
8+
const cover = gx.rgba(85, 200, 85, 255)
9+
const csize = 120 // cell size in pixels
10+
const letters = 'AABBOOCCVVXXYYZZMMKKHHTT'.runes().map(it.str())
11+
const header_size = 30
12+
13+
struct Cell {
14+
mut:
15+
is_open bool
16+
letter string
17+
}
18+
19+
struct Game {
20+
mut:
21+
ctx &gg.Context = unsafe { nil }
22+
cells []Cell
23+
card1_idx ?int
24+
card2_idx ?int
25+
size int // in cells
26+
remaining int
27+
sw time.StopWatch = time.new_stopwatch()
28+
revert_sw time.StopWatch = time.new_stopwatch(auto_start: false)
29+
}
30+
31+
fn (mut g Game) restart() {
32+
ncells := g.size * g.size
33+
g.remaining = ncells
34+
g.cells = []Cell{len: ncells, init: Cell{
35+
letter: letters[index % letters.len]
36+
}}
37+
rand.shuffle(mut g.cells) or {}
38+
g.sw = time.new_stopwatch()
39+
g.card1_idx = none
40+
g.card2_idx = none
41+
}
42+
43+
fn (mut g Game) draw_cell(i int, cell Cell) {
44+
x, y := i % g.size, i / g.size
45+
rect_x, rect_y := x * csize, header_size + y * csize
46+
dt := g.sw.elapsed().milliseconds()
47+
if g.cells[i].is_open || dt <= 1000 {
48+
lsize := 96
49+
g.ctx.draw_rect_empty(rect_x + 6, rect_y + 6, csize - 10, csize - 10, gx.light_gray)
50+
g.ctx.draw_text(rect_x + csize / 2 - lsize / 3, rect_y + csize / 2 - lsize / 2,
51+
g.cells[i].letter, color: gx.yellow, size: lsize)
52+
} else {
53+
g.ctx.draw_rect_filled(rect_x + 6, rect_y + 6, csize - 10, csize - 10, cover)
54+
}
55+
}
56+
57+
fn on_frame(mut g Game) {
58+
ws := gg.window_size()
59+
g.ctx.begin()
60+
message := '(r)estart (esc)ape | remaining: ${g.remaining:02} | time: ${f64(g.sw.elapsed().milliseconds()) / 1000.0:06.1f}s'
61+
g.ctx.draw_text(ws.width / 2, 7, message, color: gx.light_gray, size: 22, align: .center)
62+
for i, cell in g.cells {
63+
g.draw_cell(i, cell)
64+
}
65+
dt := g.revert_sw.elapsed().milliseconds()
66+
if dt > 750 {
67+
g.revert_sw = time.new_stopwatch(auto_start: false)
68+
if g.card1_idx != none {
69+
if g.card2_idx != none {
70+
g.cells[g.card1_idx].is_open = false
71+
g.cells[g.card2_idx].is_open = false
72+
g.card1_idx = none
73+
g.card2_idx = none
74+
g.remaining = g.cells.count(!it.is_open)
75+
}
76+
}
77+
}
78+
g.ctx.end()
79+
}
80+
81+
fn on_event(e &gg.Event, mut g Game) {
82+
if e.typ == .key_down {
83+
match e.key_code {
84+
.escape { g.ctx.quit() }
85+
.r { g.restart() }
86+
else {}
87+
}
88+
return
89+
}
90+
if e.typ != .mouse_down {
91+
return
92+
}
93+
x, y := int(e.mouse_x / csize), int((e.mouse_y - header_size) / csize)
94+
if e.mouse_button == .left && g.card2_idx == none {
95+
if g.remaining == 0 {
96+
g.restart()
97+
return
98+
}
99+
i := y * g.size + x
100+
if !g.cells[i].is_open {
101+
g.cells[i].is_open = true
102+
if g.card1_idx == none {
103+
g.card1_idx = i
104+
} else {
105+
g.card2_idx = i
106+
if g.cells[g.card1_idx].letter == g.cells[i].letter {
107+
g.card1_idx = none
108+
g.card2_idx = none
109+
} else {
110+
// start a timer, that will be checked in the on_frame callback
111+
g.revert_sw.start()
112+
}
113+
}
114+
}
115+
}
116+
g.remaining = g.cells.count(!it.is_open)
117+
if g.remaining == 0 {
118+
g.sw.stop()
119+
}
120+
}
121+
122+
mut g := &Game{
123+
// the CLI argument should be number of pairs, so `size` is even, and the puzzle can be solved:
124+
size: arguments()[1] or { '3' }.int() * 2
125+
}
126+
g.restart()
127+
g.ctx = gg.new_context(
128+
bg_color: gx.black
129+
width: g.size * csize
130+
height: header_size + g.size * csize
131+
window_title: 'V Memory ${g.size} x ${g.size}'
132+
user_data: g
133+
frame_fn: on_frame
134+
event_fn: on_event
135+
sample_count: 2
136+
)
137+
g.ctx.run()

0 commit comments

Comments
 (0)