Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Relax game - Puzzle
#2
(09-29-2024, 07:21 PM)Petr Wrote: Hi.

When I was working on the free-select image program, I didn't know that it would have consequences. An idea came during development.

And so this game was born. It's meant to be a relaxing puzzle. It contains 19 photos, some are from my private collection, but there is nothing personal, some are downloaded from the Internet. The principle of the game is simple.

The program uses the photo as if it were a painting on glass and breaks it like you break glass. The resulting pieces  then player put together to create the original image. No score, nothing like that, it's not a competition. Really small parts are banned because no one wants to mouse hunt a 4x3 pixel piece...

Tell us what you think about it.

Finally.... you know how a relaxing game turns into a hellish game? Quite simply, I could write a novel about memory leak detection.
But of course it is fixed in this version.

Code: (Select All)

'Puzzle / Relax Game writed Petr Preclik 09-27-2024 to 09-29-2024


Dim As Long Img, Mask '          Img is image (from directory images), Mask is NewImage image


'                                  easyest loader...too lasy to implant here direntry...


'                                +---------------------------------------+
'                                | Create images names to array ImaArray |
'                                +---------------------------------------+

Dim ImaArray(19) As String '            19 images in images folder
imgIndex = 0 '                          start from zero (0.jpg)
For fillIma = 19 To 0 Step -1
    Path$ = ".\images\" + LTrim$(Str$(fillIma)) + ".jpg"
    ImaArray(fillIma) = Path$
Next 


Screen _NewImage(_DesktopWidth, _DesktopHeight, 32)
Mask& = _NewImage(_DesktopWidth, _DesktopHeight, 32)

'                                +-----------------------------------------+
'                                |My Screen and Mask virtual screen declare|
'                                +-----------------------------------------+

restart: '                        here is used.... GoTo if player's chioce is try next image
Cls
_FullScreen
ReDim Shared G(0) As Long '        Graphic array (contains indexes array P and this set images order on screen. Ubound is always visible (get to screen as last)
'                                  previous image IMG is deleted from memory in row 316 and IMG is set to 0 if next image is played
Img = _LoadImage(ImaArray(imgIndex), 32)
Text1$ = "Generating Puzzle, please wait..."
CenterX = _Width \ 2 - _PrintWidth(Text1$) \ 2
CenterY = _Height \ 2 - 16
_PrintString (CenterX, CenterY), Text1$
'

'                                1) Mask border is painted as white line
_Dest Mask&
Cls '                                mask must have black background
Line (0, 1)-(_Width(Mask&) - 1, _Height(Mask&) - 1), _RGB32(255), B

'                                2) To mask is genereated some random form in white color
Dim f As _Unsigned _Byte
For form = 0 To 3
    Randomize Timer
    f = 1 + Rnd * 7
    PlaceForm f, Mask&, _RGB32(255) 'PlaceForm create some white forms
Next form
PlaceForm 7, Mask&, _RGB32(255)
PlaceForm 7, Mask&, _RGB32(255)


'                                +-----------------------------------------+
'                                |  My game declare, arrays and variables  |
'                                +-----------------------------------------+



'                                3) define the Puzzle type, scan the screen, write the shapes in the field and color the individual shapes.

Type Puzz
    As Integer X, Y, pX, pY '    X, Y is real position in image (left upper corner which contains non zero pixel), pX, pY is position on the screen
    As _Unsigned _Byte R, G, B ' mask color - used in program begining (row 83)
    As Long Handle '            image handle (part of complete image)
    As _Byte Locked '          if is image moved to correct place, is this 1 else is 0. If is set to 1, player can't moving this part
    '                          default angle is always 0 in this version, rotation is not used in this first version
End Type

ReDim Shared P(0) As Puzz '    Game array

