Skip to content

Instantly share code, notes, and snippets.

@macu
Created September 6, 2015 02:28
Show Gist options
  • Select an option

  • Save macu/1a0177f8075f025f0b0c to your computer and use it in GitHub Desktop.

Select an option

Save macu/1a0177f8075f025f0b0c to your computer and use it in GitHub Desktop.
Quick and dirty console minesweeper game
package main
import (
"fmt"
"time"
)
import "math/rand"
func main() {
interactiveGame()
}
func interactiveGame() {
size := readInt("How many rows and cols:")
mines := readInt("How many mines:")
game := makeGame(size, size, mines)
fmt.Println()
for {
fmt.Println(game)
r, c := readCoords("Flip:")
if game.Flip(r, c) {
fmt.Println(game)
fmt.Println("YOU LOSE.")
return
}
if game.countUncheckedClear() == 0 {
fmt.Println(game)
fmt.Println("YOU WIN!!!")
return
}
fmt.Println()
}
}
func readInt(prompt string) (i int) {
var s string
for {
fmt.Printf("%s ", prompt)
if _, err := fmt.Scanf("%s", &s); err != nil {
fmt.Println(err)
return 0
}
if _, err := fmt.Sscanf(s, "%d", &i); err != nil {
fmt.Println("Not a number.")
continue
}
return i
}
}
func readCoords(prompt string) (r, c int) {
var s string
for {
fmt.Printf("%s ", prompt)
if _, err := fmt.Scanf("%s", &s); err != nil {
fmt.Println(err)
return 0, 0
}
if _, err := fmt.Sscanf(s, "%d,%d", &r, &c); err != nil {
fmt.Println("Invalid input. Expected: <int>,<int>")
continue
}
return r, c
}
}
type TileState int
const (
UncheckedClear TileState = iota
UncheckedMine
CheckedClear
CheckedMine
OutOfBounds
)
type Game struct {
spaces []TileState
rows int
cols int
}
func makeGame(rows, cols, mines int) *Game {
game := &Game{
spaces: make([]TileState, rows*cols),
rows: rows,
cols: cols,
}
// Lay mines randomly
rand.Seed(time.Now().UnixNano())
for i := 0; i < mines; i++ {
for {
r := rand.Intn(rows)
c := rand.Intn(cols)
if game.get(r, c) == UncheckedClear {
game.set(r, c, UncheckedMine)
break
}
}
}
return game
}
// Flips the tile at the given x, y coordinate.
// Returns true if the tile is a mine.
func (game *Game) Flip(row, col int) bool {
switch game.get(row, col) {
case UncheckedMine, CheckedMine:
game.set(row, col, CheckedMine)
return true
case UncheckedClear:
game.set(row, col, CheckedClear)
game.AutoClear(row, col)
}
return false
}
func (game *Game) AutoClear(r, c int) {
game.set(r, c, CheckedClear)
if game.get(r-1, c) == UncheckedClear {
game.AutoClear(r-1, c)
}
if game.get(r+1, c) == UncheckedClear {
game.AutoClear(r+1, c)
}
if game.get(r, c-1) == UncheckedClear {
game.AutoClear(r, c-1)
}
if game.get(r, c+1) == UncheckedClear {
game.AutoClear(r, c+1)
}
}
func (game *Game) get(r, c int) TileState {
if r < 0 || c < 0 {
return OutOfBounds
}
i := (r * game.cols) + c
if i < len(game.spaces) {
return game.spaces[i]
}
return OutOfBounds
}
func (game *Game) set(r, c int, s TileState) {
game.spaces[(r*game.cols)+c] = s
}
func (game *Game) countAdjacentMines(r, c int) (mines int) {
for x := -1; x <= 1; x++ {
for y := -1; y <= 1; y++ {
if game.get(r+x, c+y) == UncheckedMine {
mines++
}
}
}
return mines
}
func (game *Game) countUncheckedClear() (clear int) {
for _, t := range game.spaces {
if t == UncheckedClear {
clear++
}
}
return clear
}
func (game *Game) String() string {
var s string
s += " "
for c := 0; c < game.cols; c++ {
s += fmt.Sprintf("%-3d", c)
}
s += "\n\n"
for r := 0; r < game.rows; r++ {
s += fmt.Sprintf("%-6d", r)
for c := 0; c < game.cols; c++ {
switch game.get(r, c) {
case UncheckedClear, UncheckedMine:
s += fmt.Sprintf("%-3s", "?")
case CheckedMine:
s += fmt.Sprintf("%-3s", "M")
default:
mines := game.countAdjacentMines(r, c)
switch mines {
case 0:
s += fmt.Sprintf("%-3s", "_")
default:
s += fmt.Sprintf("%-3d", mines)
}
}
}
s += "\n"
}
return s
}
func (game *Game) RevealString() string {
var s string
for r := 0; r < game.rows; r++ {
for c := 0; c < game.cols; c++ {
switch game.get(r, c) {
case UncheckedMine, CheckedMine:
s += "M"
default:
s += "_"
}
}
s += "\n"
}
return s
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment