Share My Creation ImageSlider

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

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.png
    Screenshot_20250731-235226.png
    479.7 KB · Views: 405
  • Screenshot_20250818-102639.png
    Screenshot_20250818-102639.png
    361.4 KB · Views: 62
  • Screenshot_20250818-102652.png
    Screenshot_20250818-102652.png
    418.1 KB · Views: 53
  • Screenshot_20250818-102817.png
    Screenshot_20250818-102817.png
    453 KB · Views: 47
  • Screenshot_20250818-103939.png
    Screenshot_20250818-103939.png
    450.9 KB · Views: 48
  • puzzle1.jpg
    puzzle1.jpg
    146.9 KB · Views: 48
  • puzzle2.jpg
    puzzle2.jpg
    140.7 KB · Views: 45
  • puzzle3.jpg
    puzzle3.jpg
    320.7 KB · Views: 44
  • puzzle4.jpg
    puzzle4.jpg
    151.5 KB · Views: 40
  • puzzle5.jpg
    puzzle5.jpg
    355.6 KB · Views: 44
  • puzzle6.jpg
    puzzle6.jpg
    483.4 KB · Views: 62
Top