'                              4) searching and coloring individual parts of the mask
_Source Mask&
_Dest Mask& '                      Part image size is not set here, but on row  528, 529 in SUB GiveForm
B = 150
R = 127
For Y = 0 To _Height(Mask&)
    For X = 0 To _Width(Mask&)
        p~& = Point(X, Y)
        If p~& = _RGB32(0) Then
            B = B + 1
            If B > 255 Then B = 0: G = G + 1
            If G > 255 Then G = 0: R = R + 1
            Paint (X, Y), _RGB32(R, G, B), _RGB32(255)
            UbP = UBound(P)
            P(UbP).R = R
            P(UbP).G = G
            P(UbP).B = B
            UbP = UbP + 1
            ReDim _Preserve P(UbP) As Puzz
        End If
    Next X
Next Y
_Dest 0

'                                +---------------------+
'                                |  Call GiveForm SUB  |
'                                +---------------------+


'                                5) the GiveForm SUB does all of the above:
'                                  1) place one concrete shape on the virtual screen
'                                  2) find the dimensions of the shape (width and height)
'                                  3) create a suitable virtual screen and copies the shape onto it and then closes the original virtual screen
'                                  4) return the image descriptor of this shape in P().handle
GiveForm Mask&, Img, P()
'                                  GiveForm (Mask image contains colored parts, always one part = one color; IMG is loaded image file, P() is game array)


'                                +---------------------+
'                                |    Level logic    |
'                                +---------------------+

'                                5b) calculate how many pieces will be generated (small pieces are not generated) - i try it, but.... move part 1x3 pixels.... NO! NO! NO!
'                                    not all .Handle contains image descriptor. If is image too small, here is 0, this image is not used in game and therefore must be real number calculated.
TotalPieces = 0
For a = 0 To UBound(P) - 1
    If P(a).Handle < -1 Then TotalPieces = TotalPieces + 1
Next a


'                                6) Generate parts start position on the screen - you can delete it, then all parts in game are in left upper corner
For s = 0 To UBound(P)
    imgX = Rnd * _Width
    If imgX > _Width(0) - _Width(P(s).Handle) Then imgX = _Width(0) - _Width(P(s).Handle)
    imgY = Rnd * _Height
    If imgY > _Height(0) - _Height(P(s).Handle) Then imgY = _Height(0) - _Height(P(s).Handle)
    P(s).pX = imgX
    P(s).pY = imgY
Next s
'                                +---------------------+
'                                |      Sort Sub      |
'                                +---------------------+


Sort '                        It calculates the area of ??individual images according to the width and height in the P().Handle array and
'                            arranges them in the G array so that the largest are below and the smallest above. The smallest part is then
'                            rendered last. But.... (keep reading it)
Cls , _RGB32(200)
Show
'                            Show displays parts by field G. Image in UBOUND field G is shown last

