Numeric1 Posted April 17 Share Posted April 17 Sometimes, the most interesting discoveries happen by accident. That's how I stumbled upon an old post presenting a classic version of the game Snake. However, rather than simply appreciating the game as it was, I saw an opportunity for improvement by using AutoItObject.au3 , a powerful tool that didn't exist at the time of the original version's creation. In this article, I'll introduce you to my object-oriented version of Snake Game, which brings flexibility and modernity to this timeless classic. AutoItObject.au3 is an incredibly flexible tool that allows for modular and efficient application and game development. With this library, I was able to completely rethink the structure of the Snake game, transforming it into a rich and dynamic object-oriented experience. This means that every entity in the game, from the snake to the food to the user interface, is represented as a distinct object, offering extraordinary flexibility and extensibility. One of the main features of my version of Snake Game is the ability for players to express their own preferences and customize the gaming experience to their liking. Whether adjusting the score, modifying the difficulty level, or adding new game elements, players have the freedom to shape the game to suit their preferences. Despite its underlying complexity, using AutoItObject.au3 makes the code for Snake Game surprisingly simple and easy to understand. Each game object is carefully encapsulated and modeled to accurately reflect its behavior in the real world. This means that developers can easily add new features or modify the game without compromising its stability or quality. By reimagining Snake Game through the lens of object-oriented programming with AutoItObject.au3, I was able to create a version of the game that combines the simplicity of the original classic with the flexibility and modernity of object-oriented programming. I look forward to seeing how this version evolves over time, and I hope it inspires other developers to rethink their own projects using this powerful library. expandcollapse popup#include-once #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #INDEX# ======================================================================================================================= ; Title .........: Snake Game ; AutoIt Version : 3.3 ; AutoItObject Version : v1.2.8.2 ; Language ......: English ; Description ...: A simple Snake game implementation using AutoIt->AutoItObject.au3. ; Dependencies ..: AutoItObject.au3 ; Author ........: Numeric ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include "AutoItObject.au3" #include <Print.au3> #include <Misc.au3> Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") _AutoItObject_Startup() Func Snake($oPoint = 0, $Board = Default) Local $aRandomDirectionTab = [-1, 0, 1] Local $dx = $aRandomDirectionTab[Int(Random(0, 2, 1))] Local $dy = $aRandomDirectionTab[Int(Random(0, 2, 1))] Local $iLowSpeed = 100 Local $foodCounter = 0 If $Board = Default Then $Board = GUICreate("Snake", 400, 400) GUISetBkColor(0x000000) GUISetState() Local $aBoardPos = WinGetClientSize($Board) If @error Then Return SetError(1, 0, False) ; window not found Local $iBoardWidth = $aBoardPos[0] Local $iBoardHeight = $aBoardPos[1] Local $X = Random(10, $iBoardWidth - 10, 1) Local $Y = Random(10, $iBoardHeight - 10, 1) If $oPoint = 0 Then $oPoint = Point($X, $Y) Local $aMap[] MapAppend($aMap, $oPoint) Local $oFood = Point(Random(10, $iBoardWidth - 10), Random(10, $iBoardHeight - 10), 39219) Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("head", $ELSCOPE_PRIVATE, $oPoint) .AddProperty("aMap", $ELSCOPE_PRIVATE, $aMap) .AddProperty("food", $ELSCOPE_PRIVATE, $oFood) .AddProperty("foodCounter", $ELSCOPE_PRIVATE, $foodCounter) .AddProperty("dx", $ELSCOPE_PRIVATE, $dx) .AddProperty("dy", $ELSCOPE_PRIVATE, $dy) .AddProperty("Board", $ELSCOPE_PRIVATE, $Board) .AddProperty("speedLevel", $ELSCOPE_PRIVATE, $iLowSpeed) .AddProperty("iBoardWidth", $ELSCOPE_PRIVATE, $iBoardWidth) .AddProperty("iBoardHeight", $ELSCOPE_PRIVATE, $iBoardHeight) .AddMethod("getBoard", "_getBoard") .AddMethod("setSpeedLevel", "_setSpeedLevel") .AddMethod("getSpeedLevel", "_getSpeedLevel") .AddMethod("runGameLoop", "_runGameLoop") .AddMethod("setdx", "_setdx") .AddMethod("setdy", "_setdy") .AddMethod("getdx", "_getdx") .AddMethod("getdy", "_getdy") .AddMethod("sleepW", "_sleepW") .AddMethod("getMap", "_getMap") .AddMethod("setMap", "_setMap") .AddMethod("getHead", "_getHead") .AddMethod("getBoardHeight", "_getBoardHeight") .AddMethod("getBoardWidth", "_getBoardWidth") .AddMethod("getFoodCounter", "_getFoodCounter") .AddMethod("setFoodCounter", "_setFoodCounter", True) .AddMethod("move", "_move", True) .AddMethod("generateFood", "_generateFood") .AddMethod("setFood", "_setFood") .AddMethod("getFood", "_getFood") .AddDestructor("_destructor") EndWith Return $sClass.Object EndFunc ;==>Snake Func _destructor($this) ConsoleWrite("Destructor ....." & @CRLF) Local $aMap = $this.getMap() For $oPoint In $aMap $oPoint = 0 Next $aMap = 0 $this.setMap(0) EndFunc ;==>_destructor Func _getFoodCounter($this) Return $this.foodCounter EndFunc ;==>_getFoodCounter Func _setFoodCounter($this, $foodCounter) $this.foodCounter = $foodCounter EndFunc ;==>_setFoodCounter Func _setSpeedLevel($this, $iLevel) $this.speedLevel = $iLevel EndFunc ;==>_setSpeedLevel Func _getSpeedLevel($this) Return $this.speedLevel EndFunc ;==>_getSpeedLevel Func _getBoard($this) Return $this.Board EndFunc ;==>_getBoard Func _runGameLoop($this) While 1 If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop If _IsPressed(25) Then $this.setdx(-1) $this.setdy(0) EndIf If _IsPressed(27) Then $this.setdx(1) $this.setdy(0) EndIf If _IsPressed(26) Then $this.setdx(0) $this.setdy(-1) EndIf If _IsPressed(28) Then $this.setdx(0) $this.setdy(1) EndIf If _IsPressed(53) Then $this.sleepW() EndIf $this.move() Sleep($this.getSpeedLevel()) WEnd EndFunc ;==>_runGameLoop Func _getBoardWidth($this) Return $this.iBoardWidth EndFunc ;==>_getBoardWidth Func _getBoardHeight($this) Return $this.iBoardHeight EndFunc ;==>_getBoardHeight Func _generateFood($this) Local $oFood = Point(Random(10, $this.getBoardWidth() - 10), Random(10, $this.getBoardHeight() - 10), 39219) $this.setFood($oFood) EndFunc ;==>_generateFood Func _setFood($this, $oFood) $this.food = $oFood EndFunc ;==>_setFood Func _getFood($this) Return $this.food EndFunc ;==>_getFood Func _getHead($this) Return $this.head EndFunc ;==>_getHead Func _setdx($this, $dx) $this.dx = $dx EndFunc ;==>_setdx Func _setdy($this, $dy) $this.dy = $dy EndFunc ;==>_setdy Func _getdx($this) Return $this.dx EndFunc ;==>_getdx Func _getdy($this) Return $this.dy EndFunc ;==>_getdy Func _getMap($this) Return $this.aMap EndFunc ;==>_getMap Func _setMap($this, $aMap) $this.aMap = $aMap EndFunc ;==>_setMap Func _move($this) Local $aMap = $this.getMap() Local $head = $aMap[0] Local $newX = $head.getXPos() + $this.getdx() * 10 Local $newY = $head.getYPos() + $this.getdy() * 10 ; Vérifier si la tête du serpent sort de l'écran If $newX <= 0 Or $newX >= $this.getBoardWidth() - 10 Or $newY <= 0 Or $newY >= $this.getBoardHeight() - 10 Then Return SetError(1, 0, False) EndIf Local $oFood = $this.getFood() If $head.checkCollision($oFood) Then $this.setFoodCounter($this.getFoodCounter() + 1) Local $lastSegment = $aMap[UBound($aMap) - 1] Local $lastX = $lastSegment.getXPos() + $this.getdx() * 10 Local $lastY = $lastSegment.getYPos() + $this.getdy() * 10 MapAppend($aMap, Point($lastX, $lastY)) $this.setMap($aMap) If $this.getFoodCounter() = 4 Then Local $newSpeedLevel = $this.getSpeedLevel() - 20 If $newSpeedLevel < 0 Then $newSpeedLevel = 1 $this.setSpeedLevel($newSpeedLevel) $this.setFoodCounter(0) EndIf GUICtrlDelete($oFood.getID()) $this.generateFood() EndIf ; Mettre à jour la position de la tête du serpent $head.setXPos($newX) $head.setYPos($newY) ; Déplacer les segments du serpent For $i = UBound($aMap) - 1 To 1 Step -1 $aMap[$i].setXPos($aMap[$i - 1].getXPos()) $aMap[$i].setYPos($aMap[$i - 1].getYPos()) Next EndFunc ;==>_move ;bug Func _sleepW(ByRef $this) Do Do Sleep(100) Until _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) If _IsPressed(25) Then $this.setdx(-1) $this.setdy(0) EndIf If _IsPressed(27) Then $this.setdx(1) $this.setdy(0) EndIf If _IsPressed(26) Then $this.setdx(0) $this.setdy(-1) EndIf If _IsPressed(28) Then $this.setdx(0) $this.setdy(1) EndIf Until _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) EndFunc ;==>_sleepW Func Point($X = 0, $Y = 0, $iClolor = 16777215) Local $idLabel = GUICtrlCreateLabel("", $X, $Y, 10, 10, 0x0001) ; $BS_CENTER GUICtrlSetBkColor($idLabel, $iClolor) Local $cPoint = _AutoItObject_Class() With $cPoint .Create() .AddProperty("___sType___", $ELSCOPE_PRIVATE, "Point") .AddProperty("X", $ELSCOPE_PRIVATE, $X) .AddProperty("Y", $ELSCOPE_PRIVATE, $Y) .AddProperty("iColor", $ELSCOPE_PRIVATE, $iClolor) .AddProperty("idLabel", $ELSCOPE_PRIVATE, $idLabel) .AddMethod("getObjType", "_getObjType") .AddMethod("setXPos", "_setXPos") .AddMethod("setYPos", "_setYPos") .AddMethod("getID", "_getID") .AddMethod("setID", "_setID") .AddMethod("getXPos", "_getXPos") .AddMethod("getYPos", "_getYPos") .AddMethod("checkCollision", "_checkCollision") EndWith Return $cPoint.Object EndFunc ;==>Point Func _getObjType($this) Return $this.___sType___ EndFunc ;==>_getObjType Func _checkCollision($this, $oPoint) If Not IsObj($oPoint) Then Return SetError(1, 0, False) Local $sType = $oPoint.getObjType() If $sType <> "Point" Then Return SetError(2, 0, False) Local $aPos1 = ControlGetPos("", "", $this.getID()) Local $aPos2 = ControlGetPos("", "", $oPoint.getID()) If $aPos1[0] + $aPos1[2] >= $aPos2[0] And $aPos1[0] <= $aPos2[0] + $aPos2[2] And $aPos1[1] + $aPos1[3] >= $aPos2[1] And $aPos1[1] <= $aPos2[1] + $aPos2[3] Then Return True EndIf Return False EndFunc ;==>_checkCollision Func _setXPos($this, $iX) $this.X = $iX GUICtrlSetPos($this.idLabel, $iX, $this.getYPos()) EndFunc ;==>_setXPos Func _setYPos($this, $iY) $this.Y = $iY GUICtrlSetPos($this.idLabel, $this.getXPos(), $iY) EndFunc ;==>_setYPos Func _getXPos($this) Return $this.X EndFunc ;==>_getXPos Func _getYPos($this) Return $this.Y EndFunc ;==>_getYPos Func _getID($this) Return $this.idLabel EndFunc ;==>_getID Func _setID($this, $idLabel) $this.idLabel = $idLabel EndFunc ;==>_setID ;*************************************************** Global $sNake = Snake() $sNake.runGameLoop() $sNake = 0 ;************************************************** _AutoItObject_Shutdown() Link to comment Share on other sites More sharing options...
Andreik Posted April 17 Share Posted April 17 The script doesn't run since you use some unknown script called Print.au3 (at least in the latest AutoIt version). If you comment the line then the script run but the collision doesn't seems to work. Also the flicker is present even when the length of the snake it's just one and get worse when the length increase. Mabye drawing the objects using GDI+ will fix this issue. Overall it's a good start but it leaves room for improvements. Link to comment Share on other sites More sharing options...
Numeric1 Posted April 18 Author Share Posted April 18 Thank you very much, Andreik, for your constructive feedback. Indeed, the adoption of GDI+ has significantly improved the fluidity of the snake's movements in its eternal quest for food. This major change was accomplished in record time (including a 5-minute coffee break) thanks to the flexibility of the object-oriented paradigm offered by the AutoItObject.au3 UDF. With this tool, you can literally build an empire, brick by brick. Now, you have the freedom to adjust the snake's speed, change its color on the fly, even during gameplay, and even generate fake prey of different colors to try to trap "Kaa," Mowgli's old companion. In other words, you can unleash your imagination and push the boundaries of what's possible. expandcollapse popup#include-once #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #INDEX# ======================================================================================================================= ; Title .........: Snake Game ; AutoIt Version : 3.3 ; AutoItObject Version : v1.2.8.2 ; Language ......: English ; Description ...: A simple Snake game implementation using AutoIt->AutoItObject.au3. ; Dependencies ..: AutoItObject.au3 ; Author ........: Numeric ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include "AutoItObject.au3" #include <Misc.au3> #include <GDIPlus.au3> #include <GUIConstantsEx.au3> #include <WinAPIGdi.au3> Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") _GDIPlus_Startup() _AutoItObject_Startup() Func Snake($oPoint = 0, $Board = Default, $iBgColor = 0xFF0000FF) Local $aRandomDirectionTab = [-1, 0, 1] Local $dx = $aRandomDirectionTab[Int(Random(0, 2, 1))] Local $dy = $aRandomDirectionTab[Int(Random(0, 2, 1))] Local $iLowSpeed = 100 Local $foodCounter = 0 If $Board = Default Then $Board = GUICreate("Snake", 400, 400) GUISetBkColor(0x303030) GUISetState() Local $aBoardPos = WinGetClientSize($Board) If @error Then Return SetError(1, 0, False) ; window not found Local $iBoardWidth = $aBoardPos[0] Local $iBoardHeight = $aBoardPos[1] Local $X = Random(10, $iBoardWidth - 10, 1) Local $Y = Random(10, $iBoardHeight - 10, 1) Local $aMap[] Local $oFood = Point(Random(10, $iBoardWidth - 10), Random(10, $iBoardHeight - 10)) MapAppend($aMap, $oFood) If $oPoint = 0 Then $oPoint = Point($X, $Y) MapAppend($aMap, $oPoint) Local $hGraphics = _GDIPlus_GraphicsCreateFromHWND($Board) Local $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iBoardWidth, $iBoardHeight, $hGraphics) Local $hGraphicsCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap) _GDIPlus_GraphicsSetSmoothingMode($hGraphicsCtxt, $GDIP_SMOOTHINGMODE_HIGHQUALITY) Local $hBrush = _GDIPlus_BrushCreateSolid($iBgColor) Local $hFoodBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ;(0x00FF00) Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("hGraphics", $ELSCOPE_PRIVATE, $hGraphics) .AddProperty("hBitmap", $ELSCOPE_PRIVATE, $hBitmap) .AddProperty("hGraphicsCtxt", $ELSCOPE_PRIVATE, $hGraphicsCtxt) .AddProperty("hBrush", $ELSCOPE_PRIVATE, $hBrush) .AddProperty("iColor", $ELSCOPE_PRIVATE, 0xFF8080FF) .AddProperty("hFoodBrush", $ELSCOPE_PRIVATE, $hFoodBrush) .AddProperty("head", $ELSCOPE_PRIVATE, $oPoint) .AddProperty("aMap", $ELSCOPE_PRIVATE, $aMap) .AddProperty("food", $ELSCOPE_PRIVATE, $oFood) .AddProperty("foodCounter", $ELSCOPE_PRIVATE, $foodCounter) .AddProperty("dx", $ELSCOPE_PRIVATE, $dx) .AddProperty("dy", $ELSCOPE_PRIVATE, $dy) .AddProperty("Board", $ELSCOPE_PRIVATE, $Board) .AddProperty("speedLevel", $ELSCOPE_PRIVATE, $iLowSpeed) .AddProperty("iBoardWidth", $ELSCOPE_PRIVATE, $iBoardWidth) .AddProperty("iBoardHeight", $ELSCOPE_PRIVATE, $iBoardHeight) .AddMethod("getBoard", "_getBoard") .AddMethod("setSpeedLevel", "_setSpeedLevel") .AddMethod("getSpeedLevel", "_getSpeedLevel") .AddMethod("runGameLoop", "_runGameLoop") .AddMethod("getFoodBrush", "_getFoodBrush") .AddMethod("setdx", "_setdx") .AddMethod("setdy", "_setdy") .AddMethod("getdx", "_getdx") .AddMethod("getdy", "_getdy") .AddMethod("sleepW", "_sleepW") .AddMethod("getMap", "_getMap") .AddMethod("setMap", "_setMap") .AddMethod("getHead", "_getHead") .AddMethod("getBoardHeight", "_getBoardHeight") .AddMethod("getBoardWidth", "_getBoardWidth") .AddMethod("getFoodCounter", "_getFoodCounter") .AddMethod("setFoodCounter", "_setFoodCounter", True) .AddMethod("move", "_move", True) .AddMethod("generateFood", "_generateFood") .AddMethod("setFood", "_setFood") .AddMethod("getFood", "_getFood") .AddMethod("getSColor", "_getSColor") .AddMethod("setSColor", "_setSColor") .AddMethod("getGraphics", "_getGraphics") .AddMethod("getBitmap", "_getBitmap") .AddMethod("getGraphicsCtxt", "_getGraphicsCtxt") .AddMethod("getBrush", "_getBrush") .AddMethod("drawStage", "_drawStage") .AddMethod("checkBorderCollision", "_checkBorderCollision") .AddMethod("checkSelfCollision", "_checkSelfCollision") .AddMethod("checkFoodCollision", "_checkFoodCollision") .AddMethod("resetGame", "_resetGame") .AddMethod("cleanUpResources", "_cleanUpResources") .AddDestructor("_cleanUpResources") EndWith Return $sClass.Object EndFunc ;==>Snake Func _getFoodBrush($this) Return $this.hFoodBrush EndFunc ;==>_getFoodBrush Func _getGraphics($this) Return $this.hGraphics EndFunc ;==>_getGraphics Func _getBitmap($this) Return $this.hBitmap EndFunc ;==>_getBitmap Func _getGraphicsCtxt($this) Return $this.hGraphicsCtxt EndFunc ;==>_getGraphicsCtxt Func _getBrush($this) Return $this.hBrush EndFunc ;==>_getBrush Func _drawStage($this, $aPoints) Local $hGraphicsCtxt = $this.getGraphicsCtxt() $this.move() ; Effacer l'arrière-plan _GDIPlus_GraphicsClear($hGraphicsCtxt, 0xFF000000) ; Dessiner la nourriture Local $oFood = $this.getMap()[0] _GDIPlus_GraphicsFillRect($hGraphicsCtxt, $oFood.getXPos(), $oFood.getYPos(), $oFood.getWidth(), $oFood.getHeight(), $this.getFoodBrush()) ; Dessiner les segments du serpent For $i = 1 To UBound($aPoints) - 1 Local $oPoint = $aPoints[$i] _GDIPlus_GraphicsFillRect($hGraphicsCtxt, $oPoint.getXPos(), $oPoint.getYPos(), $oPoint.getWidth(), $oPoint.getHeight(), $this.getBrush()) Next ; Mettre à jour l'affichage _GDIPlus_GraphicsDrawImageRect($this.getGraphics(), $this.getBitmap(), 0, 0, $this.getBoardWidth(), $this.getBoardHeight()) EndFunc ;==>_drawStage Func _drawStage2($this, $aPoints) _GDIPlus_GraphicsClear($this.getGraphicsCtxt(), 0xFF000000) $this.move() Local $oFood = $this.getMap()[0] _GDIPlus_GraphicsFillRect($this.getGraphicsCtxt(), $oFood.getXPos(), $oFood.getYPos(), $oFood.getWidth(), $oFood.getHeight(), $this.getFoodBrush()) For $i = 1 To UBound($aPoints) - 1 Local $oPoint = $aPoints[$i] _GDIPlus_GraphicsFillRect($this.getGraphicsCtxt(), $oPoint.getXPos(), $oPoint.getYPos(), $oPoint.getWidth(), $oPoint.getHeight(), $this.getBrush()) Next _GDIPlus_GraphicsDrawImageRect($this.getGraphics(), $this.getBitmap(), 0, 0, $this.getBoardWidth(), $this.getBoardHeight()) EndFunc ;==>_drawStage2 Func _getFoodCounter($this) Return $this.foodCounter EndFunc ;==>_getFoodCounter Func _setFoodCounter($this, $foodCounter) $this.foodCounter = $foodCounter EndFunc ;==>_setFoodCounter Func _setSpeedLevel($this, $iLevel) $this.speedLevel = $iLevel EndFunc ;==>_setSpeedLevel Func _getSpeedLevel($this) Return $this.speedLevel EndFunc ;==>_getSpeedLevel Func _getBoard($this) Return $this.Board EndFunc ;==>_getBoard Func _runGameLoop($this) While 1 If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop If _IsPressed(25) Then $this.setdx(-1) $this.setdy(0) EndIf If _IsPressed(27) Then $this.setdx(1) $this.setdy(0) EndIf If _IsPressed(26) Then $this.setdx(0) $this.setdy(-1) EndIf If _IsPressed(28) Then $this.setdx(0) $this.setdy(1) EndIf If _IsPressed(53) Then $this.sleepW() EndIf $this.drawStage($this.getMap()) Sleep($this.getSpeedLevel()) WEnd EndFunc ;==>_runGameLoop Func _getBoardWidth($this) Return $this.iBoardWidth EndFunc ;==>_getBoardWidth Func _getBoardHeight($this) Return $this.iBoardHeight EndFunc ;==>_getBoardHeight Func _generateFood($this) Local $aMap = $this.getMap() Local $oFood = $aMap[0] $oFood.setXPos(Random(10, $this.getBoardWidth() - 10)) $oFood.setXPos(Random(10, $this.getBoardHeight() - 10)) $this.setMap($aMap) EndFunc ;==>_generateFood Func _setFood($this, $oFood) $this.food = $oFood EndFunc ;==>_setFood Func _getFood($this) Return $this.food EndFunc ;==>_getFood Func _getHead($this) Return $this.head EndFunc ;==>_getHead Func _setdx($this, $dx) $this.dx = $dx EndFunc ;==>_setdx Func _setdy($this, $dy) $this.dy = $dy EndFunc ;==>_setdy Func _getdx($this) Return $this.dx EndFunc ;==>_getdx Func _getdy($this) Return $this.dy EndFunc ;==>_getdy Func _getMap($this) Return $this.aMap EndFunc ;==>_getMap Func _setMap($this, $aMap) $this.aMap = $aMap EndFunc ;==>_setMap Func _checkBorderCollision($this) Local $aMap = $this.getMap() Local $head = $aMap[1] Local $hX = $head.getXPos() + $this.getdx() * 10 Local $hY = $head.getYPos() + $this.getdy() * 10 If $hX <= 0 Or $hX >= $this.getBoardWidth() - 10 Or $hY <= 0 Or $hY >= $this.getBoardHeight() - 10 Then Return True EndIf Return False EndFunc ;==>_checkBorderCollision Func _checkSelfCollision($this) Local $aMap = $this.getMap() Local $head = $aMap[1] Local $hX = $head.getXPos() Local $hY = $head.getYPos() Local $hW = $hX + $this.getdx() * 10 Local $hH = $hY + $this.getdy() * 10 For $i = 2 To UBound($aMap) - 1 Local $segment = $aMap[$i] Local $segX = $segment.getXPos() Local $segY = $segment.getYPos() Local $segW = $segX + $segment.getWidth() Local $segH = $segY + $segment.getHeight() If $hX < $segW And $hW > $segX And $hY < $segH And $hH > $segY Then Return True EndIf Next Return False EndFunc ;==>_checkSelfCollision Func _checkFoodCollision($this) Local $aMap = $this.getMap() Local $head = $aMap[1] Local $hX = $head.getXPos() + $this.getdx() * 10 Local $hY = $head.getYPos() + $this.getdy() * 10 Local $hW = $hX + $head.getWidth() Local $hH = $hY + $head.getHeight() Local $food = $aMap[0] Local $fX = $food.getXPos() Local $fY = $food.getYPos() Local $fW = $fX + $food.getWidth() Local $fH = $fY + $food.getHeight() If $hX < $fW And $hW > $fX And $hY < $fH And $hH > $fY Then $this.generateFood() Local $lastSegment = $aMap[UBound($aMap) - 1] Local $lastX = $lastSegment.getXPos() + $this.getdx() * 10 Local $lastY = $lastSegment.getYPos() + $this.getdy() * 10 MapAppend($aMap, Point($lastX, $lastY)) $this.setMap($aMap) Return True EndIf Return False EndFunc ;==>_checkFoodCollision Func _resetGame($this) Local $aMap = $this.getMap() For $e In $aMap $e = 0 Next $aMap = 0 Local $aNewMap[] MapAppend($aNewMap, Point()) $this.setMap($aNewMap) $this.generateFood() MapAppend($aNewMap, Point(Random(10, $this.getBoardWidth() - 10), Random(10, $this.getBoardHeight() - 10))) $this.setMap($aNewMap) Local $aRandomDirectionTab = [-1, 0, 1] Local $dx = $aRandomDirectionTab[Int(Random(0, 2, 1))] Local $dy = $aRandomDirectionTab[Int(Random(0, 2, 1))] $this.setdx($dx) $this.setdy($dy) $this.setSpeedLevel(100) $this.setFoodCounter(0) $this.drawStage($aNewMap) EndFunc ;==>_resetGame Func _move($this) Local $aMap = $this.getMap() Local $head = $aMap[1] Local $newX = $head.getXPos() + $this.getdx() * 10 Local $newY = $head.getYPos() + $this.getdy() * 10 If $this.checkBorderCollision() Or $this.checkSelfCollision() Then MsgBox(64, "Game Over", "Collision! Game Over.") Local $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then _resetGame($this) Return True Else $this.cleanUpResources() _GDIPlus_Shutdown() _AutoItObject_Shutdown() Exit EndIf EndIf If $this.checkFoodCollision() Then $this.setFoodCounter($this.getFoodCounter() + 1) Local $lastSegment = $aMap[UBound($aMap) - 1] Local $lastX = $lastSegment.getXPos() + $this.getdx() * 10 Local $lastY = $lastSegment.getYPos() + $this.getdy() * 10 MapAppend($aMap, Point($lastX, $lastY)) If $this.getFoodCounter() = 4 Then Local $newSpeedLevel = $this.getSpeedLevel() - 20 If $newSpeedLevel < 0 Then $newSpeedLevel = 1 $this.setSpeedLevel($newSpeedLevel) $this.setFoodCounter(0) EndIf $this.generateFood() EndIf $head.setXPos($newX) $head.setYPos($newY) For $i = UBound($aMap) - 1 To 2 Step -1 $aMap[$i].setXPos($aMap[$i - 1].getXPos()) $aMap[$i].setYPos($aMap[$i - 1].getYPos()) Next $this.setMap($aMap) EndFunc ;==>_move ; #FUNCTION# ==================================================================================================================== ; Name ..........: _sleepW ; Description ...: Waits for keyboard input to change the direction of movement in the Snake game. ; Syntax ........: _sleepW(ByRef $this) ; Parameters ....: $this - [in/out] A reference to the Snake object. ; Return values .: None ; Modified ......: ; Remarks .......: This function is triggered when the player presses the 'S' key in the Snake game. It waits for directional input ; (arrow keys) to change the movement direction of the snake. The function updates the movement direction properties ; of the Snake object accordingly. Note: There seems to be a crashing issue with this function that needs further investigation. ; Related .......: Snake ; Link ..........: ; Example .......: No ; =============================================================================================================================== Func _sleepW($this) Do Do Sleep(100) Until _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) If _IsPressed(25) Then $this.setdx(-1) $this.setdy(0) EndIf If _IsPressed(27) Then $this.setdx(1) $this.setdy(0) EndIf If _IsPressed(26) Then $this.setdx(0) $this.setdy(-1) EndIf If _IsPressed(28) Then $this.setdx(0) $this.setdy(1) EndIf Until _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) EndFunc ;==>_sleepW ; #FUNCTION# ==================================================================================================================== ; Name ..........: _cleanUpResources ; Description ...: Cleans up all resources used in the Snake game. ; Syntax ........: _cleanUpResources() ; Parameters ....: None ; Return values .: None ; Remarks .......: This function deallocates all resources used in the Snake game, including graphics resources, objects, and variables. ; Related .......: Snake ; =============================================================================================================================== Func _cleanUpResources($this) ConsoleWrite("cleanUpRessources" & @CRLF) Local $aMap = $this.getMap() For $oPoint In $aMap $oPoint = 0 Next $aMap = 0 $this.setMap(0) ; Deallocate GDI+ resources _GDIPlus_GraphicsDispose($this.getGraphicsCtxt()) _GDIPlus_BrushDispose($this.getBrush()) _GDIPlus_BrushDispose($this.getFoodBrush()) _GDIPlus_BitmapDispose($this.getBitmap()) _GDIPlus_Shutdown() EndFunc ;==>_cleanUpResources ; #FUNCTION# ==================================================================================================================== ; Name ..........: Point ; Description ...: Creates a Point object used in the Snake game to represent coordinates. ; Syntax ........: Point([$X = 0[, $Y = 0]]) ; Parameters ....: $X - [optional] The X coordinate value. Default is 0. ; $Y - [optional] The Y coordinate value. Default is 0. ; Return values .: Returns a Point object with specified coordinates. ; Remarks .......: This function is used to create a Point object representing coordinates on the game board for the Snake game. ; It is used to manage positions of the snake segments and the food items. ; Related .......: Snake, _AutoItObject_Class ; =============================================================================================================================== Func Point($X = 0, $Y = 0) Local $cPoint = _AutoItObject_Class() Local Const $iWidth = 10 Local Const $iHeight = 10 With $cPoint .Create() .AddProperty("X", $ELSCOPE_PRIVATE, $X) .AddProperty("Y", $ELSCOPE_PRIVATE, $Y) .AddProperty("iWidth", $ELSCOPE_PRIVATE, $iWidth) .AddProperty("iHeight", $ELSCOPE_PRIVATE, $iHeight) .AddMethod("setXPos", "_setXPos") .AddMethod("setYPos", "_setYPos") .AddMethod("getXPos", "_getXPos") .AddMethod("getYPos", "_getYPos") .AddMethod("setWidth", "_setWidth", True) .AddMethod("setHeight", "_setHeight", True) .AddMethod("getWidth", "_getWidth") .AddMethod("getHeight", "_getHeight") .AddDestructor("_pointDestructor") EndWith Return $cPoint.Object EndFunc ;==>Point Func _setXPos($this, $iX) $this.X = $iX EndFunc ;==>_setXPos Func _setYPos($this, $iY) $this.Y = $iY EndFunc ;==>_setYPos Func _getXPos($this) Return $this.X EndFunc ;==>_getXPos Func _getYPos($this) Return $this.Y EndFunc ;==>_getYPos Func _getWidth($this) Return $this.iWidth EndFunc ;==>_getWidth Func _getHeight($this) Return $this.iHeight EndFunc ;==>_getHeight Func _setColor($this, $iClolor) $this.iColor = $iClolor EndFunc ;==>_setColor Func _getColor($this) Return $this.iColor EndFunc ;==>_getColor Func _ErrFunc($oError) ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc ;***************************************************************************************************************************************** Global $sNake = Snake() $sNake.runGameLoop() $sNake = 0 ;***************************************************************************************************************************************** _AutoItObject_Shutdown() Link to comment Share on other sites More sharing options...
Andreik Posted April 18 Share Posted April 18 Congrats, it works way better. The double buffering fixed the flickers and border collisions works now but self collision still doesn't work. Look what snake I managed to create. Link to comment Share on other sites More sharing options...
Numeric1 Posted April 19 Author Share Posted April 19 I've come up with a simplified version of the code where I've addressed the issue of the snake's self-collision. Additionally, you have the freedom to choose the color of the snake. This showcases the versatility of the tool I'm using. expandcollapse popup#include-once #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #INDEX# ======================================================================================================================= ; Title .........: Snake Game ; AutoIt Version : 3.3 ; AutoItObject Version : v1.2.8.2 ; Language ......: English ; Description ...: A simple Snake game implementation using AutoIt->AutoItObject.au3. ; Dependencies ..: AutoItObject.au3 ; Author ........: Numeric ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include "AutoItObject.au3" #include <GDIPlus.au3> #include <Misc.au3> Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") _GDIPlus_Startup() _AutoItObject_Startup() Func Snake($iBgColor = 0xFF0000FF) Local $dx = Random(-1, 1, 1) Local $dy = Random(-1, 1, 1) Local $aMap[] MapAppend($aMap, Point(Random(10, 380, 1), Random(10, 380, 1))) ;food MapAppend($aMap, Point(Random(10, 380, 1), Random(10, 380, 1))) ; head Local $Board = GUICreate("Snake", 400, 400) GUISetState() Local $aBoardPos = WinGetClientSize($Board) Local $iBoardWidth = $aBoardPos[0] Local $iBoardHeight = $aBoardPos[1] Local $hGraphics = _GDIPlus_GraphicsCreateFromHWND($Board) Local $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iBoardWidth, $iBoardHeight, $hGraphics) Local $hGraphicsCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap) _GDIPlus_GraphicsSetSmoothingMode($hGraphicsCtxt, $GDIP_SMOOTHINGMODE_HIGHQUALITY) Local $hBrush = _GDIPlus_BrushCreateSolid($iBgColor) Local $hFoodBrush = _GDIPlus_BrushCreateSolid(0xFF00FF00) ;(0x00FF00) Local $aGDIMap[] $aGDIMap["hGraphics"] = $hGraphics $aGDIMap["hBitmap"] = $hBitmap $aGDIMap["hGraphicsCtxt"] = $hGraphicsCtxt $aGDIMap["hBrush"] = $hBrush $aGDIMap["hFoodBrush"] = $hFoodBrush Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("dx", $ELSCOPE_PUBLIC, $dx) .AddProperty("dy", $ELSCOPE_PUBLIC, $dy) .AddProperty("speedLevel", $ELSCOPE_PUBLIC, 100) .AddProperty("foodCounter", $ELSCOPE_PUBLIC, 0) .AddProperty("Map", $ELSCOPE_PUBLIC, $aMap) .AddProperty("gdiMap", $ELSCOPE_PUBLIC, $aGDIMap) .AddProperty("Board", $ELSCOPE_PUBLIC, $Board) .AddProperty("iBoardWidth", $ELSCOPE_PUBLIC, $iBoardWidth) .AddProperty("iBoardHeight", $ELSCOPE_PUBLIC, $iBoardHeight) .AddMethod("move", "_move") .AddMethod("drawStage", "_drawStage") .AddMethod("resetGame", "_resetGame") .AddMethod("runGameLoop", "_runGameLoop") .AddMethod("cleanUpResources", "_cleanUpResources") .AddDestructor("_cleanUpResources") EndWith Return $sClass.Object EndFunc ;==>Snake Func _runGameLoop($this) While 1 If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop If _IsPressed(25) Then $this.dx = -1 $this.dy = 0 EndIf If _IsPressed(27) Then $this.dx = 1 $this.dy = 0 EndIf If _IsPressed(26) Then $this.dx = 0 $this.dy = -1 EndIf If _IsPressed(28) Then $this.dx = 0 $this.dy = 1 EndIf If _IsPressed(53) Then While 1 If _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) Then ExitLoop Sleep(100) WEnd EndIf $this.drawStage() Sleep($this.speedLevel) WEnd EndFunc ;==>_runGameLoop Func _move($this) Local $aMap = $this.Map Local $head = $aMap[1] Local $newX = $head.getXPos() + $this.dx * 10 Local $newY = $head.getYPos() + $this.dy * 10 Local $hW = $newX + 10 Local $hH = $newY + 10 If $newX <= 0 Or $newX >= 390 Or $newY <= 0 Or $newY >= 390 Then MsgBox(64, "Game Over", "Collision! Game Over.") Local $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() _GDIPlus_Shutdown() _AutoItObject_Shutdown() Exit EndIf EndIf For $i = 2 To UBound($aMap) - 1 Local $cX = $aMap[$i].getXPos() Local $cY = $aMap[$i].getYPos() Local $cW = $cX + 10 Local $cH = $cY + 10 If $newX < $cW And $hW > $cX And $newY < $cH And $hH > $cY Then MsgBox(64, "Game Over", " SELF!!! Collision! Game Over.") $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() _GDIPlus_Shutdown() _AutoItObject_Shutdown() Exit EndIf EndIf Next Local $food = $aMap[0] Local $fX = $food.getXPos() Local $fY = $food.getYPos() Local $fW = $fX + 10 Local $fH = $fY + 10 If $newX < $fW And $hW > $fX And $newY < $fH And $hH > $fY Then ;generate food $aMap[0].setXPos(Random(10, 390, 1)) $aMap[0].setYPos(Random(10, 390, 1)) Local $lastSegment = $aMap[UBound($aMap) - 1] Local $lastX = $lastSegment.getXPos() + $this.dx() * 10 Local $lastY = $lastSegment.getYPos() + $this.dy() * 10 MapAppend($aMap, Point($lastX, $lastY)) $this.Map = $aMap $this.foodCounter += 1 If $this.foodCounter = 4 Then $this.speedLevel -= 20 If $this.speedLevel < 0 Then $this.speedLevel = 1 EndIf $this.foodCounter = 0 EndIf EndIf $head.setXPos($newX) $head.setYPos($newY) For $i = UBound($aMap) - 1 To 2 Step -1 $aMap[$i].setXPos($aMap[$i - 1].getXPos()) $aMap[$i].setYPos($aMap[$i - 1].getYPos()) Next EndFunc ;==>_move Func _drawStage($this) Local $gdiMap = $this.gdiMap Local $aMap = $this.Map Local $hGraphics = $gdiMap["hGraphics"] Local $hBrush = $gdiMap["hBrush"] Local $hFoodBrush = $gdiMap["hFoodBrush"] $this.move() _GDIPlus_GraphicsClear($hGraphics, 0xFF000000) _GDIPlus_GraphicsFillRect($hGraphics, $aMap[0].getXPos(), $aMap[0].getYPos(), 10, 10, $hFoodBrush) For $i = 1 To UBound($aMap) - 1 _GDIPlus_GraphicsFillRect($hGraphics, $aMap[$i].getXPos(), $aMap[$i].getYPos(), 10, 10, $hBrush) Next EndFunc ;==>_drawStage Func _resetGame($this) For $item In $this.Map $item = 0 Next $this.Map = 0 Local $aNewMap[] MapAppend($aNewMap, Point(Random(10, $this.iBoardWidth - 10), Random(10, $this.iBoardHeight - 10))) MapAppend($aNewMap, Point(Random(10, $this.iBoardWidth - 10), Random(10, $this.iBoardHeight - 10))) $this.Map = $aNewMap $this.dx = Random(-1, 1, 1) $this.dy = Random(-1, 1, 1) $this.speedLevel = 100 $this.foodCounter = 0 $this.drawStage() EndFunc ;==>_resetGame Func _cleanUpResources($this) Local $gdiMap = $this.gdiMap Local $hGraphics = $gdiMap["hGraphics"] Local $hBrush = $gdiMap["hBrush"] Local $hFoodBrush = $gdiMap["hFoodBrush"] _GDIPlus_GraphicsDispose($hGraphics) _GDIPlus_BrushDispose($hBrush) _GDIPlus_BrushDispose($hFoodBrush) _GDIPlus_Shutdown() For $gdih In $gdiMap $gdih = 0 Next $this.gdiMap = 0 For $oPoint In $this.Map $oPoint = 0 Next $this.Map = 0 _AutoItObject_Shutdown() EndFunc ;==>_cleanUpResources Func Point($X = 0, $Y = 0) Local $cPoint = _AutoItObject_Class() With $cPoint .Create() .AddProperty("X", $ELSCOPE_PRIVATE, $X) .AddProperty("Y", $ELSCOPE_PRIVATE, $Y) .AddMethod("setXPos", "_setXPos") .AddMethod("setYPos", "_setYPos") .AddMethod("getXPos", "_getXPos") .AddMethod("getYPos", "_getYPos") EndWith Return $cPoint.Object EndFunc ;==>Point Func _setXPos($this, $iX) $this.X = $iX EndFunc ;==>_setXPos Func _setYPos($this, $iY) $this.Y = $iY EndFunc ;==>_setYPos Func _getXPos($this) Return $this.X EndFunc ;==>_getXPos Func _getYPos($this) Return $this.Y EndFunc ;==>_getYPos Func _ErrFunc($oError) ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc ;***************************************************************************************************************************************** Global $sNake = Snake(0xFF8B4513) $sNake.runGameLoop() $sNake = 0 ;***************************************************************************************************************************************** _AutoItObject_Shutdown() Link to comment Share on other sites More sharing options...
Numeric1 Posted April 19 Author Share Posted April 19 EDIT : I've added a pause feature for the snake. You can now press the S key to put the game on hold and take a coffee break. argumentum 1 Link to comment Share on other sites More sharing options...
Numeric1 Posted April 22 Author Share Posted April 22 Initialization: Upon starting the game, a game window titled "Snake" is created with dimensions of 400x400 pixels. The snake and food objects are initialized, and the game environment is set up. Game Loop: The game runs in a continuous loop until the user closes the window. During each iteration of the loop, the game performs the following actions: Snake Movement: The snake automatically moves in the direction indicated by the arrow keys (up, down, left, or right). The snake's movement speed is determined by the speedLevel property, which decreases as the snake eats more food. User Input: The user can control the snake's direction using the arrow keys: Up arrow: Move the snake upwards. Down arrow: Move the snake downwards. Left arrow: Move the snake to the left. Right arrow: Move the snake to the right. Pressing the 'S' key pauses the game until another arrow key is pressed. Food Generation: Normal food items and poisoned food items are randomly generated on the game board. Normal food items are represented by green rectangles, while poisoned food items are represented by red rectangles. Normal food increases the snake's length and speed when eaten, while poisoned food triggers a warning message and restarts the game when eaten. Collision Detection: The game checks for collisions between the snake and various elements: Collision with the edges of the game board: Ends the game and prompts the user to restart. Collision with itself (i.e., when the snake's head intersects with its body segments): Ends the game and prompts the user to restart. Collision with food items: Determines whether the food is normal or poisoned, and adjusts the game accordingly. Game Over: When a collision occurs, a message box appears indicating the cause of the collision (e.g., collision with the edge of the board or with the snake itself). The user is given the option to restart the game or exit. Game Restart: If the user chooses to restart the game, the game environment is reset, and a new game begins. The snake's length, speed, and other parameters are reset to their initial values. Graphics: The game board and its elements (snake segments and food items) are drawn using GDI+ graphics. The graphics are refreshed continuously to reflect changes in the game state. In summary, the game progresses as the snake moves around the board, eating food items and avoiding collisions. The objective is to survive as long as possible without colliding with obstacles. The game provides a challenging and entertaining experience for players. Dependencies: AutoItObject.au3: This script is essential for enabling object-oriented programming (OOP) in AutoIt. It allows the creation of custom classes, properties, and methods, which are utilized extensively in the Snake game implementation. LinkedList.au3: This script provides the LinkedList data structure, which is used to manage the segments of the snake's body and the food items on the game board efficiently. LinkedList Usage: Managing Snake Segments: The LinkedList data structure is used to store the segments of the snake's body. Each segment is represented as an object with properties for its position (X and Y coordinates). The LinkedList allows easy addition, removal, and traversal of these segments. Managing Food Items: Similarly, the LinkedList is utilized to manage the food items on the game board. Each food item is represented as an object with properties for its position and type (normal or poisoned). The LinkedList enables dynamic addition and removal of food items during the game. Efficient Data Management: By using a LinkedList to store the snake segments and food items, the game can efficiently handle dynamic changes in the number of segments and food items. This data structure provides flexibility and scalability, allowing the game to adapt to different scenarios and game states. expandcollapse popup#include-once #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #INDEX# ======================================================================================================================= ; Title .........: Snake Game ; AutoIt Version : 3.3 ; AutoItObject Version : v1.2.8.2 ; Language ......: English ; Description ...: A simple Snake game implementation using AutoIt->AutoItObject.au3. ; Dependencies ..: AutoItObject.au3 ; Author ........: Numeric ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include "AutoItObject.au3" #include <GDIPlus.au3> #include <Misc.au3> #include "LinkedList.au3" Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") _GDIPlus_Startup() _AutoItObject_Startup() ; Error handling function Func _ErrFunc($oError) ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc ; Define Segment class Func Segment($X = 0, $Y = 0) Local $cSegment = _AutoItObject_Class() With $cSegment .Create() .AddProperty("X", $ELSCOPE_PRIVATE, $X) .AddProperty("Y", $ELSCOPE_PRIVATE, $Y) .AddMethod("setXPos", "_setXPos") .AddMethod("setYPos", "_setYPos") .AddMethod("getXPos", "_getXPos") .AddMethod("getYPos", "_getYPos") EndWith Return $cSegment.Object EndFunc ;==>Segment ; Methods for setting and getting X and Y positions of a Segment Func _setXPos($this, $iX) $this.X = $iX EndFunc ;==>_setXPos Func _setYPos($this, $iY) $this.Y = $iY EndFunc ;==>_setYPos Func _getXPos($this) Return $this.X EndFunc ;==>_getXPos Func _getYPos($this) Return $this.Y EndFunc ;==>_getYPos ; Define Food class Func Food($X = 0, $Y = 0, $Type = "normal") Local $cFood = _AutoItObject_Class() With $cFood .Create() .AddProperty("X", $ELSCOPE_PUBLIC, $X) .AddProperty("Y", $ELSCOPE_PUBLIC, $Y) .AddProperty("Type", $ELSCOPE_PUBLIC, $Type) EndWith Return $cFood.Object EndFunc ;==>Food ; Define Snake class Func Snake($iBackgroundColor = 0xFF0000FF) Local $dx = Random(-1, 1, 1) Local $dy = Random(-1, 1, 1) Local $oLinkedList = LinkedList() Local $oFoodList = LinkedList() $oLinkedList.insertAtIndex(Segment(Random(10, 380, 1), Random(10, 380, 1)), 0) ; head $oFoodList.insertAtIndex(Food(Random(10, 380, 1), Random(10, 380, 1), "normal"), 0) ; food Local $Board = GUICreate("Snake", 400, 400) GUISetState() Local $aBoardPos = WinGetClientSize($Board) Local $iBoardWidth = $aBoardPos[0] Local $iBoardHeight = $aBoardPos[1] Local $aGDIMap[] $aGDIMap["hGraphics"] = _GDIPlus_GraphicsCreateFromHWND($Board) $aGDIMap["hBitmap"] = _GDIPlus_BitmapCreateFromGraphics($iBoardWidth, $iBoardHeight, $aGDIMap["hGraphics"]) $aGDIMap["hGraphicsContext"] = _GDIPlus_ImageGetGraphicsContext($aGDIMap["hBitmap"]) $aGDIMap["hBrush"] = _GDIPlus_BrushCreateSolid($iBackgroundColor) $aGDIMap["hFoodBrush"] = _GDIPlus_BrushCreateSolid(0xFF00FF00) $aGDIMap["hPoisonedFoodBrush"] = _GDIPlus_BrushCreateSolid(0xFFFF0000) $aGDIMap["headBrush"] = _GDIPlus_HatchBrushCreate(4, 0xFF00FF00, $iBackgroundColor) Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("dx", $ELSCOPE_PUBLIC, $dx) .AddProperty("dy", $ELSCOPE_PUBLIC, $dy) .AddProperty("speedLevel", $ELSCOPE_PUBLIC, 100) .AddProperty("foodCounter", $ELSCOPE_PUBLIC, 0) .AddProperty("SnakeSegments", $ELSCOPE_PUBLIC, $oLinkedList) .AddProperty("foodList", $ELSCOPE_PUBLIC, $oFoodList) .AddProperty("gdiMap", $ELSCOPE_PUBLIC, $aGDIMap) .AddProperty("Board", $ELSCOPE_PUBLIC, $Board) .AddProperty("iBoardWidth", $ELSCOPE_PUBLIC, $iBoardWidth) .AddProperty("iBoardHeight", $ELSCOPE_PUBLIC, $iBoardHeight) .AddProperty("ifreq", $ELSCOPE_PUBLIC, 0) .AddProperty("poisonedFoodDuration", $ELSCOPE_PUBLIC, 50) .AddMethod("move", "_move") .AddMethod("drawStage", "_drawStage") .AddMethod("resetGame", "_resetGame") .AddMethod("runGameLoop", "_runGameLoop") .AddMethod("cleanUpResources", "_cleanUpResources") .AddDestructor("_cleanUpResources") EndWith Return $sClass.Object EndFunc ;==>Snake ; Run the game loop Func _runGameLoop($this) Local $isPoisonedFoodPresent = False Local $poisonedFoodDisappearTime = 900 ; Duration in milliseconds after which poisoned food disappears Local $timeCounter = TimerInit() ; Initialize the time counter While 1 If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop $this.ifreq += 1 ; Check if poisoned food is present If Not $isPoisonedFoodPresent Then ; If no poisoned food is present, generate a new one after a delay If $this.ifreq >= 70 Then $this.foodList.insertAtEnd(Food(Random(10, 380, 1), Random(10, 380, 1), "poisoned")) ; Food $this.ifreq = 0 $isPoisonedFoodPresent = True $timeCounter = TimerInit() ; Reset the time counter EndIf Else ; If poisoned food is present, check if it should disappear If TimerDiff($timeCounter) >= $poisonedFoodDisappearTime Then ; If elapsed time exceeds the poisoned food disappearance duration $this.foodList.remove_last_node() $isPoisonedFoodPresent = False ; Mark poisoned food as disappeared EndIf EndIf ; Check for user input to control the snake's movement If _IsPressed(25) Then $this.dx = -1 $this.dy = 0 EndIf If _IsPressed(27) Then $this.dx = 1 $this.dy = 0 EndIf If _IsPressed(26) Then $this.dx = 0 $this.dy = -1 EndIf If _IsPressed(28) Then $this.dx = 0 $this.dy = 1 EndIf If _IsPressed(53) Then ; If the S key is pressed, pause the game until another arrow key is pressed While 1 If _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) Then ExitLoop Sleep(100) WEnd EndIf ; Draw the game stage $this.drawStage() ; Pause for a short duration determined by the snake's speed level Sleep($this.speedLevel) WEnd EndFunc ;==>_runGameLoop ; Move the snake Func _move($this) Local $oLinkedList = $this.SnakeSegments Local $head = $oLinkedList.getAtIndex(0) Local $newX = $head.getXPos() + $this.dx * 10 Local $newY = $head.getYPos() + $this.dy * 10 Local $hW = $newX + 10 Local $hH = $newY + 10 ; Check collisions with the edges of the screen If $newX <= 0 Or $newX >= $this.iBoardWidth - 10 Or $newY <= 0 Or $newY >= $this.iBoardHeight - 10 Then MsgBox(64, "Game Over", "Collision! Game Over.") Local $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() Exit EndIf EndIf ; Check collisions with the snake's body For $i = 1 To $oLinkedList.sizeOfLL() - 1 Local $segment = $oLinkedList.getAtIndex($i) Local $cX = $segment.getXPos() Local $cY = $segment.getYPos() Local $cW = $cX + 10 Local $cH = $cY + 10 If $newX < $cW And $hW > $cX And $newY < $cH And $hH > $cY Then MsgBox(64, "Game Over", "Collision with self! Game Over.") $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() Exit EndIf EndIf Next ; Check collisions with food For $i = 0 To $this.foodList.sizeOfLL() - 1 Local $food = $this.foodList.getAtIndex($i) Local $fX = $food.X Local $fY = $food.Y Local $fW = $fX + 10 Local $fH = $fY + 10 If $newX < $fW And $hW > $fX And $newY < $fH And $hH > $fY Then If $food.Type = "poisoned" Then MsgBox(64, "Game Restart", "Warning! You have eaten poisoned food! The game will restart for your safety.") $this.resetGame() Return True EndIf ; Generate new food $food.X = Random(10, $this.iBoardWidth - 10) $food.Y = Random(10, $this.iBoardHeight - 10) ; Add a new segment to the snake Local $lastSegment = $oLinkedList.getLastNode() Local $lastX = $lastSegment.getXPos() Local $lastY = $lastSegment.getYPos() $oLinkedList.insertAtEnd(Segment($lastX, $lastY)) ; Update food counter and speed level $this.foodCounter += 1 If $this.foodCounter = 4 Then $this.speedLevel -= 20 If $this.speedLevel < 0 Then $this.speedLevel = 1 EndIf $this.foodCounter = 0 EndIf EndIf Next ; Update positions of subsequent segments For $i = $oLinkedList.sizeOfLL() - 1 To 1 Step -1 Local $currentSegment = $oLinkedList.getAtIndex($i) Local $previousSegment = $oLinkedList.getAtIndex($i - 1) $currentSegment.setXPos($previousSegment.getXPos()) $currentSegment.setYPos($previousSegment.getYPos()) Next ; Update head position $head.setXPos($newX) $head.setYPos($newY) EndFunc ;==>_move ; Reset the game Func _resetGame($this) Local $oLinkedList = $this.SnakeSegments Local $oFoodList = $this.foodList ; Clear snake segments linked list $oLinkedList.clear() $oFoodList.clear() ; Generate new snake segments $oLinkedList.insertAtIndex(Segment(Random(10, $this.iBoardWidth - 10), Random(10, $this.iBoardHeight - 10)), 0) $oFoodList.insertAtIndex(Food(Random(10, $this.iBoardWidth - 10), Random(10, $this.iBoardHeight - 10)), 0) ; Generate new food Local $food = $this.foodList.getAtIndex(0) $food.X = Random(10, $this.iBoardWidth - 10) $food.Y = Random(10, $this.iBoardHeight - 10) ; Reset snake direction and other parameters $this.dx = Random(-1, 1, 1) $this.dy = Random(-1, 1, 1) $this.speedLevel = 100 $this.foodCounter = 0 $this.poisonedFoodDuration = 50 ; Redraw game board $this.drawStage() EndFunc ;==>_resetGame ; Draw the game stage Func _drawStage($this) Local $gdiMap = $this.gdiMap Local $oLinkedList = $this.SnakeSegments Local $oFoodList = $this.foodList Local $hGraphics = $gdiMap["hGraphics"] Local $hGraphicsCtx = $gdiMap["hGraphicsContext"] Local $hBrush = $gdiMap["hBrush"] Local $hFoodBrush = $gdiMap["hFoodBrush"] Local $headBrush = $gdiMap["headBrush"] Local $hImage = $gdiMap["hBitmap"] $this.move() ; Clear previous graphics content _GDIPlus_GraphicsClear($hGraphicsCtx, 0xFF000000) ; Draw food Local $foodListIterator = $oFoodList.iterator() While Not $foodListIterator.isDone() Local $food = $foodListIterator.next() If $food.Type = "poisoned" Then $hFoodBrush = $gdiMap["hPoisonedFoodBrush"] EndIf _GDIPlus_GraphicsFillRect($hGraphicsCtx, $food.X, $food.Y, 10, 10, $hFoodBrush) WEnd _GDIPlus_GraphicsFillRect($hGraphicsCtx, $oLinkedList.getAtIndex(0).getXPos(), $oLinkedList.getAtIndex(0).getYPos(), 10, 10, $headBrush) ; Draw each snake segment For $i = 1 To $oLinkedList.sizeOfLL() - 1 Local $segment = $oLinkedList.getAtIndex($i) _GDIPlus_GraphicsFillRect($hGraphicsCtx, $segment.getXPos(), $segment.getYPos(), 10, 10, $hBrush) Next _GDIPlus_GraphicsDrawImageRect($hGraphics, $hImage, 0, 0, $this.iBoardWidth, $this.iBoardHeight) EndFunc ;==>_drawStage ; Clean up resources when game ends Func _cleanUpResources($this) Local $gdiMap = $this.gdiMap ; Dispose graphics resources _GDIPlus_GraphicsDispose($gdiMap["hGraphics"]) _GDIPlus_BrushDispose($gdiMap["hBrush"]) _GDIPlus_BrushDispose($gdiMap["hFoodBrush"]) _GDIPlus_BrushDispose($gdiMap["hPoisonedFoodBrush"]) _GDIPlus_Shutdown() ; Set map and linked list items to zero For $gdih In $this.gdiMap $gdih = 0 Next $this.gdiMap = 0 Local $oLinkedList = $this.SnakeSegments $oLinkedList.clear() ; Clear snake segments linked list ; Shutdown AutoItObject _AutoItObject_Shutdown() EndFunc ;==>_cleanUpResources ; Create and run the snake game Global $snakeGame = Snake() $snakeGame.runGameLoop() $snakeGame = 0 Link to comment Share on other sites More sharing options...
Andreik Posted April 22 Share Posted April 22 When the snake is long enough the chance of food being spawn under the snake it's pretty high. It would be a good idea to check if the location where the food is spawn it's an empty space. Also it's very hard to eat a poison food (even if you want) if it's randomly spawn and the duration of being displayed it's so short. It would be more interesting if the food stays longer or if the poison food it's spawned in the proximity of the snake. Link to comment Share on other sites More sharing options...
Numeric1 Posted April 22 Author Share Posted April 22 I have taken note of your valuable suggestion. What do you think about this idea? : instead of making poisoned food appear, I thought about making a speed reducer appear to give the player more luck. What do you think ? argumentum 1 Link to comment Share on other sites More sharing options...
Andreik Posted April 22 Share Posted April 22 Yes, why not? Whatever makes the game interesting and entertaining. Link to comment Share on other sites More sharing options...
Numeric1 Posted May 4 Author Share Posted May 4 Final version: I wanted to demonstrate the constant evolution of this code since its inception to highlight the ease of use of object-oriented programming in AutoItObject.au3, in addition to the remarkable power already provided by AutoIt. This combination forms a single entity: maximum power. So, I effortlessly introduced a new feature in the game: a speed reducer, where the snake must do everything to swallow it in order to slightly slow down and catch its breath. You'll notice that adding new features doesn't affect the structure of the code, which remains easy to maintain. That's the magic of AutoItObject. I encourage you to explore this aspect in depth. You are free to develop this game, whose code is clean, modular, and easy to design and maintain. expandcollapse popup#include-once #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 ; #INDEX# ======================================================================================================================= ; Title .........: Snake Game ; AutoIt Version : 3.3 ; AutoItObject Version : v1.2.8.2 ; Language ......: English ; Description ...: A simple Snake game implementation using AutoIt->AutoItObject.au3. ; Dependencies ..: AutoItObject.au3 ; Author ........: Numeric ; =============================================================================================================================== #include <GUIConstantsEx.au3> #include "AutoItObject.au3" #include <GDIPlus.au3> #include <Misc.au3> #include <WinAPISysWin.au3> #include "LinkedList.au3" Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") _GDIPlus_Startup() _AutoItObject_Startup() ; Error handling function Func _ErrFunc($oError) ConsoleWrite(@ScriptName & " (" & $oError.scriptline & ") : ==> COM Error intercepted !" & @CRLF & _ @TAB & "err.number is: " & @TAB & @TAB & "0x" & Hex($oError.number) & @CRLF & _ @TAB & "err.windescription:" & @TAB & $oError.windescription & @CRLF & _ @TAB & "err.description is: " & @TAB & $oError.description & @CRLF & _ @TAB & "err.source is: " & @TAB & @TAB & $oError.source & @CRLF & _ @TAB & "err.helpfile is: " & @TAB & $oError.helpfile & @CRLF & _ @TAB & "err.helpcontext is: " & @TAB & $oError.helpcontext & @CRLF & _ @TAB & "err.lastdllerror is: " & @TAB & $oError.lastdllerror & @CRLF & _ @TAB & "err.scriptline is: " & @TAB & $oError.scriptline & @CRLF & _ @TAB & "err.retcode is: " & @TAB & "0x" & Hex($oError.retcode) & @CRLF & @CRLF) EndFunc ;==>_ErrFunc ; Define Segment class Func Segment($X = 0, $Y = 0) Local $cSegment = _AutoItObject_Class() With $cSegment .Create() .AddProperty("X", $ELSCOPE_PRIVATE, $X) .AddProperty("Y", $ELSCOPE_PRIVATE, $Y) .AddMethod("setXPos", "_setXPos") .AddMethod("setYPos", "_setYPos") .AddMethod("getXPos", "_getXPos") .AddMethod("getYPos", "_getYPos") EndWith Return $cSegment.Object EndFunc ;==>Segment ; Methods for setting and getting X and Y positions of a Segment Func _setXPos($this, $iX) $this.X = $iX EndFunc ;==>_setXPos Func _setYPos($this, $iY) $this.Y = $iY EndFunc ;==>_setYPos Func _getXPos($this) Return $this.X EndFunc ;==>_getXPos Func _getYPos($this) Return $this.Y EndFunc ;==>_getYPos ; Define Food class Func Food($X = 0, $Y = 0, $Type = "normal") Local $cFood = _AutoItObject_Class() With $cFood .Create() .AddProperty("X", $ELSCOPE_PUBLIC, $X) .AddProperty("Y", $ELSCOPE_PUBLIC, $Y) .AddProperty("Type", $ELSCOPE_PUBLIC, $Type) EndWith Return $cFood.Object EndFunc ;==>Food ; Define Snake class Func Snake($iBackgroundColor = 0xFF0000FF) Local $dx = Random(-1, 1, 1) Local $dy = Random(-1, 1, 1) Local $oLinkedList = LinkedList() Local $oFoodList = LinkedList() $oLinkedList.insertAtIndex(Segment(Random(100, 280, 1), Random(100, 280, 1)), 0) ; head $oFoodList.insertAtIndex(Food(Random(10, 380, 1), Random(10, 380, 1), "normal"), 0) ; food Local $Board = GUICreate("Snake", 400, 400) GUISetState() Local $aBoardPos = WinGetClientSize($Board) Local $iBoardWidth = $aBoardPos[0] Local $iBoardHeight = $aBoardPos[1] Local $aGDIMap[] $aGDIMap["hGraphics"] = _GDIPlus_GraphicsCreateFromHWND($Board) $aGDIMap["hBitmap"] = _GDIPlus_BitmapCreateFromGraphics($iBoardWidth, $iBoardHeight, $aGDIMap["hGraphics"]) $aGDIMap["hGraphicsContext"] = _GDIPlus_ImageGetGraphicsContext($aGDIMap["hBitmap"]) $aGDIMap["hBrush"] = _GDIPlus_BrushCreateSolid($iBackgroundColor) $aGDIMap["hFoodBrush"] = _GDIPlus_BrushCreateSolid(0xFF00FF00) $aGDIMap["hPoisonedFoodBrush"] = _GDIPlus_BrushCreateSolid(0xFFFF0000) $aGDIMap["headBrush"] = _GDIPlus_HatchBrushCreate(4, 0xFF00FF00, $iBackgroundColor) $aGDIMap["speedPowerBrush"] = _GDIPlus_HatchBrushCreate(4, 0xFFD2691E, 0xFFFF00FF) Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("dx", $ELSCOPE_PUBLIC, $dx) .AddProperty("dy", $ELSCOPE_PUBLIC, $dy) .AddProperty("speedLevel", $ELSCOPE_PUBLIC, 100) .AddProperty("foodCounter", $ELSCOPE_PUBLIC, 0) .AddProperty("SnakeSegments", $ELSCOPE_PUBLIC, $oLinkedList) .AddProperty("foodList", $ELSCOPE_PUBLIC, $oFoodList) .AddProperty("powerList", $ELSCOPE_PUBLIC, LinkedList()) .AddProperty("gdiMap", $ELSCOPE_PUBLIC, $aGDIMap) .AddProperty("Board", $ELSCOPE_PUBLIC, $Board) .AddProperty("iBoardWidth", $ELSCOPE_PUBLIC, $iBoardWidth) .AddProperty("iBoardHeight", $ELSCOPE_PUBLIC, $iBoardHeight) .AddProperty("poisonedFoodManager", $ELSCOPE_PUBLIC, FoodManager()) .AddProperty("speedPowerManager", $ELSCOPE_PUBLIC, FoodManager()) .AddMethod("move", "_move") .AddMethod("drawStage", "_drawStage") .AddMethod("resetGame", "_resetGame") .AddMethod("runGameLoop", "_runGameLoop") .AddMethod("cleanUpResources", "_cleanUpResources") .AddDestructor("_cleanUpResources") EndWith Return $sClass.Object EndFunc ;==>Snake ; Creates and returns a FoodManager object with default properties. ; Note: Traps, power-ups, and food itself are all considered as "food" because the snake Kaa only wants to eat. Func FoodManager() Local $sClass = _AutoItObject_Class() With $sClass .Create() .AddProperty("ifreq", $ELSCOPE_PUBLIC, 0) .AddProperty("iduration", $ELSCOPE_PUBLIC, 50) .AddProperty("isPresent", $ELSCOPE_PUBLIC, False) .AddProperty("disappearTime", $ELSCOPE_PUBLIC, 2000) .AddProperty("timeCounter", $ELSCOPE_PUBLIC, 0) EndWith Return $sClass.Object EndFunc ;==>FoodManager ; Run the game loop Func _runGameLoop($this) $this.poisonedFoodManager.disappearTime = 4000 ; Duration in milliseconds after which poisoned food disappears $this.poisonedFoodManager.timeCounter = TimerInit() ; Initialize the time counter $this.speedPowerManager.disappearTime = 3000 $this.speedPowerManager.timeCounter = TimerInit() While 1 If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop _WinAPI_SetWindowText($this.Board, "Score : " & $this.foodCounter & " SpeedLevel : " & (100 - $this.speedLevel)) $this.poisonedFoodManager.ifreq += 1 $this.speedPowerManager.ifreq += 1 ; Check if speed reducer is present If Not $this.speedPowerManager.isPresent Then ; If no speed reducer is present, generate a new one after a delay If $this.speedPowerManager.ifreq >= 90 Then $this.powerList.insertAtEnd(Food(Random(10, 380, 1), Random(10, 380, 1), "speedPower")) $this.speedPowerManager.ifreq = 0 $this.speedPowerManager.isPresent = True $this.speedPowerManager.timeCounter = TimerInit() EndIf Else ; If speed reducer is present, check if it should disappear If TimerDiff($this.speedPowerManager.timeCounter) >= $this.speedPowerManager.disappearTime Then $this.powerList.clear() ;remove_last_node() $this.speedPowerManager.isPresent = False EndIf EndIf ; Check if poisoned food is present If Not $this.poisonedFoodManager.isPresent Then ; If no poisoned food is present, generate a new one after a delay If $this.poisonedFoodManager.ifreq >= 70 Then $this.foodList.insertAtEnd(Food(Random(10, 380, 1), Random(10, 380, 1), "poisoned")) ; Food $this.poisonedFoodManager.ifreq = 0 $this.poisonedFoodManager.isPresent = True $this.poisonedFoodManager.timeCounter = TimerInit() ; Reset the time counter EndIf Else ; If poisoned food is present, check if it should disappear If TimerDiff($this.poisonedFoodManager.timeCounter) >= $this.poisonedFoodManager.disappearTime Then ; If elapsed time exceeds the poisoned food disappearance duration $this.foodList.remove_last_node() $this.poisonedFoodManager.isPresent = False ; Mark poisoned food as disappeared EndIf EndIf ; Check for user input to control the snake's movement If _IsPressed(25) Then $this.dx = -1 $this.dy = 0 EndIf If _IsPressed(27) Then $this.dx = 1 $this.dy = 0 EndIf If _IsPressed(26) Then $this.dx = 0 $this.dy = -1 EndIf If _IsPressed(28) Then $this.dx = 0 $this.dy = 1 EndIf If _IsPressed(53) Then ; If the S key is pressed, pause the game until another arrow key is pressed While 1 If _IsPressed(25) Or _IsPressed(26) Or _IsPressed(27) Or _IsPressed(28) Then ExitLoop Sleep(100) WEnd EndIf ; Draw the game stage $this.drawStage() ; Pause for a short duration determined by the snake's speed level Sleep($this.speedLevel) WEnd EndFunc ;==>_runGameLoop ; Move the snake Func _move($this) Local $oLinkedList = $this.SnakeSegments Local $head = $oLinkedList.getAtIndex(0) Local $newX = $head.getXPos() + $this.dx * 10 Local $newY = $head.getYPos() + $this.dy * 10 Local $hW = $newX + 10 Local $hH = $newY + 10 ; Check collisions with the edges of the screen If $newX <= 0 Or $newX >= $this.iBoardWidth - 10 Or $newY <= 0 Or $newY >= $this.iBoardHeight - 10 Then MsgBox(64, "Game Over", "Collision! Game Over.") Local $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() Exit EndIf EndIf ; Check collisions with the snake's body For $i = 1 To $oLinkedList.sizeOfLL() - 1 Local $segment = $oLinkedList.getAtIndex($i) Local $cX = $segment.getXPos() Local $cY = $segment.getYPos() Local $cW = $cX + 10 Local $cH = $cY + 10 If $newX < $cW And $hW > $cX And $newY < $cH And $hH > $cY Then MsgBox(64, "Game Over", "Collision with self! Game Over.") $iResponse = MsgBox(36, "Restart Game", "Do you want to restart the game?") If $iResponse = 6 Then $this.resetGame() Return True Else $this.cleanUpResources() Exit EndIf EndIf Next ; Check collisions with food For $i = 0 To $this.foodList.sizeOfLL() - 1 Local $food = $this.foodList.getAtIndex($i) Local $fX = $food.X Local $fY = $food.Y Local $fW = $fX + 10 Local $fH = $fY + 10 If $newX < $fW And $hW > $fX And $newY < $fH And $hH > $fY Then If $food.Type = "poisoned" Then MsgBox(64, "Game Restart", "Warning! You have eaten poisoned food! The game will restart for your safety.") $this.resetGame() Return True EndIf ; Generate new food $food.X = Random(10, $this.iBoardWidth - 10) $food.Y = Random(10, $this.iBoardHeight - 10) ; Add a new segment to the snake Local $lastSegment = $oLinkedList.getLastNode() Local $lastX = $lastSegment.getXPos() Local $lastY = $lastSegment.getYPos() $oLinkedList.insertAtEnd(Segment($lastX, $lastY)) ; Update food counter and speed level $this.foodCounter += 1 If $this.foodCounter = 4 Then $this.speedLevel -= 20 If $this.speedLevel < 0 Then $this.speedLevel = 1 EndIf $this.foodCounter = 0 EndIf EndIf Next ; Check collisions with speedPower For $i = 0 To $this.powerList.sizeOfLL() - 1 Local $timeReducer = $this.powerList.getAtIndex($i) Local $tX = $timeReducer.X Local $tY = $timeReducer.Y Local $tW = $tX + 10 Local $tH = $tY + 10 If $newX < $tW And $hW > $tX And $newY < $tH And $hH > $tY Then If $timeReducer.Type = "speedPower" Then $this.speedLevel += 20 $this.powerList.clear() ;remove_last_node() $this.speedPowerManager.isPresent = False $this.speedPowerManager.timeCounter = TimerInit() EndIf EndIf Next ; Update positions of subsequent segments For $i = $oLinkedList.sizeOfLL() - 1 To 1 Step -1 Local $currentSegment = $oLinkedList.getAtIndex($i) Local $previousSegment = $oLinkedList.getAtIndex($i - 1) $currentSegment.setXPos($previousSegment.getXPos()) $currentSegment.setYPos($previousSegment.getYPos()) Next ; Update head position $head.setXPos($newX) $head.setYPos($newY) EndFunc ;==>_move ; Reset the game Func _resetGame($this) Local $oLinkedList = $this.SnakeSegments Local $oFoodList = $this.foodList Local $oPowerList = $this.powerList ; Clear snake segments linked list $oLinkedList.clear() $oFoodList.clear() $oPowerList.clear() ; Generate new snake segments $oLinkedList.insertAtIndex(Segment(Random(100, $this.iBoardWidth - 100), Random(100, $this.iBoardHeight - 100)), 0) $oFoodList.insertAtIndex(Food(Random(10, $this.iBoardWidth - 10), Random(10, $this.iBoardHeight - 10)), 0) ; Generate new food Local $food = $this.foodList.getAtIndex(0) $food.X = Random(10, $this.iBoardWidth - 10) $food.Y = Random(10, $this.iBoardHeight - 10) ; Reset snake direction and other parameters $this.dx = Random(-1, 1, 1) $this.dy = Random(-1, 1, 1) $this.speedLevel = 100 $this.foodCounter = 0 $this.poisonedFoodManager.ifreq = 0 $this.poisonedFoodManager.disappearTime = 4000 ; Duration in milliseconds after which poisoned food disappears $this.poisonedFoodManager.timeCounter = TimerInit() ; Initialize the time counter $this.speedPowerManager.ifreq = 0 $this.speedPowerManager.disappearTime = 3000 $this.speedPowerManager.timeCounter = TimerInit() ; Redraw game board $this.drawStage() EndFunc ;==>_resetGame ; Draw the game stage Func _drawStage($this) Local $gdiMap = $this.gdiMap Local $oLinkedList = $this.SnakeSegments Local $oFoodList = $this.foodList Local $oPowerList = $this.powerList Local $hGraphics = $gdiMap["hGraphics"] Local $hGraphicsCtx = $gdiMap["hGraphicsContext"] Local $hBrush = $gdiMap["hBrush"] Local $hFoodBrush = $gdiMap["hFoodBrush"] Local $headBrush = $gdiMap["headBrush"] Local $hImage = $gdiMap["hBitmap"] Local $speedPowerBrush = $gdiMap["speedPowerBrush"] $this.move() ; Clear previous graphics content _GDIPlus_GraphicsClear($hGraphicsCtx, 0xFF000000) ; Draw food Local $foodListIterator = $oFoodList.iterator() While Not $foodListIterator.isDone() Local $food = $foodListIterator.next() If $food.Type = "poisoned" Then $hFoodBrush = $gdiMap["hPoisonedFoodBrush"] EndIf _GDIPlus_GraphicsFillRect($hGraphicsCtx, $food.X, $food.Y, 10, 10, $hFoodBrush) WEnd Local $powerListIterator = $oPowerList.iterator() While Not $powerListIterator.isDone() Local $oPower = $powerListIterator.next() _GDIPlus_GraphicsFillRect($hGraphicsCtx, $oPower.X, $oPower.Y, 10, 10, $speedPowerBrush) WEnd _GDIPlus_GraphicsFillRect($hGraphicsCtx, $oLinkedList.getAtIndex(0).getXPos(), $oLinkedList.getAtIndex(0).getYPos(), 10, 10, $headBrush) ; Draw each snake segment For $i = 1 To $oLinkedList.sizeOfLL() - 1 Local $segment = $oLinkedList.getAtIndex($i) _GDIPlus_GraphicsFillRect($hGraphicsCtx, $segment.getXPos(), $segment.getYPos(), 10, 10, $hBrush) Next _GDIPlus_GraphicsDrawImageRect($hGraphics, $hImage, 0, 0, $this.iBoardWidth, $this.iBoardHeight) EndFunc ;==>_drawStage ; Clean up resources when game ends Func _cleanUpResources($this) Local $gdiMap = $this.gdiMap ; Dispose graphics resources _GDIPlus_GraphicsDispose($gdiMap["hGraphics"]) _GDIPlus_BrushDispose($gdiMap["hBrush"]) _GDIPlus_BrushDispose($gdiMap["hFoodBrush"]) _GDIPlus_BrushDispose($gdiMap["hPoisonedFoodBrush"]) _GDIPlus_Shutdown() ; Set map and linked list items to zero For $gdih In $this.gdiMap $gdih = 0 Next $this.gdiMap = 0 Local $oLinkedList = $this.SnakeSegments $oLinkedList.clear() ; Clear snake segments linked list ; Shutdown AutoItObject _AutoItObject_Shutdown() EndFunc ;==>_cleanUpResources ;==================================================== ; Create and run the snake game Global $snakeGame = Snake() $snakeGame.runGameLoop() $snakeGame = 0 ;==================================================== Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now