Skip to content

Commit c226572

Browse files
committed
33 mines
Signed-off-by: plutov <a.pliutau@gmail.com>
1 parent 14d0724 commit c226572

File tree

4 files changed

+102
-42
lines changed

4 files changed

+102
-42
lines changed

33-raylib-go-minesweeper/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

33-raylib-go-minesweeper/go.mod

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ module minesweeper
22

33
go 1.23.4
44

5-
require github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b
5+
require (
6+
github.com/gen2brain/raylib-go/raygui v0.0.0-20250109172833-6dbba4f81a9b
7+
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b
8+
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8
9+
)
610

711
require (
812
github.com/ebitengine/purego v0.8.2 // indirect
9-
github.com/gen2brain/raylib-go/raygui v0.0.0-20250109172833-6dbba4f81a9b // indirect
10-
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
1113
golang.org/x/sys v0.29.0 // indirect
1214
)

33-raylib-go-minesweeper/go.sum

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
github.com/ebitengine/purego v0.7.1 h1:6/55d26lG3o9VCZX8lping+bZcmShseiqlh2bnUDiPA=
2-
github.com/ebitengine/purego v0.7.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
31
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
42
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
53
github.com/gen2brain/raylib-go/raygui v0.0.0-20250109172833-6dbba4f81a9b h1:kyotn5/LDKyuRHZlR33ItQuhyvsOoJopvdAn+QmaouM=
64
github.com/gen2brain/raylib-go/raygui v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:Ji/uPEko2AUkcyPLAelEUa+E8Npc89/XY5Fo/lS/e3I=
75
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b h1:JJfspevP3YOXcSKVABizYOv++yMpTJIdPUtoDzF/RWw=
86
github.com/gen2brain/raylib-go/raylib v0.0.0-20250109172833-6dbba4f81a9b/go.mod h1:BaY76bZk7nw1/kVOSQObPY1v1iwVE1KHAGMfvI6oK1Q=
9-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
10-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
117
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
128
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
13-
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
14-
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
159
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
1610
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

33-raylib-go-minesweeper/main.go

Lines changed: 97 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"fmt"
5+
"time"
56

67
gui "github.com/gen2brain/raylib-go/raygui"
78
rl "github.com/gen2brain/raylib-go/raylib"
@@ -18,12 +19,13 @@ const (
1819
)
1920

2021
type state struct {
21-
menu bool
22-
gameOver bool
23-
won bool
24-
rows int32
25-
cols int32
26-
mines int32
22+
menu bool
23+
gameOver bool
24+
gameWon bool
25+
startedAt time.Time
26+
rows int32
27+
cols int32
28+
mines int32
2729
// [x][y]
2830
field [][]point
2931
}
@@ -35,32 +37,35 @@ type point struct {
3537
neighbours int
3638
}
3739

38-
// buttons and paddings
3940
func (s *state) getWidth() int32 {
41+
// buttons, paddings
4042
return size*s.cols + 2*padding
4143
}
4244

4345
func (s *state) getHeight() int32 {
44-
// with status bar
46+
// buttons, paddings, status bar
4547
return size*s.rows + 2*padding + size
4648
}
4749

4850
func (s *state) getStatus() string {
4951
fps := rl.GetFPS()
52+
elapsed := time.Since(s.startedAt)
5053

51-
return fmt.Sprintf("FPS: %d", fps)
54+
return fmt.Sprintf("FPS: %d, TIME: %d", fps, int(elapsed.Seconds()))
5255
}
5356

5457
func (s *state) reset() {
5558
s.gameOver = false
56-
s.won = false
59+
s.gameWon = false
60+
s.startedAt = time.Now()
5761
s.menu = true
5862
s.rows = 9
5963
s.cols = 9
6064
s.mines = 10
6165
}
6266