Completed = 0 '              Number parts placed to correct image area
Do Until k& = 27
    Show
    k& = _KeyHit '          You read source code? You are doing good, keep it up!
    While _MouseInput: Wend
    MX = _MouseX
    MY = _MouseY
    LB = _MouseButton(1)


    For T = UBound(P) - 1 To 0 Step -1

        Id = T
        If P(Id).Handle < 0 Then '                          if Handle is valid,
            If LB = -1 Then '                                if left mouse button is pressed,
                s = _Source
                _Source P(Id).Handle
                sc~& = Point(MX - P(Id).pX, MY - P(Id).pY)
                If _Alpha32(sc~&) > 0 Then '                if mouse is placed on visible area on the part,

                    Xs = P(Id).pX
                    Ys = P(Id).pY
                    Xe = Xs + _Width(P(Id).Handle) - 1
                    Ye = Ys + _Height(P(Id).Handle) - 1

                    If MX >= Xs And MX <= Xe Then
                        If MY >= Ys And MY <= Ye Then
                            If P(T).Locked = 0 Then '        if part is not moved on the screen in correct place (if locked is 1, is not possible moving it)

                                Do Until LB = 0 '            LOOP until left mouse button is pressed

                                    While _MouseInput
                                    Wend
                                    MX = _MouseX
                                    MY = _MouseY
                                    LB = _MouseButton(1)

                                    If P(T).Locked = 0 Then 'if part is not moved on the screen in correct place (if locked is 1, is not possible moving it)
                                        MyT = T

                                        If posX = 0 Then '  calculate and memorize mouse position and part left upper corner position - for moving on the screen
                                            posX = MX - Xs
                                            PosY = MY - Ys
                                            oMX = MX
                                            oMY = MY
                                        End If

                                        P(T).pX = MX - posX 'write to array new part position on the screen
                                        P(T).pY = MY - PosY

                                        '                  if we go to the correct position with a tolerance of 10 pixels, it will lock the part
                                        If Abs(P(T).X - P(T).pX) < 10 Then
                                            If Abs(P(T).Y - P(T).pY) < 10 Then
                                                P(T).pX = P(T).X
                                                P(T).pY = P(T).Y
                                                P(T).Locked = 1 'ok
                                                _SetAlpha 220, _RGB32(0) To _RGB32(255), P(T).Handle
                                                Completed = Completed + 1 '                              this is counter for correct placed parts
                                                Sound 250, .5
                                                LB = 0
                                                Exit Do
                                            End If
                                        End If
                                    End If
                                    _Display
                                    MoveShow T ' If the part is moved, the display is done by this SUB. The only difference is that it ensures that the piece being moved is always visible.


                                    MovedImage = T '                                      note which piece was moved last
                                    '                                                      congrats, if you study source code: program small hack (move piece to 10, 10 or less)
                                    Hck& = _KeyDown(100303) '                                                                  press and hold right shift for hack
                                    If Hck& Then '                                                                            use if you don't see correct target position for part when is moved!
                                        Locate 3
                                        Print "Move to 10, 10 or less: "; Abs(P(T).X - P(T).pX), Abs(P(T).Y - P(T).pY)
                                    End If

                                    _Display
                                    _Limit 30
                                Loop


                                If Completed = TotalPieces Then '                              if all parts are in correct position,
                                    _PrintMode _FillBackground
                                    For a = 0 To UBound(P) - 1
                                        _SetAlpha 255, _RGB32(0) To _RGB32(255), P(a).Handle ' set for all parts full alpha
                                    Next a
                                    Show '                                                    diplay it
                                    _Display
                                    Sleep 3
                                    _AutoDisplay
                                    msg = _MessageBox("Puzzle", "Try next image?", "yesno", "question", 1)
                                    Select Case msg
                                        Case 0 '                                                if player doesn't want continue,
                                            Screen 0
                                            Print "Good bye..." '                              print message
                                            For a = 0 To UBound(P)
                                                If P(a).Handle < 0 Then _FreeImage P(a).Handle 'erase all images from memory
                                            Next a
                                            _FreeImage Mask& '                                  erase mask
                                            _FreeImage Img& '                                  erase image loaded in begin
                                            System '                                            exit to system
                                        Case 1
                                            _FreeImage Img& '                                  if player continue to next image,
                                            Img = 0 '                                          erase previous image loaded in begin from memory,
                                            For a = 0 To UBound(P)
                                                If P(a).Handle < 0 Then _FreeImage P(a).Handle 'arase all parts images
                                            Next a
                                            ReDim G(0) As Long '                              reset graphic array to 0
                                            ReDim P(0) As Puzz '                              reset game array to 0
                                            Erase P '                                          delete game array
                                            Erase G '                                          delete graphic array


                                            imgIndex = imgIndex + 1 '                          images counter up to 1 (used in begin)
                                            If imgIndex > UBound(ImaArray) Then imgIndex = LBound(ImaArray)
                                            GoTo restart '                                    skip to program begin
                                    End Select
                                End If

                                '                                +-------------------------------------------------------------------------------+
                                '                                |the program only goes to this area once every time the mouse button is released|
                                '                                +-------------------------------------------------------------------------------+
                                posX = 0 '                        this variable now reset mouse settings used on row 189
                                _Dest 0

                                If MovedImage > -1 Then '        see row 217
                                    MyIndexP = MovedImage '      this block shift images in graphic array so, when you click to part, this is placed to UBOUND,
                                    ShiftIndex = 0 '            se when is mouse button released, is not last moved piece "in backround" behind the other pieces
                                    For srt = 0 To UBound(G) - 1
                                        If G(srt) = MyIndexP Then ShiftIndex = 1
                                        G(srt) = G(srt + ShiftIndex)
                                    Next srt
                                    ubG = UBound(G)
                                    G(ubG) = MyIndexP
                                    MovedImage = -1
                                End If
                            End If
                        End If
                    End If
                End If
                _Source s
            End If
        End If
    Next T

    '                                                        small program for on-screen text in left upper corner
    Locate 1 '                                                and help - show parts which are not placed when H is pressed
    _PrintMode _KeepBackground
    Print "Done: "; Completed; " / "; TotalPieces; "["; Int((Completed / TotalPieces) * 100); "%]"
    Print "Can't find part? Press H!"
    Kbd$ = InKey$
    If UCase$(Kbd$) = "H" Then
        D = _Dest
        _Dest 0
        For a = 0 To UBound(P)
            If P(a).Handle < -1 And P(a).Locked = 0 Then
                MiddleX = (P(a).pX + _Width(P(a).Handle) \ 2)
                MiddleY = (P(a).pY + _Height(P(a).Handle) \ 2)

                _AutoDisplay
                For h = 1 To 5
                    For Circl = 10 To 30
                        Circle (MiddleX, MiddleY), Circl, _RGB32(255 - 128 / circ, 10, 210 / circ)
                    Next
                    _Delay .01
                    For Circl = 30 To 10 Step -1
                        Circle (MiddleX, MiddleY), Circl, _RGB32(255 - 128 / circ, 10, 210 / circ)
                    Next
                    _Delay .01
                Next h
            End If
        Next a
        _Dest D
    End If
    _Display
