commit 73f562ca7045a8e4e2bc89fa331476403da32175 Author: Pablu23 Date: Wed May 21 09:23:08 2025 +0200 Initial commit, also running version diff --git a/cell.go b/cell.go new file mode 100644 index 0000000..5f318e3 --- /dev/null +++ b/cell.go @@ -0,0 +1,26 @@ +package minesweeper + +import "image/color" + +type CellType int + +const ( + NO_BOMB CellType = iota + BOMB +) + +type State int + +const ( + OPEN State = iota + DARK + FLAG +) + +type Cell struct { + top, left float32 + color color.Color + state State + cellType CellType + bombNeighbours int +} diff --git a/cmd/mine-sweeper/main.go b/cmd/mine-sweeper/main.go new file mode 100644 index 0000000..808be65 --- /dev/null +++ b/cmd/mine-sweeper/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "log" + + minesweeper "github.com/Pablu23/mine-sweeper" + "github.com/hajimehoshi/ebiten/v2" +) + +func main() { + width := 720 + height := 720 + cellSize := 60 + bombPercentage := 20 + + ebiten.SetWindowSize(width, height) + ebiten.SetWindowTitle("Hello, World!") + game, err := minesweeper.NewGame(width, height, cellSize, bombPercentage) + if err != nil { + log.Fatal(err) + } + + if err := ebiten.RunGame(game); err != nil { + log.Fatal(err) + } +} diff --git a/constants.go b/constants.go new file mode 100644 index 0000000..fb7a59d --- /dev/null +++ b/constants.go @@ -0,0 +1,9 @@ +package minesweeper + +import "image/color" + +var ( + OPEN_COLOR = color.RGBA{200, 200, 200, 255} + DARK_COLOR = color.RGBA{20, 20, 20, 255} + FLAG_COLOR = color.RGBA{0, 255, 0, 255} +) diff --git a/game.go b/game.go new file mode 100644 index 0000000..b33f475 --- /dev/null +++ b/game.go @@ -0,0 +1,181 @@ +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 +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..064216e --- /dev/null +++ b/go.mod @@ -0,0 +1,24 @@ +module github.com/Pablu23/mine-sweeper + +go 1.23.4 + +require github.com/hajimehoshi/ebiten/v2 v2.8.8 + +require ( + github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 // indirect + github.com/go-text/typesetting v0.2.0 // indirect + golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect + golang.org/x/image v0.20.0 // indirect + golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f // indirect + golang.org/x/text v0.18.0 // indirect +) + +require ( + github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 // indirect + github.com/ebitengine/hideconsole v1.0.0 // indirect + github.com/ebitengine/purego v0.8.0 // indirect + github.com/hajimehoshi/ebiten v1.12.12 + github.com/jezek/xgb v1.1.1 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.25.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ab7680a --- /dev/null +++ b/go.sum @@ -0,0 +1,80 @@ +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325 h1:Gk1XUEttOk0/hb6Tq3WkmutWa0ZLhNn/6fc6XZpM7tM= +github.com/ebitengine/gomobile v0.0.0-20240911145611-4856209ac325/go.mod h1:ulhSQcbPioQrallSuIzF8l1NKQoD7xmMZc5NxzibUMY= +github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj1yReDqE= +github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A= +github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= +github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2 h1:Ac1OEHHkbAZ6EUnJahF0GKcU0FjPc/V8F1DvjhKngFE= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200707082815-5321531c36a2/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-text/typesetting v0.2.0 h1:fbzsgbmk04KiWtE+c3ZD4W2nmCRzBqrqQOvYlwAOdho= +github.com/go-text/typesetting v0.2.0/go.mod h1:2+owI/sxa73XA581LAzVuEBZ3WEEV2pXeDswCH/3i1I= +github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/hajimehoshi/bitmapfont v1.3.0/go.mod h1:/Qb7yVjHYNUV4JdqNkPs6BSZwLjKqkZOMIp6jZD0KgE= +github.com/hajimehoshi/ebiten v1.12.12 h1:JvmF1bXRa+t+/CcLWxrJCRsdjs2GyBYBSiFAfIqDFlI= +github.com/hajimehoshi/ebiten v1.12.12/go.mod h1:1XI25ImVCDPJiXox4h9yK/CvN5sjDYnbF4oZcFzPXHw= +github.com/hajimehoshi/ebiten/v2 v2.8.8 h1:xyMxOAn52T1tQ+j3vdieZ7auDBOXmvjUprSrxaIbsi8= +github.com/hajimehoshi/ebiten/v2 v2.8.8/go.mod h1:durJ05+OYnio9b8q0sEtOgaNeBEQG7Yr7lRviAciYbs= +github.com/hajimehoshi/file2byteslice v0.0.0-20200812174855-0e5e8a80490e/go.mod h1:CqqAHp7Dk/AqQiwuhV1yT2334qbA/tFWQW0MD2dGqUE= +github.com/hajimehoshi/go-mp3 v0.3.1/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= +github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= +github.com/hajimehoshi/oto v0.6.8/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= +github.com/jakecoffman/cp v1.0.0/go.mod h1:JjY/Fp6d8E1CHnu74gWNnU0+b9VzEdUVPoJxg2PsTQg= +github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= +github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk= +github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk= +github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200801110659-972c09e46d76/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw= +golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f h1:aEcjdTsycgPqO/caTgnxfR9xwWOltP/21vtJyFztEy0= +golang.org/x/mobile v0.0.0-20210208171126-f462b3930c8f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/util.go b/util.go new file mode 100644 index 0000000..4120011 --- /dev/null +++ b/util.go @@ -0,0 +1,131 @@ +package minesweeper + +import ( + "image/color" + + "golang.org/x/exp/rand" +) + +func RandomUntilNoBomb(cellCount int, bombIndexes map[int]struct{}) int { + r := rand.Intn(cellCount) + + loop := true + for loop { + if _, ok := bombIndexes[r]; !ok { + loop = false + } + r = rand.Intn(cellCount) + } + return r +} + +func (g *Game) XYToIndex(x int, y int) int { + index := y*(g.columns) + x + return index +} + +func (g *Game) IndexToXY(index int) (int, int) { + x := index % (g.columns) + y := index / (g.columns) + return x, y +} + +func (g *Game) CursorPosToIndex(x int, y int) int { + logicX := x / g.CellSize + logicY := y / g.CellSize + + return g.XYToIndex(logicX, logicY) +} + +func (g *Game) CalcNeighbourBombs(index int) int { + bombs := 0 + + g.applyNeighbours(index, func(i int) { + if g.cells[i].cellType == BOMB { + bombs += 1 + } + }) + + return bombs +} + +func (g *Game) SetBomblessNeighboursToOpen(index int) { + g.applyNeighbours(index, func(i int) { + if g.cells[i].bombNeighbours == 0 && g.cells[i].state != OPEN { + g.cells[i].state = OPEN + g.SetBomblessNeighboursToOpen(i) + } else if g.cells[i].cellType != BOMB { + g.cells[i].state = OPEN + } + }) +} + +func (g *Game) applyNeighbours(index int, f func(index int)) { + posX, posY := g.IndexToXY(index) + for x := -1; x <= 1; x++ { + for y := -1; y <= 1; y++ { + if posX+x < 0 || posX+x >= g.rows { + continue + } + if posY+y < 0 || posY+y >= g.columns { + continue + } + + i := g.XYToIndex(posX+x, posY+y) + f(i) + } + } +} + +func MatchColor(state State) color.Color { + switch state { + case OPEN: + return OPEN_COLOR + case DARK: + return DARK_COLOR + case FLAG: + return FLAG_COLOR + default: + return color.Black + } +} + +func (g *Game) SetupCells() ([]Cell, map[int]struct{}) { + cellCount := (g.columns * g.rows) + bombs := float64(cellCount) * (float64(g.bombPercentage) / 100) + cells := make([]Cell, 0) + + bombIndexes := make(map[int]struct{}) + + for range int(bombs) { + r := RandomUntilNoBomb(cellCount, bombIndexes) + bombIndexes[r] = struct{}{} + } + + index := 0 + for x := range g.columns { + for y := range g.rows { + cellType := NO_BOMB + cellColor := color.RGBA{120, 120, 120, 255} + if _, isBomb := bombIndexes[index]; isBomb { + cellType = BOMB + cellColor = color.RGBA{255, 0, 0, 255} + } + + cells = append(cells, Cell{ + top: float32(x * g.CellSize), + left: float32(y * g.CellSize), + color: cellColor, + cellType: cellType, + state: DARK, + }) + index += 1 + } + } + + for i := range cells { + cells[i].bombNeighbours = g.CalcNeighbourBombs(i) + } + + return cells, bombIndexes +}