6367
func (s *state) start() {
68+
// Build grid
6469
s.field = make([][]point, s.rows)
6570
for x := range s.rows {
6671
s.field[x] = make([]point, s.cols)
@@ -69,7 +74,7 @@ func (s *state) start() {
6974
}
7075
}
7176

72-
// plant mines
77+
// Plant mines
7378
m := s.mines
7479
for m > 0 {
7580
x, y := rand.Intn(int(s.rows)), rand.Intn(int(s.cols))
@@ -105,27 +110,75 @@ func (s *state) doForNeighbours(x, y int, do func(x, y int)) {
105110
}
106111
}
107112

113+
func (s *state) checkIfGameWon() bool {
114+
open := 0
115+
total := int(s.rows * s.cols)
116+
117+
for x := 0; x < int(s.rows); x++ {
118+
for y := 0; y < int(s.cols); y++ {
119+
if s.field[x][y].open {
120+
open++
121+
}
122+
}
123+
}
124+
125+
return open == total-int(s.mines)
126+
}
127+
128+
func (s *state) revealTile(x, y int) {
129+
if s.field[x][y].open {
130+
return
131+
}
132+
133+
if s.field[x][y].hasMine {
134+
s.gameOver = true
135+
return
136+
}
137+
138+
s.field[x][y].open = true
139+
s.gameWon = s.checkIfGameWon()
140+
141+
// No neighbors, reveal all adjacent tiles recursively.
142+
if s.field[x][y].neighbours == 0 {
143+
s.doForNeighbours(x, y, func(nx, ny int) {
144+
s.revealTile(nx, ny)
145+
})
146+
}
147+
}
148+
108149
func (s *state) drawMenu() {
109150
w, h := defaultWinWidth, defaultWinHeight
110151
colw := float32(w / 2)
111-
var lineHeight int32 = 50
152+
var rowh float32 = 50
112153
rl.SetWindowSize(w, h)
113154

114-
rl.DrawText("ROWS:", padding, lineHeight, size, rl.White)
115-
s.rows = gui.Spinner(rl.NewRectangle(colw, float32(lineHeight), float32(colw-padding), size), "", &s.rows, minRowsCols, maxRowsCols, true)
155+
rl.DrawText("ROWS:", padding, int32(rowh), size, rl.White)
156+
s.rows = gui.Spinner(rl.NewRectangle(colw, rowh, float32(colw-padding), size), "", &s.rows, minRowsCols, maxRowsCols, true)
116157

117-
rl.DrawText("COLS:", padding, 2*lineHeight, size, rl.White)
118-
s.cols = gui.Spinner(rl.NewRectangle(colw, float32(2*lineHeight), float32(colw-padding), size), "", &s.cols, minRowsCols, maxRowsCols, true)
158+
rl.DrawText("COLS:", padding, 2*int32(rowh), size, rl.White)
159+
s.cols = gui.Spinner(rl.NewRectangle(colw, 2*rowh, float32(colw-padding), size), "", &s.cols, minRowsCols, maxRowsCols, true)
119160

120-
rl.DrawText("MINES:", padding, 3*lineHeight, size, rl.White)
121-
s.mines = gui.Spinner(rl.NewRectangle(colw, float32(3*lineHeight), float32(colw-padding), size), "", &s.mines, minRowsCols, maxRowsCols, true)
161+
rl.DrawText("MINES:", padding, 3*int32(rowh), size, rl.White)
162+
s.mines = gui.Spinner(rl.NewRectangle(colw, 3*rowh, float32(colw-padding), size), "", &s.mines, 1, int(s.rows)*int(s.cols), true)
122163

123-
clicked := gui.Button(rl.NewRectangle(padding, float32(4*lineHeight), float32(w-2*padding), size), "START")
124-
if clicked {
164+
if clicked := gui.Button(rl.NewRectangle(padding, 4*rowh, float32(w-2*padding), size), "START"); clicked {
125165
s.start()
126166
}
127167
}
128168

169+
func getTextColor(neighbors int) rl.Color {
170+
switch neighbors {
171+
case 1:
172+
return rl.Blue
173+
case 2:
174+
return rl.Green
175+
case 3:
176+
return rl.Red
177+
default:
178+
return rl.Black
179+
}
180+
}
181+
129182
func (s *state) drawField() {
130183
w := float32(s.getWidth())
131184
h := float32(s.getHeight())
@@ -135,16 +188,30 @@ func (s *state) drawField() {
135188

136189
for x := range s.field {
137190
for y := range s.field[x] {
138-
if s.field[x][y].open {
139-
rl.DrawRectangle(padding+int32(x)*size, padding+int32(y)*size, size, size, rl.Gray)
140-
if s.field[x][y].hasMine {
141-
gui.DrawIcon(gui.ICON_STAR, 5+padding+int32(x)*size, 5+padding+int32(y)*size, 1, rl.Red)
142-
s.gameOver = true
143-
} else {
144-
rl.DrawText(fmt.Sprintf("%d", s.field[x][y].neighbours), 5+padding+int32(x)*size, 5+padding+int32(y)*size, 20, rl.DarkGreen)
191+
rect := rl.NewRectangle(float32(padding+x*size), float32(padding+y*size), size, size)
192+
193+
// Mark on right mouse button
194+
if rl.IsMouseButtonPressed(rl.MouseButtonRight) {
195+
if rl.CheckCollisionPointRec(rl.GetMousePosition(), rect) {
196+
if !s.field[x][y].open {
197+
s.field[x][y].marked = !s.field[x][y].marked
198+
}
145199
}
200+
}
201+
202+
if s.field[x][y].marked {
203+
rl.DrawText("M", 5+padding+int32(x)*size, 5+padding+int32(y)*size, 20, rl.Violet)
204+
} else if s.field[x][y].open {
205+
text := ""
206+
if s.field[x][y].neighbours > 0 {
207+
text = fmt.Sprintf("%d", s.field[x][y].neighbours)
208+
}
209+
210+
rl.DrawText(text, 5+padding+int32(x)*size, 5+padding+int32(y)*size, 20, getTextColor(s.field[x][y].neighbours))
146211
} else {
147-
s.field[x][y].open = gui.Button(rl.NewRectangle(float32(padding+x*size), float32(padding+y*size), size, size), "")
212+
if open := gui.Button(rect, ""); open {
213+
s.revealTile(x, y)
214+
}
148215
}
149216
}
150217
}
@@ -158,7 +225,7 @@ func (s *state) drawMessageWithRestart() {
158225
if s.gameOver {
159226
rl.DrawText("GAME OVER :(", padding, lineHeight, size, rl.White)
160227
}
161-
if s.won {
228+
if s.gameWon {
162229
rl.DrawText("WELL DONE !", padding, lineHeight, size, rl.White)
163230
}
164231

@@ -180,7 +247,7 @@ func main() {
180247
rl.BeginDrawing()
181248
rl.ClearBackground(rl.DarkGray)
182249

183-
if game.gameOver {
250+
if game.gameOver || game.gameWon {
184251
game.drawMessageWithRestart()
185252
} else if game.menu {
186253
game.drawMenu()

0 commit comments

Comments
 (0)