Loop



End


Sub Show
    _Dest 0
    Cls , _RGB32(200)
    For s = 0 To UBound(G)
        '                                                                  SUB contains two steps: First - display correct placed parts,
        If P(G(s)).Handle < 0 Then
            If P(G(s)).Locked = 1 Then
                imgX = P(G(s)).pX
                If imgX > _Width(0) + _Width(P(G(s)).Handle) Then imgX = _Width(0) - _Width(P(G(s)).Handle)
                imgY = P(G(s)).pY
                If imgY > _Height(0) + _Height(P(G(s)).Handle) Then imgY = _Height(0) - _Height(P(G(s)).Handle)
                _PutImage (imgX, imgY), P(G(s)).Handle
            End If
        End If
    Next s

    For s = 0 To UBound(G)
        '                                                                  Second - then show all others parts - so is not possible "hide" free parts
        If P(G(s)).Handle < 0 Then
            If P(G(s)).Locked = 0 Then
                imgX = P(G(s)).pX
                If imgX > _Width(0) + _Width(P(G(s)).Handle) Then imgX = _Width(0) - _Width(P(G(s)).Handle)
                imgY = P(G(s)).pY
                If imgY > _Height(0) + _Height(P(G(s)).Handle) Then imgY = _Height(0) - _Height(P(G(s)).Handle)
                _PutImage (imgX, imgY), P(G(s)).Handle
                ' _Delay .1
            End If
        End If
    Next s
End Sub


Sub MoveShow (id)
    Show
    If P(id).Handle < 0 Then
        imgX = P(id).pX '                                                  the same as show, but moved part is always displayed on top
        imgY = P(id).pY '                                                  (sub is used when part is moved with mouse)
        _PutImage (imgX, imgY), P(id).Handle
    End If
End Sub


Sub Sort '                                                                  according to the area of ??the picture, place the indexes of the P field
    '                                                                      for rendering in the G field. The largest area will be the first (index 0)

    '                                                                  1) Create array P2 and write here area (width * height) for image the same index in array P
    ubP = UBound(P) '                                                    this is not very good method, because transparency pixels are here calculated as full pixels...
    ReDim p2(ubP) As Long
    For copy = 0 To ubP
        p2(copy) = _Width(P(copy).Handle) * _Height(P(copy).Handle)
    Next

    '                                                                  go through field P2, find the largest area, write it in field G and then set it to zero in field P2
    ReDim G(ubP) As Long

    Do Until All = ubP
        i = 0
        max = 0
        Rec = 0
        Do Until i = ubP
            If p2(i) > 0 Then
                If max < p2(i) Then max = p2(i): Rec = i
            End If
            i = i + 1
        Loop

        If max Then
            p2(Rec) = 0
            G(iG) = Rec '                                              set the value of the index of the field P in field G
            iG = iG + 1
        End If

        All = All + 1
    Loop
    ReDim p2(0) As Long
    Erase p2
