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