This code defines a class named PictureBoxGridManager responsible for generating a grid of PictureBoxes and creating a path through it. It initializes with a gridPanel and a toolTip object. The CreatePictureBoxGridCentered method generates a grid of PictureBoxes centered on the screen, calculates rows and columns, and populates the grid with PictureBoxes representing scenery. Additionally, it generates a snake-like path across the grid and places random patches over the scenery. The ClearExistingTiles method removes existing tiles from the grid. The GenerateSnakePath method generates the path, and the PlacePatchesOverScenery method places random patches. Finally, the FillPatch method fills a patch with a specific type.
The code to cover a certain percentage of the path is flawed and fails to function properly. When setting values from 0.1 to 0.9, or even 1, the percentage coverage doesn’t align mathematically. This discrepancy undermines the code’s accuracy and reliability.
This code snippet defines three procedures within the same class. The first, CreatePictureBoxGridCentered, is responsible for creating a grid of PictureBoxes centered on the screen. It first clears any existing tiles, calculates the dimensions of the screen and the grid, initializes the tiles array, creates PictureBoxes for each grid cell, and sets their properties such as size, location, image, and tooltip. Then, it generates a path through the grid using the GenerateSnakePath method and places patches over the remaining scenery using the PlacePatchesOverScenery method.
The second procedure, GenerateSnakePath, generates a path through the grid by starting from the leftmost column and randomly moving right, down, or up until reaching the rightmost column or the specified percentage of tiles for the path. It sets the appropriate image and tooltip for the path tiles.
The third procedure, PlacePatchesOverScenery, randomly places patches (water or mountain) over the remaining scenery tiles in the grid. It selects random starting points for the patches, limits the number of tiles for each patch type to 10% of the total tiles, and ensures that each patch consists of 3 to 5 tiles. It uses the FillPatch method to fill each patch with the chosen patch type and sets the appropriate image and tooltip for the patch tiles.
Private Sub CreatePictureBoxGridCentered()
' Check and clear any old tiles if already initialized
If tiles IsNot Nothing Then
For Each tile As Tile In tiles
If tile IsNot Nothing AndAlso tile.PictureBox IsNot Nothing Then
Me.Controls.Remove(tile.PictureBox)
tile.PictureBox.Dispose()
End If
Next
End If
' Screen dimensions
Dim screenWidth As Integer = Screen.PrimaryScreen.Bounds.Width
Dim screenHeight As Integer = Screen.PrimaryScreen.Bounds.Height
Dim pictureBoxSize As New Size(60, 60)
Dim margin As Integer = 70
Dim usableScreenWidth As Integer = screenWidth - 2 * margin
Dim usableScreenHeight As Integer = screenHeight - 2 * margin
Dim numRows As Integer = usableScreenHeight pictureBoxSize.Height
Dim numCols As Integer = usableScreenWidth pictureBoxSize.Width
Dim startX As Integer = (screenWidth - numCols * pictureBoxSize.Width) 2
Dim startY As Integer = (screenHeight - numRows * pictureBoxSize.Height) 2
' Initialize the tiles array to match grid dimensions
ReDim tiles(numRows * numCols - 1)
' Initialize all tiles as default scenery (using the image)
Dim index As Integer = 0
For row As Integer = 0 To numRows - 1
For col As Integer = 0 To numCols - 1
Dim pictureBox As New PictureBox() With {
.Size = pictureBoxSize,
.Location = New Point(startX + col * pictureBoxSize.Width, startY + row * pictureBoxSize.Height),
.BorderStyle = BorderStyle.FixedSingle,
.Image = My.Resources.scenery_img, ' Set the scenery image
.SizeMode = PictureBoxSizeMode.StretchImage
}
tiles(index) = New Tile(pictureBox, TileType.Scenery)
Me.Controls.Add(pictureBox)
' Set tooltip for initial "Scenery" type
ToolTip1.SetToolTip(pictureBox, "Scenery")
index += 1
Next
Next
' Generate and apply the path
GenerateSnakePath(numRows, numCols, 0.25)
' Apply patches over the remaining scenery
PlacePatchesOverScenery(numRows, numCols)
End Sub
Private Sub GenerateSnakePath(numRows As Integer, numCols As Integer, pathPercentage As Double)
Dim rand As New Random()
Dim totalTiles As Integer = numRows * numCols
Dim requiredPathTiles As Integer = CInt(Math.Ceiling(totalTiles * pathPercentage))
' Initialize the path from the leftmost column at a random row
Dim currentRow As Integer = rand.Next(1, numRows - 1)
Dim currentCol As Integer = 0
Dim pathTiles As New List(Of Tuple(Of Integer, Integer))()
pathTiles.Add(Tuple.Create(currentRow, currentCol))
tiles(currentRow * numCols + currentCol).PictureBox.Image = My.Resources.path_img ' Set path image
tiles(currentRow * numCols + currentCol).Type = TileType.Path
ToolTip1.SetToolTip(tiles(currentRow * numCols + currentCol).PictureBox, "Path") ' Set tooltip for path
' Movement directions: right, down, up, left
Dim directions As New List(Of Tuple(Of Integer, Integer)) From {
Tuple.Create(0, 1), ' right
Tuple.Create(1, 0), ' down
Tuple.Create(-1, 0), ' up
Tuple.Create(0, -1) ' left
}
' Generate the path until reaching the rightmost column or required tiles count
While pathTiles.Count < requiredPathTiles AndAlso currentCol < numCols - 1
' Find potential movements (example)
Dim possibleMoves As New List(Of Tuple(Of Integer, Integer))
' Check right movement
Dim newCol As Integer = currentCol + 1
If newCol < numCols AndAlso Not pathTiles.Contains(Tuple.Create(currentRow, newCol)) Then
possibleMoves.Add(Tuple.Create(currentRow, newCol))
End If
' Check vertical movements (up and down)
For Each direction In {directions(1), directions(2)}
Dim newRow As Integer = currentRow + direction.Item1
If newRow >= 0 AndAlso newRow < numRows AndAlso Not pathTiles.Contains(Tuple.Create(newRow, currentCol)) Then
possibleMoves.Add(Tuple.Create(newRow, currentCol))
End If
Next
' If no moves are possible, exit
If possibleMoves.Count = 0 Then Exit While
' Choose the next move randomly
Dim nextMove = possibleMoves(rand.Next(possibleMoves.Count))
currentRow = nextMove.Item1
currentCol = nextMove.Item2
pathTiles.Add(nextMove)
tiles(currentRow * numCols + currentCol).PictureBox.Image = My.Resources.path_img
tiles(currentRow * numCols + currentCol).Type = TileType.Path
ToolTip1.SetToolTip(tiles(currentRow * numCols + currentCol).PictureBox, "Path")
End While
End Sub
Private Sub PlacePatchesOverScenery(numRows As Integer, numCols As Integer)
Dim rand As New Random()
Dim patchTypes As TileType() = {TileType.Water, TileType.Mountain}
Dim tilesToProcess As New List(Of Tuple(Of Integer, Integer))()
' Prepare list of potential start points for patches, only for remaining scenery tiles
For i As Integer = 0 To tiles.Length - 1
If tiles(i).Type = TileType.Scenery Then
tilesToProcess.Add(New Tuple(Of Integer, Integer)(i numCols, i Mod numCols))
End If
Next
' Shuffle the list to randomize start points
tilesToProcess = tilesToProcess.OrderBy(Function(x) rand.Next()).ToList()
' Limit patches to 10% of total tiles for each type
Dim totalTiles As Integer = tiles.Length
Dim maxPatchTiles As Integer = CInt(totalTiles * 0.1) ' 10% of total tiles
Dim waterTilesCount As Integer = 0
Dim mountainTilesCount As Integer = 0
' Place patches ensuring they are of size 3 to 5 tiles
For Each startTile In tilesToProcess
Dim row As Integer = startTile.Item1
Dim col As Integer = startTile.Item2
Dim index As Integer = row * numCols + col
If tiles(index).Type = TileType.Scenery Then
Dim patchType As TileType = patchTypes(rand.Next(patchTypes.Length))
Dim patchSize As Integer = rand.Next(3, 6) ' Random patch size between 3 and 5 tiles
' Check the patch type and control the number of tiles
Dim currentPatchCount As Integer = If(patchType = TileType.Water, waterTilesCount, mountainTilesCount)
If currentPatchCount + patchSize > maxPatchTiles Then
patchSize = maxPatchTiles - currentPatchCount ' Adjust size to not exceed limit
End If
If patchSize > 0 Then ' Proceed only if there's room for more tiles of this type
FillPatch(row, col, numRows, numCols, patchType, patchSize)
If patchType = TileType.Water Then
waterTilesCount += patchSize
Else
mountainTilesCount += patchSize
End If
End If
End If
Next
End Sub
' Helper method for patch filling
Private Sub FillPatch(row As Integer, col As Integer, numRows As Integer, numCols As Integer, patchType As TileType, patchSize As Integer)
Dim queue As New Queue(Of Tuple(Of Integer, Integer))
queue.Enqueue(New Tuple(Of Integer, Integer)(row, col))
Dim processed As Integer = 0
While queue.Count > 0 AndAlso processed < patchSize
Dim current As Tuple(Of Integer, Integer) = queue.Dequeue()
Dim r As Integer = current.Item1
Dim c As Integer = current.Item2
Dim idx As Integer = r * numCols + c
If r >= 0 AndAlso r < numRows AndAlso c >= 0 AndAlso c < numCols AndAlso tiles(idx).Type = TileType.Scenery Then
tiles(idx).PictureBox.Image = If(patchType = TileType.Water, My.Resources.water_img, My.Resources.mountain_img)
tiles(idx).Type = patchType
processed += 1
' Set tooltip for the new patch type
ToolTip1.SetToolTip(tiles(idx).PictureBox, If(patchType = TileType.Water, "Water", "Mountain"))
' Enqueue neighboring tiles to form a patch
Dim neighbors As Tuple(Of Integer, Integer)() = {
Tuple.Create(r - 1, c), Tuple.Create(r + 1, c),
Tuple.Create(r, c - 1), Tuple.Create(r, c + 1)
}
For Each neighbor In neighbors
Dim nRow As Integer = neighbor.Item1
Dim nCol As Integer = neighbor.Item2
Dim neighborIdx As Integer = nRow * numCols + nCol
If nRow >= 0 AndAlso nRow < numRows AndAlso nCol >= 0 AndAlso nCol < numCols AndAlso
tiles(neighborIdx).Type = TileType.Scenery Then
queue.Enqueue(neighbor)
End If
Next
End If
End While
End Sub
Bernard Aybout is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.