End Sub


Sub GiveForm (source As Long, img As Long, images() As Puzz) '            returns individual tiles as individual images
    '                                                                    source is MASK source image, img is input image, images() is array for image parts

    ReDim As Long X, Y, MinX, MinY, MaxX, maxY, Virtual, V2, Allimages, Ui
    Cls
    For Allimages = 0 To UBound(P) - 1
        Kolor~& = _RGB32(P(Allimages).R, P(Allimages).G, P(Allimages).B) 'read mask color for THIS part
        X = 0
        Y = 0
        MinX = _Width(source)
        MaxX = 0
        MinY = _Height(source)
        maxY = 0

        '                                                                find image size and copy element to virtual screen in 1 step
        Virtual = _NewImage(_Width(source), _Height(source), 32)
        _Source source
        _Dest Virtual
        Cls '                                                            Background color in Virtual must be set as black
        Do Until Y = _Height(source) - 1
            X = 0
            Do Until X = _Width(source) - 1
                CC~& = Point(X, Y)
                If CC~& = Kolor~& Then
                    If MinY > Y Then MinY = Y
                    If maxY < Y Then maxY = Y
                    If MinX > X Then MinX = X
                    If MaxX < X Then MaxX = X
                    PSet (X, Y), Kolor~& '                              draw 1 part to virtual screen (which use the same size as mask image)
                End If '                                                and measure real image width and height
                X = X + 1 '                                            (you draw here only 1 mask for 1 part image to Virtual)
            Loop
            Y = Y + 1
        Loop

        '---------------------------
        P(Allimages).X = MinX '                                        put left upper corner position for this 1 part to array P
        P(Allimages).Y = MinY
        '---------------------------

        If MaxX - MinX < 10 Or maxY - MinY < 10 Then '                  this condition block creating really very small parts (10x10 pixels and less)                                                                              '
            _FreeImage Virtual '                                        HERE WAS memory leak - miss _freeimage before... - _continue skip _freeimage...
            _Continue '                                                do not create really small pieces
        End If

        _SetAlpha 0, Kolor~&, Virtual '                                set color in mask image as transparent
        V2 = _NewImage(_Width(source), _Height(source), 32) '          create image the same size as mask
        d = _Dest
        _Dest V2
        Cls , _RGB32(50) '                                              set this image background color (also set colors intensity on the game screen)
        _Dest d

        RatioX = _DesktopWidth / _Width(img&) '                        RotioX, RatioY - calculate the aspect ratio of the image
        RatioY = _DesktopHeight / _Height(img&) '                      relative to the aspect ratio of the screen so that the placed image is not stretched
        If RatioX < RatioY Then Ratio = RatioX Else Ratio = RatioY '    and distorted, but reduced (or enlarged) in the aspect ratio, without its deformation
        ImageX = (_DesktopWidth - _Width(img&) * Ratio) \ 2 '                                calculate image position in middle
        MyImage& = _NewImage(_Width(img&) * Ratio, _Height(img&) * Ratio) '                  output image  -  create it in ratio
        _PutImage (0, 0)-(_Width(img&) * Ratio, _Height(img&) * Ratio), img&, MyImage& '      place original image to empty image MyImage - in ratio
        _PutImage (ImageX, 0), MyImage&, V2 '                                                place centered image (complete image) in ratio to V2
        _PutImage , Virtual, V2 '                                                            place mask image to complete centered image in correct ratio

        Ui = UBound(images)
        P(Allimages).Handle = _NewImage(MaxX - MinX + 1, maxY - MinY + 1, 32) '              create image in real size (big as part)
        _PutImage (0, 0), V2, P(Allimages).Handle, (MinX, MinY)-(MaxX, maxY) '                place this part image from V2 to P().handle
        _SetAlpha 0, _RGB32(0), P(Allimages).Handle '                                        set transparent background to P().handle

        _FreeImage V2 '                                                                      clear ram
        _FreeImage Virtual
        _FreeImage MyImage&
        Virtual = 0


        R = 16 / 9 '                                                                        this is for preview only, when parts are created, i show image on the screen
        PreviewWDTH = 150 * R
        PreviewHGHT = 150
        _PutImage (100 + PreviewWDTH, 50 + PreviewHGHT)-(400 + PreviewWDTH * R, 350 + PreviewHGHT), img&
        Text1$ = "Generating Puzzle, please wait..."
        Text2$ = Str$(Allimages) + "/" + LTrim$(Str$(UBound(P)))
        CenterX = _Width(0) \ 2 - _PrintWidth(Text1$) \ 2
        CenterX2 = _Width(0) \ 2 - _PrintWidth(Text2$) \ 2
        CenterY = _Height(0) \ 2 - 16
        Centery2 = CenterY + 20
        _PrintString (CenterX, CenterY), Text1$, 0
        _PrintString (CenterX2, Centery2), Text2$, 0

    Next Allimages
