package geom import ( //"fmt" "image/color" "math/rand" "github.com/hajimehoshi/ebiten/v2" ) var defaultSquareSpacing = 5 var minBorder = 20 var ScoreOffset = 50 var HighScoreOffset = 10 var GameGrid *Grid var MaxGridDimension = 20 type Grid struct { squareSpacing int squares [][]*Square startX int startY int squareSize int coloredSquaresRemaining int } // used for comparing two squares positions type Pair struct { rowNum int colNum int } func (pr Pair) equal(pr2 Pair) bool { return pr.rowNum == pr2.rowNum && pr.colNum == pr2.colNum } func pairAlreadyExists(pr Pair, pairs []Pair) bool { result := false for _, nextPair := range pairs { if pr.equal(nextPair) { return true } } return result } func (grd *Grid) NumSquares() int { size := -1 if len(grd.squares[0]) != 0 { size = len(grd.squares) * len(grd.squares[0]) } return size } // returns num rows by num columns func (grd *Grid) Size() (int, int) { if len(grd.squares[0]) != 0 { return len(grd.squares), len(grd.squares[0]) } return 0, 0 } func BuildGrid(numRows int, numColumns int) { grd := Grid{squareSpacing: defaultSquareSpacing} winWidth, winHeight := ebiten.WindowSize() sqSize := min((winWidth-2*minBorder-defaultSquareSpacing*numColumns)/numColumns, (winHeight-2*minBorder-ScoreOffset-3*HighScoreOffset-defaultSquareSpacing*numRows)/numRows) sqSize = max(sqSize, 1) //ensure a nonzero, nonnegative square size gridWidth := numColumns*(sqSize+grd.squareSpacing) - grd.squareSpacing gridHeight := numRows*(sqSize+grd.squareSpacing) - grd.squareSpacing currentX := (winWidth - gridWidth) / 2 currentY := ScoreOffset + (winHeight-gridHeight-ScoreOffset-HighScoreOffset)/2 grd.startX = currentX grd.startY = currentY grd.squareSize = sqSize table := make([][]*Square, numRows) for i := 0; i < numRows; i++ { row := make([]*Square, numColumns) for j := 0; j < numColumns; j++ { sq := MakeSquare(sqSize) sq.SetPosition(currentX, currentY) row[j] = &sq currentX += sqSize + grd.squareSpacing } table[i] = row currentY += sqSize + grd.squareSpacing currentX = grd.startX } grd.squares = table //assign random colours to a subset of all squares squaresToColour := min(numColumns, numRows) usedPositions := make([]Pair, squaresToColour) coloredSoFar := 0 for i := 0; i < squaresToColour; i++ { colourIndex := rand.Intn(len(LightRGBMap)) row := rand.Intn(numRows) + 1 col := rand.Intn(numColumns) + 1 //prevent a colour on the starting square if row == numRows && col == 1 { col = 2 } //make sure we don't colour the same square twice nextPair := Pair{rowNum: row, colNum: col} if coloredSoFar == 0 { usedPositions[0] = nextPair coloredSoFar = 1 } else { for pairAlreadyExists(nextPair, usedPositions) { row = rand.Intn(numRows) + 1 col = rand.Intn(numColumns) + 1 //prevent a colour on the starting square if row == numRows && col == 1 { col = 2 } nextPair = Pair{rowNum: row, colNum: col} } usedPositions[coloredSoFar] = nextPair coloredSoFar += 1 } grd.ChangeColour(row, col, LightRGBMap[colourIndex]) grd.GetSquareAt(row, col).RGBColourIndex = colourIndex } grd.coloredSquaresRemaining = squaresToColour GameGrid = &grd } func (grd *Grid) Draw(screen *ebiten.Image) { opts := &ebiten.DrawImageOptions{} for _, rowOfSquares := range grd.squares { for _, sq := range rowOfSquares { xPos := float64(sq.X()) yPos := float64(sq.Y()) opts.GeoM.Translate(xPos, yPos) screen.DrawImage(sq.Img, opts) opts.GeoM.Reset() } } } func (grd *Grid) DrawUpTo(finalSquareCount int, screen *ebiten.Image) { opts := &ebiten.DrawImageOptions{} curr := 1 for _, rowOfSquares := range grd.squares { for _, sq := range rowOfSquares { xPos := float64(sq.X()) yPos := float64(sq.Y()) opts.GeoM.Translate(xPos, yPos) screen.DrawImage(sq.Img, opts) opts.GeoM.Reset() if curr == finalSquareCount { return } curr++ } } } func (grd *Grid) ChangeColour(row int, column int, clr color.Color) { sq := grd.squares[row-1][column-1] sq.ChangeColour(clr) } func (grd *Grid) GetSquareAt(row int, column int) *Square { return grd.squares[row-1][column-1] } func (grd *Grid) ColouredSquaresRemaining() int { return grd.coloredSquaresRemaining }