Hi, I've been playing with B4A to create my old-time favorite games, and given I'm 68 they go back a few years. This is a version of the old plastic square slider, where usually you have a grid of either 3 or four tiles with one blank space that you can move an adjacent tile into.
The game starts off with a 3x3 grid, which is pretty easy to complete, then becomes progressively harder, increasing the grid by one, i.e, 4x4, then 5x5, etc., up to an 8x8 grid. I've never gotten past 5x5
Hope you like it, I've included the source code below, tried to zip but too large to attach, please insert the six puzzles, puzzle1.jpg...puzzle6.jpg into the files folder or use six of your own.
I have completed several more games, which I'll share when finished and error-free. I think the only additional library I have used is JoyStickView for a couple of the games
Ballon Pop
BlockStacker
Bombs and Fruit
Breakout
Frak (ish)
Tower of Hanoi
MooTest
Pacman
Space Invaders
Tetris
Pipe Connect
The game starts off with a 3x3 grid, which is pretty easy to complete, then becomes progressively harder, increasing the grid by one, i.e, 4x4, then 5x5, etc., up to an 8x8 grid. I've never gotten past 5x5
Hope you like it, I've included the source code below, tried to zip but too large to attach, please insert the six puzzles, puzzle1.jpg...puzzle6.jpg into the files folder or use six of your own.
I have completed several more games, which I'll share when finished and error-free. I think the only additional library I have used is JoyStickView for a couple of the games
Ballon Pop
BlockStacker
Bombs and Fruit
Breakout
Frak (ish)
Tower of Hanoi
MooTest
Pacman
Space Invaders
Tetris
Pipe Connect
B4X:
#Region Project Attributes
#ApplicationLabel: DITL ImageSlider
#VersionCode: 1
#VersionName: 1.1
#SupportedOrientations: portrait
#CanInstallToExternalStorage: False
#End Region
#Region Activity Attributes
#FullScreen: False
#IncludeTitle: True
#BridgeLogger: True
#End Region
Sub Process_Globals
Private Timer1 As Timer
End Sub
Sub Globals
Private pnlMain As Panel
Private btnTiles() As Button ' Dynamic array for tiles
Private gameGrid(8,8) As Int ' Dynamic 2D grid
Private emptyRow, emptyCol As Int
Private lblMoves As Label
Private lblLevel As Label
Private lblLevelInfo As Label
Private btnShuffle As Button
Private btnNewGame As Button
Private btnRestart As Button
Private btnShowImage As Button
Private moveCount As Int
Private tileSize As Int
Private margin As Int = 8dip
Private currentLevel As Int
Private gridSize As Int
Private totalMoves As Int
Private maxLevel As Int = 6 ' 6 levels (3x3 to 8x8)
' Image handling
Private currentImage As Bitmap
Private tileBitmaps() As Bitmap
Private imageNames() As String
End Sub
Sub Activity_Create(FirstTime As Boolean)
Timer1.Initialize("Timer1", 2000) ' 2 second delay
InitializeImageNames
InitializeGame
StartLevel(3) ' Start with 3x3 grid
End Sub
Sub InitializeImageNames
' Define the image file names for each level
' You need to add these 6 images to your Files folder in B4A
Dim tempNames(6) As String
tempNames(0) = "puzzle1.jpg" ' Level 1 (3x3)
tempNames(1) = "puzzle2.jpg" ' Level 2 (4x4)
tempNames(2) = "puzzle3.jpg" ' Level 3 (5x5)
tempNames(3) = "puzzle4.jpg" ' Level 4 (6x6)
tempNames(4) = "puzzle5.jpg" ' Level 5 (7x7)
tempNames(5) = "puzzle6.jpg" ' Level 6 (8x8)
imageNames = tempNames
End Sub
Sub InitializeGame
' Calculate base tile size
Dim baseSize As Int = (100%x - (9 * margin)) / 8 ' Base on max 8x8 grid
' Create main panel
pnlMain.Initialize("")
Activity.AddView(pnlMain, margin, 140dip, 100%x - (2 * margin), 100%x - (2 * margin))
pnlMain.Color = Colors.DarkGray
' Create level info label
lblLevel.Initialize("")
lblLevel.Text = "Level 1 (3x3)"
lblLevel.TextSize = 24
lblLevel.TextColor = Colors.Blue
lblLevel.Gravity = Gravity.CENTER
Activity.AddView(lblLevel, margin, 20dip, 100%x - (2 * margin), 40dip)
' Create level description
lblLevelInfo.Initialize("")
lblLevelInfo.Text = "Arrange the image pieces!"
lblLevelInfo.TextSize = 16
lblLevelInfo.TextColor = Colors.Black
lblLevelInfo.Gravity = Gravity.CENTER
Activity.AddView(lblLevelInfo, margin, 65dip, 100%x - (2 * margin), 30dip)
' Create move counter label
lblMoves.Initialize("")
lblMoves.Text = "Moves: 0 | Total: 0"
lblMoves.TextSize = 18
lblMoves.TextColor = Colors.Black
lblMoves.Gravity = Gravity.CENTER
Activity.AddView(lblMoves, margin, 100dip, 100%x - (2 * margin), 35dip)
' Create buttons
btnShuffle.Initialize("btnShuffle")
btnShuffle.Text = "Shuffle"
btnShuffle.TextSize = 12
Activity.AddView(btnShuffle, margin, 100%y - 120dip, 70dip, 35dip)
btnRestart.Initialize("btnRestart")
btnRestart.Text = "Restart"
btnRestart.TextSize = 12
Activity.AddView(btnRestart, margin + 80dip, 100%y - 120dip, 70dip, 35dip)
btnShowImage.Initialize("btnShowImage")
btnShowImage.Text = "Show Image"
btnShowImage.TextSize = 12
Activity.AddView(btnShowImage, margin + 160dip, 100%y - 120dip, 90dip, 35dip)
btnNewGame.Initialize("btnNewGame")
btnNewGame.Text = "New Game"
btnNewGame.TextSize = 12
Activity.AddView(btnNewGame, 100%x - 80dip, 100%y - 120dip, 70dip, 35dip)
currentLevel = 1
totalMoves = 0
End Sub
Sub StartLevel(size As Int)
gridSize = size
moveCount = 0
' Update level display
lblLevel.Text = "Level " & currentLevel & " (" & gridSize & "x" & gridSize & ")"
If currentLevel = 1 Then
lblLevelInfo.Text = "Arrange the image pieces!"
Else
lblLevelInfo.Text = "Level " & (currentLevel - 1) & " completed! New image:"
End If
' Calculate tile size for current grid FIRST
tileSize = (pnlMain.Width - ((gridSize + 1) * margin)) / gridSize
' Make sure tileSize is valid
If tileSize <= 0 Then
tileSize = 50dip ' Fallback minimum size
End If
' Clear existing tiles
pnlMain.RemoveAllViews
' Initialize dynamic arrays
Dim totalTiles As Int = gridSize * gridSize
Dim btnTilesTemp(totalTiles) As Button
btnTiles = btnTilesTemp
Dim gridTemp(gridSize, gridSize) As Int
gameGrid = gridTemp
LoadLevelImage
CreateGameBoard
NewLevel
End Sub
Sub LoadLevelImage
Try
' Load the image for current level
Dim imageFileName As String = imageNames(currentLevel - 1)
currentImage = LoadBitmap(File.DirAssets, imageFileName)
' Create scaled version that fits our tile area
Dim boardSize As Int = tileSize * gridSize
' Resize the bitmap using B4A's Bitmap methods
Dim scaledBitmap As Bitmap
scaledBitmap.InitializeMutable(boardSize, boardSize)
Dim canvas As Canvas
canvas.Initialize2(scaledBitmap)
Dim srcRect As Rect
srcRect.Initialize(0, 0, currentImage.Width, currentImage.Height)
Dim destRect As Rect
destRect.Initialize(0, 0, boardSize, boardSize)
canvas.DrawBitmap(currentImage, srcRect, destRect)
currentImage = scaledBitmap
' Slice image into tiles
SliceImageIntoTiles
Catch
Log("Error loading image: " & LastException)
CreateFallbackTiles
End Try
End Sub
Sub SliceImageIntoTiles
' Create bitmap array for tiles
Dim totalTiles As Int = gridSize * gridSize
Dim tempTileBitmaps(totalTiles) As Bitmap
tileBitmaps = tempTileBitmaps
' Create canvas for slicing
Dim srcCanvas As Canvas
Dim destCanvas As Canvas
For row = 0 To gridSize - 1
For col = 0 To gridSize - 1
Dim index As Int = row * gridSize + col
' Skip the last tile (empty space)
If index < totalTiles - 1 Then
' Create bitmap for this tile piece
tileBitmaps(index).InitializeMutable(tileSize, tileSize)
destCanvas.Initialize2(tileBitmaps(index))
' Calculate source rectangle
Dim srcRect As Rect
srcRect.Initialize(col * tileSize, row * tileSize, (col + 1) * tileSize, (row + 1) * tileSize)
' Calculate destination rectangle
Dim destRect As Rect
destRect.Initialize(0, 0, tileSize, tileSize)
' Draw the slice
destCanvas.DrawBitmap(currentImage, srcRect, destRect)
End If
Next
Next
End Sub
Sub CreateFallbackTiles
' Create colored tiles with numbers as fallback
Dim totalTiles As Int = gridSize * gridSize
Dim tempTileBitmaps(totalTiles) As Bitmap
tileBitmaps = tempTileBitmaps
For i = 0 To totalTiles - 2
tileBitmaps(i).InitializeMutable(tileSize, tileSize)
Next
End Sub
Sub CreateGameBoard
Dim row, col As Int
' Initialize tiles
For row = 0 To gridSize - 1
For col = 0 To gridSize - 1
Dim index As Int = row * gridSize + col
btnTiles(index).Initialize("btnTile")
Dim boardWidth As Int = gridSize * tileSize + (gridSize - 1) * margin
Dim startX As Int = (pnlMain.Width - boardWidth) / 2
Dim startY As Int = (pnlMain.Height - boardWidth) / 2
Dim left As Int = startX + col * (tileSize + margin)
Dim top As Int = startY + row * (tileSize + margin)
pnlMain.AddView(btnTiles(index), left, top, tileSize, tileSize)
btnTiles(index).Text = "" ' No text for image tiles
btnTiles(index).Color = Colors.Transparent
Next
Next
End Sub
Sub NewLevel
' Initialize solved state
Dim row, col As Int
For row = 0 To gridSize - 1
For col = 0 To gridSize - 1
gameGrid(row, col) = row * gridSize + col + 1
Next
Next
' Set empty space (last position becomes 0)
gameGrid(gridSize - 1, gridSize - 1) = 0
emptyRow = gridSize - 1
emptyCol = gridSize - 1
UpdateDisplay
ShuffleBoard
End Sub
Sub ShuffleBoard
' Perform random valid moves to shuffle (more moves for larger grids)
Dim shuffleMoves As Int = gridSize * gridSize * 200
Dim i As Int
For i = 1 To shuffleMoves
Dim validMoves As List
validMoves.Initialize
' Find all valid moves
If emptyRow > 0 Then validMoves.Add(Array(emptyRow - 1, emptyCol)) ' Up
If emptyRow < gridSize - 1 Then validMoves.Add(Array(emptyRow + 1, emptyCol)) ' Down
If emptyCol > 0 Then validMoves.Add(Array(emptyRow, emptyCol - 1)) ' Left
If emptyCol < gridSize - 1 Then validMoves.Add(Array(emptyRow, emptyCol + 1)) ' Right
' Pick random valid move
If validMoves.Size > 0 Then
Dim randomIndex As Int = Rnd(0, validMoves.Size)
Dim move(2) As Int
Dim tempArray() As Object = validMoves.Get(randomIndex)
move(0) = tempArray(0)
move(1) = tempArray(1)
MakeMoveInternal(move(0), move(1))
End If
Next
moveCount = 0 ' Reset move counter after shuffle
UpdateDisplay
End Sub
Sub MakeMoveInternal(row As Int, col As Int)
' Internal move without incrementing counter
gameGrid(emptyRow, emptyCol) = gameGrid(row, col)
gameGrid(row, col) = 0
emptyRow = row
emptyCol = col
End Sub
Sub btnTile_Click
Dim btn As Button = Sender
Dim clickedIndex As Int = -1
Dim i As Int
For i = 0 To btnTiles.Length - 1
If btnTiles(i) = btn Then
clickedIndex = i
Exit
End If
Next
If clickedIndex = -1 Then Return
Dim clickedRow As Int = clickedIndex / gridSize
Dim clickedCol As Int = clickedIndex Mod gridSize
' Check if move is valid (adjacent to empty space)
If IsValidMove(clickedRow, clickedCol) Then
gameGrid(emptyRow, emptyCol) = gameGrid(clickedRow, clickedCol)
gameGrid(clickedRow, clickedCol) = 0
emptyRow = clickedRow
emptyCol = clickedCol
moveCount = moveCount + 1
totalMoves = totalMoves + 1
UpdateDisplay
' Check for win
If CheckWin Then
' Level completed!
Dim message As String
If currentLevel < maxLevel Then
currentLevel = currentLevel + 1
message = "Level " & (currentLevel - 1) & " completed in " & moveCount & " moves!" & CRLF & "Advancing to " & (gridSize + 1) & "x" & (gridSize + 1) & " grid..."
' Advance to next level after short delay
Timer1.Enabled = True
Else
message = "Congratulations! You completed ALL levels!" & CRLF & "Final level completed in " & moveCount & " moves." & CRLF & "Total moves: " & totalMoves
End If
ToastMessageShow(message, True)
End If
End If
End Sub
Sub Timer1_Tick
Timer1.Enabled = False
StartLevel(gridSize + 1)
End Sub
Sub IsValidMove(row As Int, col As Int) As Boolean
' Check if the clicked position is adjacent to empty space
Dim rowDiff As Int = Abs(row - emptyRow)
Dim colDiff As Int = Abs(col - emptyCol)
Return (rowDiff = 1 And colDiff = 0) Or (rowDiff = 0 And colDiff = 1)
End Sub
Sub UpdateDisplay
Dim row, col As Int
For row = 0 To gridSize - 1
For col = 0 To gridSize - 1
Dim index As Int = row * gridSize + col
Dim value As Int = gameGrid(row, col)
If value = 0 Then
btnTiles(index).Background = Null
btnTiles(index).Color = Colors.DarkGray
btnTiles(index).Visible = False
Else
btnTiles(index).Visible = True
If tileBitmaps(value - 1).IsInitialized Then
Dim bmpDrawable As BitmapDrawable
bmpDrawable.Initialize(tileBitmaps(value - 1))
btnTiles(index).Background = bmpDrawable
Else
btnTiles(index).Text = value
btnTiles(index).Color = Colors.LightGray
End If
End If
Next
Next
lblMoves.Text = "Moves: " & moveCount & " | Total: " & totalMoves
End Sub
Sub CheckWin As Boolean
Dim row, col As Int
For row = 0 To gridSize - 1
For col = 0 To gridSize - 1
Dim expectedValue As Int
If row = gridSize - 1 And col = gridSize - 1 Then
expectedValue = 0
Else
expectedValue = row * gridSize + col + 1
End If
If gameGrid(row, col) <> expectedValue Then
Return False
End If
Next
Next
Return True
End Sub
Sub btnShuffle_Click
ShuffleBoard
End Sub
Sub btnRestart_Click
NewLevel
End Sub
Sub btnShowImage_Click
Dim imageDialog As Panel
imageDialog.Initialize("")
Activity.AddView(imageDialog, 0, 0, 100%x, 100%y)
imageDialog.Color = Colors.ARGB(200, 0, 0, 0)
Dim imageView As ImageView
imageView.Initialize("imageView")
Dim imageSize As Int = Min(80%x, 80%y)
imageDialog.AddView(imageView, (100%x - imageSize) / 2, (100%y - imageSize) / 2, imageSize, imageSize)
imageView.Bitmap = currentImage
' Remove after 3 seconds
Dim hideTimer As Timer
hideTimer.Initialize("hideTimer", 3000)
hideTimer.Enabled = True
imageDialog.Tag = hideTimer
End Sub
Sub hideTimer_Tick
Dim timer As Timer = Sender
timer.Enabled = False
For i = Activity.NumberOfViews - 1 To 0 Step -1
Dim v As View = Activity.GetView(i)
If v Is Panel Then
Dim p As Panel = v
If p.Tag Is Timer And p.Tag = timer Then
Activity.RemoveViewAt(i)
Exit
End If
End If
Next
End Sub
Sub btnNewGame_Click
currentLevel = 1
totalMoves = 0
StartLevel(3) ' Start over with 3x3
End Sub
Sub Activity_Resume
End Sub
Sub Activity_Pause (UserClosed As Boolean)
End Sub
Attachments
-
Screenshot_20250731-235226.png479.7 KB · Views: 416
-
Screenshot_20250818-102639.png361.4 KB · Views: 62
-
Screenshot_20250818-102652.png418.1 KB · Views: 53
-
Screenshot_20250818-102817.png453 KB · Views: 47
-
Screenshot_20250818-103939.png450.9 KB · Views: 48
-
puzzle1.jpg146.9 KB · Views: 48
-
puzzle2.jpg140.7 KB · Views: 45
-
puzzle3.jpg320.7 KB · Views: 44
-
puzzle4.jpg151.5 KB · Views: 41
-
puzzle5.jpg355.6 KB · Views: 45
-
puzzle6.jpg483.4 KB · Views: 62