Jump to content

Recommended Posts

Posted (edited)

While browsing through old scientific magazine archives, I stumbled upon a very interesting subject: GASP. This captivating puzzle, introduced in Jeux & Stratégie magazine issue 38 (April/May 1986), combines simple rules with complex strategies. It’s the perfect challenge for logical minds, curious puzzle enthusiasts, and programmers looking for innovative implementations.


Game Rules

  1. Objective:
    The goal is to flip all the pieces on the board so that they display a uniform color (e.g., all black or all white).

  2. Initial Setup:

    • The game is played on an n x n grid.
    • Each square contains a two-colored piece (one black side and one white side). At the start, all pieces are black-side up.
  3. Gameplay Mechanics:

    • On each turn, the player selects a square on the grid.
    • The pieces located in the neighboring squares of the selected square then flip their color (but the selected square itself does not flip).
    • The definition of neighboring squares depends on the square's position:
      • Center squares: 8 neighbors.
      • Edge squares: 5 neighbors.
      • Corner squares: 3 neighbors.
  4. Winning Condition:

    • The player wins when all the pieces on the grid display the same color, whether black or white.
  5. Dependencies:  AutoItObject.au3

image.png.0d2ee8977c332e26ef29f0b26d81d6f5.png

 

 

 

 

image.png.c68c23792b5c2b62cfed828de9b3b1a8.png

#include-once
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
; =======================================================================================================================
; Title .........: GASP Puzzle Game
; AutoIt Version : 3.3
; AutoItObject Version : v1.2.8.2
; Language ......: English
; Description ...: A digital adaptation of the GASP puzzle, a game first introduced in Jeux & Stratégie magazine in 1986.
; Dependencies ..: AutoItObject.au3, GDIPlus.au3
; Author ........: Numeric
; =======================================================================================================================
#include <GUIConstantsEx.au3>
#include <GDIPlus.au3>
#include "AutoItObject.au3"
#include <Array.au3>
#include <Math.au3>
#include <WinAPISysWin.au3>


_AutoItObject_Startup()
_GDIPlus_Startup()

Func CreateSquare($x, $y, $size, $color = 0xFFFFFFFF)
    Local $squareObj = _AutoItObject_Class()
    Local $radius = $size / 2 ; Circle radius = half the smallest square dimension

    With $squareObj
        .Create()
        .AddProperty("Type", $ELSCOPE_PUBLIC, "Square")
        .AddProperty("isClicked", $ELSCOPE_PUBLIC, False)

        .AddProperty("X", $ELSCOPE_PUBLIC, $x)
        .AddProperty("Y", $ELSCOPE_PUBLIC, $y)
        .AddProperty("Size", $ELSCOPE_PUBLIC, $size)
        .AddProperty("Color", $ELSCOPE_PUBLIC, $color)
        .AddProperty("CircleRadius", $ELSCOPE_PUBLIC, $radius)
        .AddMethod("Move", "_MoveSquare")
        .AddMethod("Resize", "_ResizeSquare")
        .AddMethod("CollideWith", "_CollideWithSquare")
        .AddMethod("ContainsPoint", "_ContainsPointSquare")
    EndWith
    Return $squareObj.Object
EndFunc   ;==>CreateSquare

Func _ContainsPointSquare($this, $px, $py)
    ; Checks if the point is within the square boundaries
    Return ($px >= $this.X And $px <= $this.X + $this.Size) And _
            ($py >= $this.Y And $py <= $this.Y + $this.Size)
EndFunc   ;==>_ContainsPointSquare

Func _CollideWithSquare($this, $otherShape)
    If $this.Type = "Square" And $otherShape.Type = "Square" Then
        ; Square-to-square collision
        Return Not ( _
                $this.X + $this.Size < $otherShape.X Or _
                $this.X > $otherShape.X + $otherShape.Size Or _
                $this.Y + $this.Size < $otherShape.Y Or _
                $this.Y > $otherShape.Y + $otherShape.Size)
    ElseIf $this.Type = "Square" And $otherShape.Type = "Circle" Then
        ; Square-to-circle collision
        Local $closestX = _Max($this.X, _Min($otherShape.X, $this.X + $this.Size))
        Local $closestY = _Max($this.Y, _Min($otherShape.Y, $this.Y + $this.Size))
        Local $dx = $otherShape.X - $closestX
        Local $dy = $otherShape.Y - $closestY
        Return ($dx * $dx + $dy * $dy) <= ($otherShape.Radius * $otherShape.Radius)
    EndIf
    Return False
