A minesweeper game
This code is organized as a package with a main function and several supporting functions. The main function sets up the game board. The supporting functions handle user input and various aspects of the game, such as generating the minefield, checking for win conditions, making moves and displaying the game board.
Here is the Code for the app. If you want to try it:
1. Copy the project into main.go
领英推荐
2. Run "go run main.go" in the terminal
package main
import (
"bufio"
"fmt"
"log"
"math/rand"
"os"
"strconv"
"strings"
)
// Game
const (
CELL_EMPTY = 0
CELL_HAS_MINE = 1
CELL_HAS_FLAG = 2
CELL_OPEN = 3
)
func main() {
x := 5
y := 5
mines := 5
field := generateField(x, y)
generateMines(field, mines)
for !isGameEnded(field) {
printField(field)
command, x, y, err := readInput()
if err != nil {
log.Fatal(err)
}
if command == "o" {
field = openCell(field, x, y)
} else if command == "f" {
field = flagCell(field, x, y)
} else {
fmt.Println("Unknown command")
}
}
printResult(field)
fmt.Println("You won!")
}
//readInput reads input from the user in format "o 1 2" or "f 1 2"
func readInput() (string, int, int, error) {
println("--------------------")
fmt.Println("Enter o x y to open a cell or f x y to flag a cell")
in := bufio.NewReader(os.Stdin)
line, err := in.ReadString('\n')
if err != nil {
return "", 0, 0, err
}
strs := strings.Split(line[0:len(line)-1], " ")
command := strs[0]
x, err := strconv.Atoi(strs[1])
if err != nil {
return "", 0, 0, err
}
y, err := strconv.Atoi(strs[2])
if err != nil {
log.Fatal(err)
}
println("--------------------")
// convert to zero-based coordinates
return command, x - 1, y - 1, nil
}
//generateField generates a field
func generateField(x, y int) [][]int {
field := make([][]int, y)
for i := range field {
field[i] = make([]int, x)
}
return field
}
//generateMines generates mines
func generateMines(field [][]int, mines int) [][]int {
for i := 0; i < mines; i++ {
x := rand.Intn(len(field))
y := rand.Intn(len(field[0]))
if field[x][y] == 1 {
mines++
}
field[x][y] = 1
}
return field
}
//printField prints the masked field
func printField(field [][]int) {
for i := range field {
for j := range field[i] {
if field[i][j] == CELL_EMPTY || field[i][j] == CELL_HAS_MINE {
fmt.Print("? ")
} else if field[i][j] == CELL_HAS_FLAG {
fmt.Print("F ")
} else if field[i][j] == CELL_OPEN {
minesAround := countMinesAround(field, i, j)
fmt.Print(minesAround, " ")
} else {
fmt.Print(" ")
}
}
fmt.Println()
}
}
//printResult prints the unmasked field
func printResult(field [][]int) {
for i := range field {
for j := range field[i] {
if field[i][j] == CELL_EMPTY {
fmt.Print("o ")
} else if field[i][j] == CELL_HAS_MINE {
fmt.Print("* ")
} else if field[i][j] == CELL_HAS_FLAG {
fmt.Print("F ")
} else if field[i][j] == CELL_OPEN {
fmt.Print(". ")
} else {
fmt.Print(" ")
}
}
fmt.Println()
}
}
//openCell opens a cell
func openCell(field [][]int, x, y int) [][]int {
if !cellExists(field, x, y) {
fmt.Println("Cell does not exist")
return field
}
if field[x][y] == CELL_HAS_MINE {
printResult(field)
log.Fatal("You lost!")
} else if field[x][y] == CELL_EMPTY {
openCellWithAdjacent(field, x, y)
} else if field[x][y] == CELL_HAS_FLAG {
fmt.Println("You can't open a flagged cell")
} else if field[x][y] == CELL_OPEN {
fmt.Println("This cell is already open")
} else {
fmt.Println("Unknown state of cell")
}
return field
}
//flagCell flags a cell
func flagCell(field [][]int, x, y int) [][]int {
if field[x][y] == CELL_HAS_MINE {
field[x][y] = CELL_HAS_FLAG
} else if field[x][y] == CELL_EMPTY {
field[x][y] = CELL_HAS_FLAG
} else if field[x][y] == CELL_HAS_FLAG {
fmt.Println("This cell is already flagged")
} else if field[x][y] == CELL_OPEN {
fmt.Println("You can't flag an open cell")
} else {
fmt.Println("Unknown state of cell")
}
return field
}
//isGameEnded checks if all mines are flagged and there are no empty cells
func isGameEnded(field [][]int) bool {
for i := range field {
for j := range field[i] {
if field[i][j] == CELL_EMPTY {
return false
}
}
}
return true
}
//countMinesAround checks counts mines around
func countMinesAround(field [][]int, x, y int) int {
count := 0
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
// check if index is out of bounds
if !cellExists(field, x+i, y+j) {
continue
}
// check if cell is a mine
if field[x+i][y+j] == CELL_HAS_MINE {
count++
}
}
}
return count
}
//openCellWithAdjacent opens adjacent cells
func openCellWithAdjacent(field [][]int, x, y int) [][]int {
for i := -1; i <= 1; i++ {
for j := -1; j <= 1; j++ {
// check if index is out of bounds
if !cellExists(field, x+i, y+j) {
continue
}
// if cell is empty and has a mine around
if field[x+i][y+j] == CELL_EMPTY && countMinesAround(field, x+i, y+j) > 0 {
// simply open cell
field[x+i][y+j] = CELL_OPEN
// if cell is empty and has no mines around
} else if field[x+i][y+j] == CELL_EMPTY && countMinesAround(field, x+i, y+j) == 0 {
// open cell and open adjacent cells
field[x+i][y+j] = CELL_OPEN
field = openCellWithAdjacent(field, x+i, y+j)
}
}
}
return field
}
//cellExists checks if a cell exists ( slice index not out of range )
func cellExists(field [][]int, x, y int) bool {
if x < 0 || y < 0 || x >= len(field) || y >= len(field[0]) {
return false
}
return true
}