End Sub



Sub PlaceForm (typ As Integer, dest As Long, kolor As _Unsigned Long) '                        generate some parts to mask image in white color. Se easy, so comment here is not needed.
    D = _Dest
    _Dest dest
    W = _Width(dest)
    H = _Height(dest)

    Select Case typ
        Case 1
            Circle (-W / 2 + Rnd * W, -H / 2 + Rnd * H), -W / 4 + Rnd * W / 8, kolor

        Case 2
            Line (-W / 2 + Rnd * W, -H / 2 + Rnd * H)-(-W / 2 + Rnd * W, -H / 2 + Rnd * H), kolor, B

        Case 3, 4, 5, 6
            Dim X(typ) As Integer
            Dim Y(typ) As Integer
            'vygeneruju cilove body
            For Rand = 0 To typ
                X(Rand) = Rand + Rnd * W
                Y(Rand) = Rand + Rnd * H
            Next

            PReset (X(0), Y(0))
            For Drw = 0 To typ
                Line -(X(Drw), Y(Drw)), kolor
            Next
            Line -(X(0), Y(0)), kolor

        Case 7
            cX = -W / 2 * Rnd + W * Rnd
            cY = -H / 2 * Rnd + H * Rnd
            For a = 0 To 300 Step 60
                PReset (cX, cY)
                For s = 0 To W Step 50
                    aR = _D2R(a)
                    If s < 100 Then noise = 0 Else noise = Rnd * 20
                    nX = cX + Cos(aR) * 2 * s + noise
                    ny = cY + Sin(aR) * 2 * s + noise
                    Line -(nX, ny), kolor
                Next s
            Next a
    End Select
End Sub



[Image: puzz-prev.png]

Nice one, Petr! I (almost) got the vole picture done, but I couldn't place the last (tiny) piece that s seemed to be hidden under the main pic. Pressing H flashed the piece and the position but it didn't seem to want to go in. I'm sure your code is right, so I'll have another try later. Nice work.
Of all the places on Earth, and all the planets in the Universe, I'd rather live here (Perth, W.A.) Big Grin
Please visit my Website at: http://oldendayskids.blogspot.com/
Reply


Messages In This Thread
Relax game - Puzzle - by Petr - 09-29-2024, 07:21 PM
RE: Relax game - Puzzle - by PhilOfPerth - 09-29-2024, 11:08 PM
RE: Relax game - Puzzle - by Petr - 09-30-2024, 05:13 AM
RE: Relax game - Puzzle - by Petr - 09-30-2024, 12:30 PM
RE: Relax game - Puzzle - by Dav - 10-03-2024, 01:48 PM
RE: Relax game - Puzzle - by Petr - 10-03-2024, 06:11 PM
RE: Relax game - Puzzle - by Petr - 10-07-2024, 05:38 PM



Users browsing this thread: 1 Guest(s)