#include <GuiConstantsEx.au3>
#include <ScreenCapture.au3>
#include <Misc.au3>
#include <WinAPI.au3>
#include <Array.au3>
; Downloaded from <a href='http://www.autoitscript.com/forum/topic/27362-bitmap-library/' class='bbc_url' title=''>http://www.autoitscript.com/forum/topic/27362-bitmap-library/</a>
#include "BMP3.au3"
#include <GDIPlus.au3>
Global $Caption = "Maze Generator"
Global $FileSaved = @ScriptDir & "Maze.bmp"
Global $vDLL_user32 = DllOpen("user32.dll")
Global $hPic, $Pic
Global $iMazeX = 15, $iMazeY = 15
Global $Generate
Global $Progress = 0
Global $sBaseName = 'Maze'
Global $aiSize[2]
Global $aCells
; Create GUI
$hGUI = GUICreate($Caption, 900, 800)
$Generate = GUICtrlCreateButton('Generate', 10, 10, 100, 20)
$Solve = GUICtrlCreateButton('Solve', 150, 10, 100, 20)
$ToPng = GUICtrlCreateButton('ToPng', 300, 10, 100, 20)
$MazeX_Label = GuiCtrlCreateLabel("Rows:", 10, 60, 60, 20)
GUICtrlSetFont(-1,8,300)
$MazeX_Edit = GuiCtrlCreateInput('15', 70, 60, 100, 20)
GUICtrlSetFont(-1,8,300)
$MazeY_Label = GuiCtrlCreateLabel("Cols:", 10, 90, 60, 20)
GUICtrlSetFont(-1,8,300)
$MazeY_Edit = GuiCtrlCreateInput('15', 70, 90, 100, 20)
GUICtrlSetFont(-1,8,300)
If FileExists($FileSaved) then
$Pic = GUICtrlCreatePic($FileSaved, 190, 60, 700, 700)
else
$Pic = GUICtrlCreatePic("", 190, 60, 700, 700)
endif
$hPic = GUICtrlGetHandle($Pic)
GUISetState()
While 1
$Msg = GUIGetMsg()
switch $Msg
case $GUI_EVENT_CLOSE
ExitLoop
case $Generate
$Progress = 1
$iMazeX = GuiCtrlRead($MazeX_Edit)
$aiSize[0] = Round($iMazeX)
$iMazeY = GuiCtrlRead($MazeY_Edit)
$aiSize[1] = Round($iMazeY)
ProgressOn("Maze progress", "Working...", "0 percent")
$aCells = Generate_Maze($aiSize, $Progress)
Draw_Maze_Image($aiSize, $aCells, $sBaseName, $Progress)
sleep(500)
ProgressOff()
GUICtrlSetImage ($Pic, @ScriptDir & "" & $sBaseName & ".bmp")
case $Solve
$Progress = 1
$aCurrentPath = Solve_Maze($aiSize, $aCells, $Progress)
ProgressOn("Maze Solving progress", "Working...", "0 percent")
Solve_Maze_ImgDraw($aiSize, $aCurrentPath, $sBaseName, $Progress)
Draw_Maze_Image($aiSize, $aCells, $sBaseName, $Progress)
sleep(500)
ProgressOff()
GUICtrlSetImage ($Pic, @ScriptDir & "" & $sBaseName & "_Solved.bmp")
case $ToPng
_SaveAsPNG(@ScriptDir & "" & $sBaseName & ".bmp")
_SaveAsPNG(@ScriptDir & "" & $sBaseName & "_Solved.bmp")
case else
endswitch
WEnd
_GDIPlus_Shutdown()
DllClose($vDLL_user32)
Exit
;; ============================================================================================================== ;;
Func Generate_Maze(Const ByRef $aiSize, ByRef $Progress)
Local Const $iTotalCells = $aiSize[0] * $aiSize[1]
Local $iMoveToCell ; What cell to move to
Local $aCellstack[$iTotalCells][2];This stores with sequnce the cell got generated in. used for backtracing. First dimension is Row. Second is Colum
Local $iCurrentCellStackNumber = 0 ; Current subscript number to write to in $aCellstack
Local $aCurrentCellStack[2] ; USed as a temp storage of a bit of cellstack. First dimension is Row. Second is Colum
Local $iUnvisitedCells = $iTotalCells ; Number of cell that are unvisited
Local $sNeighbours = ''
Local $sNeighbourOk
Local $iRndDirection
Local $aCurrentCellRowColum
;; setup cell array.
Local $aCells[$iTotalCells];This is where the maze will be stored ; It will store witch walls are intact
Local $iCurrentCell = Random(0, $iTotalCells - 1, 1) ;; set random start point/cell
;Make all walls be intact by filling the cells array with 1111
array_filler($aCells, '1111')
Local $aVisitedCells[$iTotalCells]
array_filler($aVisitedCells, False)
While 1
;Mark as visited and add to the cell stack
$aCurrentCellRowColum = AddToCellStack($iCurrentCell, $iCurrentCellStackNumber, $aCellstack, $aiSize) ;; $iCurrentCell not used in function.
;Check to see if it sould stop createing the maze
If $iUnvisitedCells <> 0 Then
;Check to see if the current cell has any neighbours there are unvisited
$sNeighbours = _Neighbours($aCurrentCellRowColum, $iCurrentCell, $aVisitedCells, $aiSize)
If $sNeighbours <> "0000" Then
;Choose a random unvisited Neighbour
Do
$iRndDirection = Random(0, 3, 1)
$sNeighbourOk = StringMid($sNeighbours, $iRndDirection + 1, 1)
Until $sNeighbourOk = "1"
Switch $iRndDirection ;Witch side to move to
Case 0 ; Move Up
$iMoveToCell = $iCurrentCell - $aiSize[1]
Case 1 ; Move Right
$iMoveToCell = $iCurrentCell + 1
Case 2 ; Move Down
$iMoveToCell = $iCurrentCell + $aiSize[1]
Case 3 ; Move Left
$iMoveToCell = $iCurrentCell - 1
EndSwitch
BustWall($iCurrentCell, $iMoveToCell, $iRndDirection, $aCells)
$iCurrentCell = $iMoveToCell
;Make the current cell visited
$aVisitedCells[$iCurrentCell] = True
$iUnvisitedCells -= 1
Else
$aCurrentCellStack[0] = $aCellstack[$iCurrentCellStackNumber - 2][0] ; Get row of last item in cellstack
$aCurrentCellStack[1] = $aCellstack[$iCurrentCellStackNumber - 2][1] ; Get colum of last item in cellstack
$iCurrentCell = _GetCellPos($aiSize, $aCurrentCellStack) ; Combine row and colum to get pos
$iCurrentCellStackNumber -= 2 ; This will ensure that the used cell from the cellstack will be overwritten
EndIf
Else
ExitLoop
EndIf
;Update the progress
$Progress = Round(Map($iUnvisitedCells, $iTotalCells, 0, 0, 25.5) + 1)
ProgressSet( $Progress*2, $Progress*2 & " percent")
WEnd
$Progress = Round(25.5) ; Update progress
ProgressSet( $Progress*2, $Progress*2 & " percent")
Return $aCells
EndFunc ;==>Generate_Maze
Func Draw_Maze_Image(Const ByRef $aiSize, ByRef Const $aCells, ByRef Const $sFilename, ByRef $Progress)
Local Const $iTotalCells = $aiSize[0] * $aiSize[1]
Local $aImageRowColum
Local $tBMP = _BMPCreate($aiSize[1] * 9, $aiSize[0] * 9) ;Define the size of the maze (in bmp)
;Draw the maze with no walls
For $i = 0 To $iTotalCells - 1
$aImageRowColum = _GetCellPos($aiSize, $i)
Draw3By3($tBMP, $aImageRowColum[0] * 9, $aImageRowColum[1] * 9)
Draw3By3($tBMP, 6 + $aImageRowColum[0] * 9, $aImageRowColum[1] * 9)
Draw3By3($tBMP, $aImageRowColum[0] * 9, 6 + $aImageRowColum[1] * 9)
Draw3By3($tBMP, 6 + $aImageRowColum[0] * 9, 6 + $aImageRowColum[1] * 9)
$Progress = Round(Map($i, 0, $iTotalCells-1, 0, 10) + 26.5) ; Update progress
ProgressSet( $Progress*2, $Progress*2 & " percent")
Next
;Then Draw the wall on the cells
For $i = 0 To $iTotalCells - 1
$aImageRowColum = _GetCellPos($aiSize, $i)
If StringMid($aCells[$i], 1, 1) = "1" Then
Draw3By3($tBMP, 3 + $aImageRowColum[1] * 9, $aImageRowColum[0] * 9)
EndIf
If StringMid($aCells[$i], 2, 1) = "1" Then
Draw3By3($tBMP, 6 + $aImageRowColum[1] * 9, 3 + $aImageRowColum[0] * 9)
EndIf
If StringMid($aCells[$i], 3, 1) = "1" Then
Draw3By3($tBMP, 3 + $aImageRowColum[1] * 9, 6 + $aImageRowColum[0] * 9)
EndIf
If StringMid($aCells[$i], 4, 1) = "1" Then
Draw3By3($tBMP, $aImageRowColum[1] * 9, 3 + $aImageRowColum[0] * 9)
EndIf
$Progress = Round(Map($i, 0, $iTotalCells-1, 0, 10) + 36.5) ; Update progress
ProgressSet($Progress*2, $Progress*2 & " percent")
Next
;Draw start and exit.
;Start will be green and exit will be red
Draw3By3($tBMP, 3, 3, '00FF00') ; Draw a green cube in the first cell(Top Left Corner)
$aImageRowColum = _GetCellPos($aiSize, $iTotalCells - 1);Get row and colum of the last cell in the maze
Draw3By3($tBMP, 3 + + $aImageRowColum[1] * 9, 3 + $aImageRowColum[0] * 9, 'FF0000') ; Draw a red cube in the last cell (Buttom right corner)
$Progress += 1.5 ; Update progress
ProgressSet( Round($Progress)*2, Round($Progress)*2 & " percent")
;And finaly save the bmp as Maze.bmp
_BMPWrite($tBMP, @ScriptDir & "" & $sFilename & ".bmp")
$Progress = 50 ; Update progress
ProgressSet( $Progress*2, $Progress*2 & " percent")
EndFunc ;==>Draw_Maze_Image
;=====================================
Func Solve_Maze(Const ByRef $aiSize, ByRef $aCells, ByRef $Progress)
Local Const $iTotalCells = $aiSize[0] * $aiSize[1]
Local $aCurrentPath[$iTotalCells] ; Will store the current solved path
Local $aSolveVisitedCells[$iTotalCells] ;This will store if the solver has been to a cell yet or not
Local $iCurrentPathSubscriptNumber = -1 ; Current subscript number to write to in $aCurrentPath
Local $iCurrentCell = 0
AddToCurrentPath($iCurrentCell, $iCurrentPathSubscriptNumber, $aCurrentPath) ; Add the cell the the path
$aSolveVisitedCells[$iCurrentCell] = 1;Mark cell as visited
;Loop until the path has been found
Local $CanGoOrNot
Local $aYX[2] ; Store Y and X in an array ; used in solveing the maze
While 1
;Find a direction to go
For $iDirection = 0 To 3
;;If $iDirection = 4 Then ExitLoop
$aYX = _GetCellPos($aiSize, $iCurrentCell); Convert it to rows and colums
$CanGoOrNot = CanGo($aYX[0], $aYX[1], $iDirection, $aCells, $aSolveVisitedCells, $aiSize) ; Check if it can go there
If $CanGoOrNot = 1 Then ExitLoop
Next
If $iDirection <> 4 Then
Switch $iDirection
Case 0 ; Up
$iCurrentCell -= $aiSize[0]
Case 1 ; Right
$iCurrentCell += 1
Case 2 ; Down
$iCurrentCell += $aiSize[0]
Case 3 ; Left
$iCurrentCell -= 1
EndSwitch
AddToCurrentPath($iCurrentCell, $iCurrentPathSubscriptNumber, $aCurrentPath) ; Add the cell the the path
$aSolveVisitedCells[$iCurrentCell] = True;Mark cell as visited
Else
If $iCurrentPathSubscriptNumber > 0 Then
$iCurrentCell = $aCurrentPath[$iCurrentPathSubscriptNumber - 1]
$iCurrentPathSubscriptNumber -= 1
;Else
; $iCurrentPathSubscriptNumber -= 1
EndIf
EndIf
If $iCurrentCell = $iTotalCells - 1 Then ExitLoop
WEnd
$Progress = Round(76.5) ; Update progress
ProgressSet( ($Progress-50)*2, ($Progress-50)*2 & " percent")
Return $aCurrentPath
EndFunc ;==>Solve_Maze
;=====================================
Func Solve_Maze_ImgDraw(Const ByRef $aiSize, ByRef $aCurrentPath, Const ByRef $sFilename, ByRef $Progress)
Local Const $iTotalCells = $aiSize[0] * $aiSize[1]
;This function will use functions from the Drawing Functions region
;Open The Generated maze as a bmp handle
Local $tBMP = _BMPOpen(@ScriptDir & "" & $sFilename & ".bmp")
Local $aCurrentRowAndColum
;Go thoug all the cells one by one
For $i = 0 To UBound($aCurrentPath) - 1
If $aCurrentPath[$i] = $iTotalCells - 1 Then ExitLoop
If $aCurrentPath[$i] <> "" Then
$aCurrentRowAndColum = _GetCellPos($aiSize, $aCurrentPath[$i])
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 3, 9 * $aCurrentRowAndColum[0] + 3, 'FFFF00')
If $aCurrentPath[$i] - $aiSize[0] = $aCurrentPath[$i + 1] Then ; Up
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 3, 9 * $aCurrentRowAndColum[0], 'FFFF00')
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 3, 9 * $aCurrentRowAndColum[0] - 3, 'FFFF00')
ElseIf $aCurrentPath[$i] + 1 = $aCurrentPath[$i + 1] Then ;Right
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 6, 9 * $aCurrentRowAndColum[0] + 3, 'FFFF00')
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 9, 9 * $aCurrentRowAndColum[0] + 3, 'FFFF00')
ElseIf $aCurrentPath[$i] + $aiSize[0] = $aCurrentPath[$i + 1] Then ; Down
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 3, 9 * $aCurrentRowAndColum[0] + 6, 'FFFF00')
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] + 3, 9 * $aCurrentRowAndColum[0] + 9, 'FFFF00')
ElseIf $aCurrentPath[$i] - 1 = $aCurrentPath[$i + 1] Then ;LEft
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1], 9 * $aCurrentRowAndColum[0] + 3, 'FFFF00')
Draw3By3($tBMP, 9 * $aCurrentRowAndColum[1] - 3, 9 * $aCurrentRowAndColum[0] + 3, 'FFFF00')
EndIf
EndIf
$Progress = Map($i, 0, UBound($aCurrentPath) - 1, 0, 22.5) + 76.5 ; Update progress
ProgressSet(Round($Progress-50)*2, Round($Progress-50)*2 & " percent")
Next
;reDraw start and exit.
;Start will be green and exit will be red
Draw3By3($tBMP, 3, 3, '00FF00') ; Draw a green cube in the first cell(Top Left Corner)
Local $aImageRowColum = _GetCellPos($aiSize, $iTotalCells - 1);Get row and colum of the last cell in the maze
Draw3By3($tBMP, 3 + + $aImageRowColum[1] * 9, 3 + $aImageRowColum[0] * 9, 'FF0000') ; Draw a red cube in the last cell (Buttom right corner)
;And finaly save th bmp as Maze.bmp
_BMPWrite($tBMP, @ScriptDir & "" & $sFilename & "_Solved.bmp")
$Progress = 100 ; Update progress
ProgressSet( ($Progress-50)*2, ($Progress-50)*2 & " percent")
EndFunc ;==>Solve_Maze_ImgDraw
;=====================================
Func BustWall($Cell1, $Cell2, $iDirection, ByRef $aCells) ; This function will remove the walls in two adjacent cells. Direction is from first cell to second. Direction can be from 0 - 3. 0 = Up, 1 = Right, 2 = Down, 3 = Left
$aCells[$Cell1] = StringReplace($aCells[$Cell1], $iDirection + 1, "0", 1, 2) ; Bust the wall between cell1 and cell2 in cell1
FlipDirection_br($iDirection)
$aCells[$Cell2] = StringReplace($aCells[$Cell2], $iDirection + 1, "0", 1, 2) ; Bust the wall between cell1 and cell2 in cell2
EndFunc ;==>BustWall
;=====================================
Func AddToCellStack($iCurrentCell, ByRef $iCurrentCellStackNumber, ByRef $aCellstack, Const ByRef $aiSize); This function will add the $Cell to the $aCellstack at the subscript of $CurretCellStackNumber
;Convert to Rows and colums
Local $aCurrentCellRowColum = _GetCellPos($aiSize, $iCurrentCell)
;Add cell to the cell stack
$aCellstack[$iCurrentCellStackNumber][0] = $aCurrentCellRowColum[0]
$aCellstack[$iCurrentCellStackNumber][1] = $aCurrentCellRowColum[1]
;Add one to $iCurrentCellStackNumber
$iCurrentCellStackNumber += 1
Return $aCurrentCellRowColum
EndFunc ;==>AddToCellStack
;=====================================
Func _Neighbours(Const ByRef $aCurrentCellRowColum, $Cell, ByRef Const $aVisitedCells, Const ByRef $aiSize) ; Check for Neighbours and store them in a string an 1 means that the neighbour has not been visited and an 0 means the neighbour has been visited it also checks for the edge of the maze.
Local $NeighbourRowColum, $sNeighbours = ''
Local Const $iTotalCells = $aiSize[0] * $aiSize[1]
;Check Clockwise
;Check Above cell
If ($Cell - $aiSize[1] >= 0) And ($aVisitedCells[$Cell - $aiSize[1]] = False) Then
$sNeighbours &= "1"
Else
$sNeighbours &= "0"
EndIf
;Check Right Cell
$NeighbourRowColum = _GetCellPos($aiSize, $Cell + 1)
If ($aCurrentCellRowColum[0] >= $NeighbourRowColum[0]) And ($Cell + 1 < $iTotalCells) And ($aVisitedCells[$Cell + 1] = False) Then
$sNeighbours &= "1"
Else
$sNeighbours &= "0"
EndIf
;Check Buttom Cell
If ($Cell + $aiSize[1] < $iTotalCells) And ($aVisitedCells[$Cell + $aiSize[1]] = False) Then
$sNeighbours &= "1"
Else
$sNeighbours &= "0"
EndIf
;Check Left Cell
$NeighbourRowColum = _GetCellPos($aiSize, $Cell - 1)
If ($aCurrentCellRowColum[0] <= $NeighbourRowColum[0]) And ($Cell - 1 >= 0) And ($aVisitedCells[$Cell - 1] = False) Then
$sNeighbours &= "1"
Else
$sNeighbours &= "0"
EndIf
Return $sNeighbours
EndFunc ;==>_Neighbours
;=====================================
Func Draw3By3(ByRef $tBMP, $x, $y, $Colur = '000000') ; This function will draw a 3*3 Cube at a given x and y on the $tBMP
_PixelWrite($tBMP, $x, $y, $Colur)
_PixelWrite($tBMP, $x + 1, $y, $Colur)
_PixelWrite($tBMP, $x + 2, $y, $Colur)
_PixelWrite($tBMP, $x, $y + 1, $Colur)
_PixelWrite($tBMP, $x + 1, $y + 1, $Colur)
_PixelWrite($tBMP, $x + 2, $y + 1, $Colur)
_PixelWrite($tBMP, $x, $y + 2, $Colur)
_PixelWrite($tBMP, $x + 1, $y + 2, $Colur)
_PixelWrite($tBMP, $x + 2, $y + 2, $Colur)
EndFunc ;==>Draw3By3
#endregion Drawing Functions
;=====================================
Func CanGo($y, $x, ByRef Const $iDirection, ByRef Const $aCells, ByRef Const $aSolveVisitedCells, Const ByRef $aiSize);Returns 1 if the direction can the traveled and if not it will return 0. $Y = Row, $x = Colum, $iDirection = Direction
Local $aYX[2] = [$y, $x]
If StringMid($aCells[_GetCellPos($aiSize, $aYX)], $iDirection + 1, 1) = "0" Then
Switch $iDirection
Case 0 ; Up
$aYX[0] -= 1
Case 1 ; Right
$aYX[1] += 1
Case 2 ; Down
$aYX[0] += 1
Case 3 ; Left
$aYX[1] -= 1
EndSwitch
If ($aSolveVisitedCells[_GetCellPos($aiSize, $aYX)] <> True) Then Return 1 ; IF cell has not been seen before by the solver
EndIf
Return 0
EndFunc ;==>CanGo
Func AddToCurrentPath(ByRef Const $Cell, ByRef $iCurrentPathSubscriptNumber, ByRef $aCurrentPath)
$iCurrentPathSubscriptNumber += 1
$aCurrentPath[$iCurrentPathSubscriptNumber] = $Cell
EndFunc ;==>AddToCurrentPath
;=====================================
Func _GetCellPos(Const ByRef $aiSize, $vFind) ; This function will make a row and a colum into a Pos to be used with $aCells or $aCellstack, Or it will Make a Pos into an array with row and colum
If IsArray($vFind) Then
Local $CellPos = $vFind[0] * $aiSize[1] + $vFind[1]
Return $CellPos
Else
Local $aCellRowColum[2] ; Will be used in the _GetCellPos function to temp.. store an array
$aCellRowColum[0] = Int($vFind / $aiSize[1])
$aCellRowColum[1] = $vFind - ($aCellRowColum[0] * $aiSize[1])
Return $aCellRowColum
EndIf
EndFunc ;==>_GetCellPos
;=====================================
Func FlipDirection_br(ByRef $iDirection) ; Flips the direction
If $iDirection > 1 Then
$iDirection -= 2
Else
$iDirection += 2
EndIf
EndFunc ;==>FlipDirection_br
Func FlipDirection($iDirection) ; Flips the direction
If $iDirection > 1 Then Return $iDirection - 2
Return $iDirection + 2
EndFunc ;==>FlipDirection
;=====================================
Func array_filler(ByRef $aArray, $vFiller) ; Fills $aArray with $vFiller
For $i = 0 To UBound($aArray) - 1
$aArray[$i] = $vFiller
Next
EndFunc ;==>array_filler
;=====================================
Func RND_SeedTimed($iSeed = Default) ;; Set seed base on current timer value. Returns used seed value.
Local Const $INT31u = Int('0x0080000000') ;; (2^31), int64. (2147483648)
Local Const $INT32u = Int('0x0100000000') ;; (2^32), int64. (4294967296)
If Not IsNumber($iSeed) Then
$iSeed = Int(Mod(Int(TimerInit()), $INT32u) - ($INT31u), 1) ;; wraps to [-2^31 and 2^31-1]. (has value shift)
EndIf
SRandom($iSeed)
Return SetError(@error, @extended, $iSeed)
EndFunc ;==>RND_SeedTimed
;=====================================
Func Map($iValue, $iFromLow, $iFromHigh, $iToLow, $iToHigh)
Return ($iValue - $iFromLow) * ($iToHigh - $iToLow) / ($iFromHigh - $iFromLow) + $iTolow
EndFunc
;=============================================
Func _SaveAsPNG($BMPFile)
Local $hImage, $sCLSID
; Initialize GDI+ library
_GDIPlus_Startup()
; Load image
$hImage = _GDIPlus_ImageLoadFromFile($BMPFile)
; Get PNG encoder CLSID
$sCLSID = _GDIPlus_EncodersGetCLSID("PNG")
; Save image with rotation
_GDIPlus_ImageSaveToFileEx($hImage, StringReplace($BMPFile,".bmp",".png"), $sCLSID)
; Clean up resources
_GDIPlus_ImageDispose($hImage)
; Shut down GDI+ library
_GDIPlus_Shutdown()
EndFunc ;==>_SaveAsPNG
Is there simple method/property to distinguish the mazes?