@@ -2,6 +2,7 @@ package main
22
33import (
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
2021type 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
3940func (s * state ) getWidth () int32 {
41+ // buttons, paddings
4042 return size * s .cols + 2 * padding
4143}
4244
4345func (s * state ) getHeight () int32 {
44- // with status bar
46+ // buttons, paddings, status bar
4547 return size * s .rows + 2 * padding + size
4648}
4749
4850func (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
5457func (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
6367func (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+
108149func (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+
129182func (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