EndFunc   ;==>_CollideWithSquare

Func CheckWinner($this)
    If $this.essai <= 2 Then Return False
    Local $squares = $this.Squares
    Local $firstColor = $squares[0].Color

    ; Check if all circles have the same color
    For $square In $squares
        If $square.Color <> $firstColor Then
            Return False
        EndIf
    Next
    Return True
EndFunc   ;==>CheckWinner

Func CreateMaze($backgroundColor = 0xFF000000)
    Local $board = GUICreate("GASP", 400, 400)

    GUISetState()
    Local $boardDimensions = WinGetClientSize($board)
    Local $boardWidth = $boardDimensions[0]
    Local $boardHeight = $boardDimensions[1]


    Local $gdiMap[]
    $gdiMap["hGraphics"] = _GDIPlus_GraphicsCreateFromHWND($board)
    $gdiMap["hBitmap"] = _GDIPlus_BitmapCreateFromGraphics($boardWidth, $boardHeight, $gdiMap["hGraphics"])
    $gdiMap["hGraphicsContext"] = _GDIPlus_ImageGetGraphicsContext($gdiMap["hBitmap"])

    ; Add squares to a 4x4 grid
    Local $squareSize = Int($boardWidth / 4) ; Size of a square
    Local $squares[0]
    For $iLine = 0 To 3
        For $iCol = 0 To 3
            ; Calculate the position (x, y) for each square
            Local $x = $iCol * $squareSize
            Local $y = $iLine * $squareSize
            ; Add a square with the specified dimensions and color
            Local $square = CreateSquare($x, $y, $squareSize)
            _ArrayAdd($squares, $square)
        Next
    Next

    Local $mazeObj = _AutoItObject_Class()
    With $mazeObj
        .Create()
        .AddProperty("bkColor", $ELSCOPE_PUBLIC, $backgroundColor)
        .AddProperty("essai", $ELSCOPE_PUBLIC, 0)
        .AddProperty("Squares", $ELSCOPE_PUBLIC, $squares)
        .AddProperty("GdiMap", $ELSCOPE_PUBLIC, $gdiMap)
        .AddProperty("Board", $ELSCOPE_PUBLIC, $board)
        .AddProperty("BoardWidth", $ELSCOPE_PUBLIC, $boardWidth)
        .AddProperty("BoardHeight", $ELSCOPE_PUBLIC, $boardHeight)
        .AddMethod("DrawStage", "_DrawStage")
        .AddMethod("ResetGame", "_ResetGame")
        .AddMethod("RunGameLoop", "_RunGameLoop")
        .AddMethod("CleanUpResources", "_CleanUpResources")
        .AddMethod("CheckWinner", "CheckWinner")
        .AddDestructor("_CleanUpResources")
    EndWith
    Return $mazeObj.Object
EndFunc   ;==>CreateMaze

Func _DrawStage($this)
    Local $gdiMap = $this.GdiMap
    Local $hGraphicsCtx = $gdiMap["hGraphicsContext"]
    Local $hPen = _GDIPlus_PenCreate(0xFFFF0000, 2)
    _GDIPlus_GraphicsClear($hGraphicsCtx, $this.bkColor)

    ; Draw each square and centered circle
    For $square In $this.Squares
        _GDIPlus_GraphicsDrawRect($hGraphicsCtx, $square.X, $square.Y, $square.Size, $square.Size, $hPen)
        Local $circleX = $square.X + ($square.Size / 2) - ($square.CircleRadius / 2)
        Local $circleY = $square.Y + ($square.Size / 2) - ($square.CircleRadius / 2)

        Local $hBrush = _GDIPlus_BrushCreateSolid($square.Color)
        _GDIPlus_GraphicsFillEllipse($hGraphicsCtx, $circleX, $circleY, $square.CircleRadius, $square.CircleRadius, $hBrush)
        _GDIPlus_BrushDispose($hBrush)
    Next
    _GDIPlus_PenDispose($hPen)
    _WinAPI_SetWindowText($this.Board, "GASP: Number of attempts: " & $this.essai)

    ; Redraw the image on the graphics context
    _GDIPlus_GraphicsDrawImageRect($gdiMap["hGraphics"], $gdiMap["hBitmap"], 0, 0, $this.BoardWidth, $this.BoardHeight)
