182 lines
4.1 KiB
Go
182 lines
4.1 KiB
Go
package minesweeper
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"image/color"
|
|
|
|
"github.com/hajimehoshi/ebiten/examples/resources/fonts"
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
|
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
|
"github.com/hajimehoshi/ebiten/v2/text/v2"
|
|
)
|
|
|
|
type Game struct {
|
|
Width, Height int
|
|
CellSize int
|
|
rows, columns int
|
|
bombPercentage int
|
|
cells []Cell
|
|
firstMove bool
|
|
lost bool
|
|
won bool
|
|
bombIndexes map[int]struct{}
|
|
faceSource *text.GoTextFaceSource
|
|
}
|
|
|
|
func NewGame(width, height, cellSize, bombPercentage int) (*Game, error) {
|
|
s, err := text.NewGoTextFaceSource(bytes.NewReader(fonts.MPlus1pRegular_ttf))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rows := width / cellSize
|
|
columns := height / cellSize
|
|
|
|
g := &Game{
|
|
Width: width,
|
|
Height: height,
|
|
CellSize: cellSize,
|
|
rows: rows,
|
|
columns: columns,
|
|
bombPercentage: bombPercentage,
|
|
cells: make([]Cell, rows*columns),
|
|
firstMove: false,
|
|
lost: false,
|
|
won: false,
|
|
bombIndexes: map[int]struct{}{},
|
|
faceSource: s,
|
|
}
|
|
|
|
g.Reset()
|
|
|
|
return g, nil
|
|
}
|
|
|
|
func (g *Game) CheckWin() {
|
|
for _, cell := range g.cells {
|
|
if cell.cellType != BOMB && cell.state == FLAG {
|
|
return
|
|
} else if cell.cellType == BOMB && cell.state != FLAG {
|
|
return
|
|
} else if cell.state == DARK {
|
|
return
|
|
}
|
|
}
|
|
|
|
g.won = true
|
|
return
|
|
}
|
|
|
|
func (g *Game) HandleFirstMove(i int) {
|
|
if !g.firstMove {
|
|
return
|
|
}
|
|
|
|
indexes := make([]int, 0)
|
|
|
|
g.applyNeighbours(i, func(index int) {
|
|
indexes = append(indexes, index)
|
|
r := RandomUntilNoBomb(g.rows*g.columns, g.bombIndexes)
|
|
g.cells[r].cellType = BOMB
|
|
})
|
|
|
|
for _, i := range indexes {
|
|
g.cells[i].cellType = NO_BOMB
|
|
}
|
|
|
|
for i := range g.cells {
|
|
g.cells[i].bombNeighbours = g.CalcNeighbourBombs(i)
|
|
}
|
|
|
|
g.SetBomblessNeighboursToOpen(i)
|
|
g.firstMove = false
|
|
}
|
|
|
|
func (g *Game) Reset() {
|
|
cells, indexes := g.SetupCells()
|
|
g.cells = cells
|
|
g.bombIndexes = indexes
|
|
g.won = false
|
|
g.lost = false
|
|
g.firstMove = true
|
|
}
|
|
|
|
func (g *Game) Update() error {
|
|
if !g.won && !g.lost {
|
|
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
|
x, y := ebiten.CursorPosition()
|
|
index := g.CursorPosToIndex(x, y)
|
|
g.HandleFirstMove(index)
|
|
|
|
if g.cells[index].cellType == BOMB && g.cells[index].state != FLAG {
|
|
g.lost = true
|
|
} else if g.cells[index].state != FLAG {
|
|
g.cells[index].state = OPEN
|
|
if g.cells[index].bombNeighbours == 0 {
|
|
g.SetBomblessNeighboursToOpen(index)
|
|
}
|
|
}
|
|
} else if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonRight) {
|
|
x, y := ebiten.CursorPosition()
|
|
index := g.CursorPosToIndex(x, y)
|
|
if g.cells[index].state == DARK {
|
|
g.cells[index].state = FLAG
|
|
} else if g.cells[index].state == FLAG {
|
|
g.cells[index].state = DARK
|
|
}
|
|
}
|
|
}
|
|
|
|
if inpututil.IsKeyJustPressed(ebiten.KeyR) {
|
|
g.Reset()
|
|
}
|
|
|
|
g.CheckWin()
|
|
return nil
|
|
}
|
|
|
|
func (g *Game) Draw(screen *ebiten.Image) {
|
|
showBombs := false
|
|
if g.lost {
|
|
showBombs = true
|
|
ebitenutil.DebugPrint(screen, "Game Over!")
|
|
} else if g.won {
|
|
ebitenutil.DebugPrint(screen, "Game Won :) YAAY!")
|
|
return
|
|
}
|
|
|
|
g.DrawCells(screen, showBombs)
|
|
}
|
|
|
|
func (g *Game) DrawCells(screen *ebiten.Image, showBombs bool) {
|
|
for _, cell := range g.cells {
|
|
op := &ebiten.DrawImageOptions{}
|
|
op.GeoM.Translate(float64(cell.left), float64(cell.top))
|
|
|
|
borderImage := ebiten.NewImage(g.CellSize, g.CellSize)
|
|
borderImage.Fill(color.Black)
|
|
|
|
cellImage := ebiten.NewImage(g.CellSize-2, g.CellSize-2)
|
|
if showBombs && cell.cellType == BOMB {
|
|
cellImage.Fill(color.RGBA{255, 0, 0, 255})
|
|
} else {
|
|
cellImage.Fill(MatchColor(cell.state))
|
|
}
|
|
borderImage.DrawImage(cellImage, nil)
|
|
|
|
if cell.state == OPEN && cell.bombNeighbours != 0 {
|
|
txOp := &text.DrawOptions{}
|
|
txOp.GeoM.Translate(float64(g.CellSize-24)/2, float64(g.CellSize-24)/2)
|
|
text.Draw(borderImage, fmt.Sprintf("%v", cell.bombNeighbours), &text.GoTextFace{Source: g.faceSource, Size: 24}, txOp)
|
|
}
|
|
|
|
screen.DrawImage(borderImage, op)
|
|
}
|
|
}
|
|
|
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
|
return outsideWidth, outsideHeight
|
|
}
|