Created
September 6, 2015 02:28
-
-
Save macu/1a0177f8075f025f0b0c to your computer and use it in GitHub Desktop.
Quick and dirty console minesweeper game
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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