EndFunc   ;==>_DrawStage

Func _RunGameLoop($this)
    Local $msg
    While 1
        $msg = GUIGetMsg(1)
        Switch $msg[0]
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $GUI_EVENT_PRIMARYDOWN
                Local $selectedSquare = Null
                Local $x = $msg[3]
                Local $y = $msg[4]
                For $square In $this.Squares
                    If $square.ContainsPoint($x, $y) And $square.isClicked = False Then
                        $selectedSquare = $square
                        ExitLoop
                    EndIf
                Next
                If Null <> $selectedSquare Then
                    $this.essai += 1
                    For $square In $this.Squares
                        $square.isClicked = False
                        $selectedSquare.isClicked = True
                        If $selectedSquare.CollideWith($square) And $square <> $selectedSquare Then
                            Switch $square.Color
                                Case -1
                                    $square.Color = 0xFFFFFF00
                                Case -256
                                    $square.Color = 0xFFFFFFFF
                            EndSwitch
                        EndIf
                    Next
                EndIf

                ; Check if the player has won
                If $this.CheckWinner() Then
                    MsgBox(0, "Congratulations", "You won! All squares have the same color.")
                    ExitLoop
                EndIf
        EndSwitch
        $this.DrawStage()
    WEnd
EndFunc   ;==>_RunGameLoop

Func _ResetGame($this)
    ; Resets all squares to their default color (white)
    For $square In $this.Squares
        $square.Color = 0xFFFFFFFF ; White
    Next
    $this.essai = 0 ; Reset the number of attempts
    $this.DrawStage()
EndFunc   ;==>_ResetGame

Func _CleanUpResources($this)
    ; Releases all GDI+ resources and destroys the GUI window
    Local $gdiMap = $this.GdiMap
    _GDIPlus_GraphicsDispose($gdiMap["hGraphicsContext"])
    _GDIPlus_BitmapDispose($gdiMap["hBitmap"])
    _GDIPlus_GraphicsDispose($gdiMap["hGraphics"])
    GUIDelete($this.Board)
EndFunc   ;==>_CleanUpResources


Global $maze = CreateMaze()
$maze.RunGameLoop()

 

Edited by Numeric1
Fixed the issue of successive clicks.
Posted
12 minutes ago, argumentum said:

Thanks for sharing.
Now, if I click twice the same square I win. I like that. Am a sore looser. But am sure that there must be something in the rules like don't play alone or 2 player board or something to make it challenging.

don't hurt yourself if you come in 3rd.

Posted
4 hours ago, argumentum said:

Thanks for sharing.
Now, if I click twice the same square I win. I like that. Am a sore looser. But am sure that there must be something in the rules like don't play alone or 2 player board or something to make it challenging.

Thank you for pointing that out! I've just fixed the issue with successive clicks on the same square, so now you can't win by clicking the same square twice. 😊 As for the rules, you're absolutely right—adding a two-player mode or some extra challenges could make the game much more engaging. I’ll definitely consider adding those features in future updates!

Posted
4 hours ago, argumentum said:

..pardon the late reply @Somerset. Had to change the keyboard. I was WASD the keyboard and the game did nothing, NOTHING. ...it got out of hand.
Funny enough, it's played with the mouse :lol:

Anyway. Got it. I won !.

@argumentum End the random victories 🕶️. And most importantly, don't forget the mouse this time, it’s going to work a lot now. Yep... it was a bug 😂.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...