forked from go/golangs_learn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
3.0 KiB
212 lines
3.0 KiB
package bugs
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type square struct {
|
|
bug bool
|
|
shown bool
|
|
flagged bool
|
|
|
|
near int
|
|
}
|
|
|
|
type board struct {
|
|
height, width int
|
|
|
|
win, lose func()
|
|
|
|
bugCount int
|
|
flagCount int
|
|
bugs [][]square
|
|
}
|
|
|
|
func (b *board) countHidden() int {
|
|
count := 0
|
|
|
|
for y := 0; y < b.height; y++ {
|
|
for x := 0; x < b.width; x++ {
|
|
item := b.bugs[y][x]
|
|
|
|
if !item.shown {
|
|
count++
|
|
}
|
|
}
|
|
}
|
|
|
|
return count
|
|
}
|
|
|
|
func (b *board) load(count int) {
|
|
b.bugCount = count
|
|
b.flagCount = 0
|
|
if b.bugs == nil {
|
|
b.bugs = make([][]square, b.height)
|
|
|
|
for y := 0; y < b.height; y++ {
|
|
b.bugs[y] = make([]square, b.width)
|
|
}
|
|
} else {
|
|
for y := 0; y < b.height; y++ {
|
|
for x := 0; x < b.width; x++ {
|
|
b.bugs[y][x].shown = false
|
|
b.bugs[y][x].bug = false
|
|
b.bugs[y][x].flagged = false
|
|
b.bugs[y][x].near = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := 0; i < count; i++ {
|
|
x := rand.Intn(b.width)
|
|
y := rand.Intn(b.height)
|
|
|
|
if b.bugs[y][x].bug {
|
|
i--
|
|
} else {
|
|
b.setMine(x, y)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *board) incSquare(x, y int) {
|
|
if x < 0 || y < 0 {
|
|
return
|
|
}
|
|
if x >= b.width || y >= b.height {
|
|
return
|
|
}
|
|
|
|
if b.bugs[y][x].bug {
|
|
return
|
|
}
|
|
b.bugs[y][x].near++
|
|
}
|
|
|
|
func (b *board) setMine(x, y int) {
|
|
if b.bugs[y][x].bug {
|
|
return
|
|
}
|
|
b.bugs[y][x].bug = true
|
|
|
|
b.incSquare(x-1, y-1)
|
|
b.incSquare(x, y-1)
|
|
b.incSquare(x+1, y-1)
|
|
|
|
b.incSquare(x-1, y)
|
|
b.incSquare(x+1, y)
|
|
|
|
b.incSquare(x-1, y+1)
|
|
b.incSquare(x, y+1)
|
|
b.incSquare(x+1, y+1)
|
|
}
|
|
|
|
func (b *board) reveal(x, y int) {
|
|
if x < 0 || y < 0 {
|
|
return
|
|
}
|
|
if x >= b.width || y >= b.height {
|
|
return
|
|
}
|
|
|
|
sq := b.bugs[y][x]
|
|
if sq.shown || sq.flagged {
|
|
return
|
|
}
|
|
b.bugs[y][x].shown = true
|
|
|
|
if sq.bug {
|
|
if b.lose != nil {
|
|
b.lose()
|
|
}
|
|
return
|
|
}
|
|
|
|
if sq.near == 0 {
|
|
b.reveal(x-1, y-1)
|
|
b.reveal(x, y-1)
|
|
b.reveal(x+1, y-1)
|
|
b.reveal(x-1, y)
|
|
b.reveal(x+1, y)
|
|
b.reveal(x-1, y+1)
|
|
b.reveal(x, y+1)
|
|
b.reveal(x+1, y+1)
|
|
}
|
|
|
|
if b.countHidden() == b.bugCount && b.win != nil {
|
|
b.win()
|
|
}
|
|
}
|
|
|
|
func (b *board) flag(x, y int) {
|
|
if x < 0 || y < 0 {
|
|
return
|
|
}
|
|
if x >= b.width || y >= b.height {
|
|
return
|
|
}
|
|
|
|
sq := b.bugs[y][x]
|
|
if sq.shown {
|
|
return
|
|
}
|
|
|
|
if sq.flagged {
|
|
b.bugs[y][x].flagged = false
|
|
b.flagCount--
|
|
} else {
|
|
b.bugs[y][x].flagged = true
|
|
b.flagCount++
|
|
}
|
|
}
|
|
|
|
func (b *board) flagged(x, y int) bool {
|
|
if x < 0 || y < 0 {
|
|
return false
|
|
}
|
|
if x >= b.width || y >= b.height {
|
|
return false
|
|
}
|
|
|
|
sq := b.bugs[y][x]
|
|
return sq.flagged
|
|
}
|
|
|
|
func (b *board) remaining() int {
|
|
return b.bugCount - b.flagCount
|
|
}
|
|
|
|
func squareString(sq square) string {
|
|
if !sq.shown {
|
|
return "?"
|
|
} else if sq.bug {
|
|
return "*"
|
|
} else if sq.near == 0 {
|
|
return " "
|
|
}
|
|
|
|
return fmt.Sprintf("%d", sq.near)
|
|
}
|
|
|
|
func (b *board) String() string {
|
|
buf := strings.Builder{}
|
|
for y := 0; y < b.height; y++ {
|
|
for x := 0; x < b.width; x++ {
|
|
sq := b.bugs[y][x]
|
|
|
|
buf.WriteString(squareString(sq))
|
|
}
|
|
buf.WriteByte('\n')
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
func newBoard(height, width int) *board {
|
|
rand.Seed(time.Now().Unix())
|
|
return &board{height: height, width: width}
|
|
}
|
|
|