Moin,
Ich wusste nicht so recht, ob der Thread in H&U oder in Skripte gehört, denn eigentlich habe ich eine Frage
Da es aber schon ein funktionsfähiges (aber ziemlich primitives) Skript gibt habe ich mich für Skripte entschieden.
"Geschichte von Adam und Eva"
Da ich irgendwann mal wieder ein TD basteln möchte was ggf mehr kann als meine bisherigen brauche ich natürlich erstmal eine ganze Menge Funktionen die mir das ermöglichen. Eine davon ist die Wegfindungsfunktion. Sie soll im 2D Raum den kürzesten Weg vom Start zum Ziel finden. Diagonales Laufen ist nicht möglich. Andys A* Algo habe ich mir schon angesehen, aber nicht gänzlich verstanden (Er beinhaltet auch Diagonales gehen). Zudem möchte ich nicht mit Kanonen auf Spatzen schießen. Es handelt sich um ein kleines Spielfeld (im Endeffekt wird es zwischen 12x12 und 16x16 Felder groß, ggf auch nicht gleichseitig), daher wird kein Algo benötigt der unnötig komplex ist um bei großen Wegen Zeit zu sparen, der aber bei kleinen Wegen unnötigen Overhead mitführt.
Das Skript anbei findet schon den Weg in akzeptabeler Geschwindigkeit. Ich bin mir aber nicht sicher, ob mein Ansatz (annähernd) optimal ist. Da zusätzlich noch Rücksicht auf Hindernisse genommen werden muss, was ich noch nicht vollständig implementiert habe möchte ich vor dem Weiterbasteln wissen, ob es villeicht einen effizienteren Ansatz zum Ziel gibt. Dann würde ich mich diesem widmen und mein bisheriges Skript als kleine Übung abtun. Das Skript sollte leicht verständlich sein, sodass Schwächen schnell erkannt werden können. Daher bitte ich um eure Meinung
Hier mein kleiner Test:
Skript
#include <Array.au3>
#include <GDIPlus.au3>
Global $__aArea[1][1], $__aAreaMap[1][1], $__aAreaUx, $__aAreaUy, $__aAreaStart[2], $__aAreaZiel[2], $__aMap = $__aArea
[/autoit] [autoit][/autoit] [autoit]; Nur zum Testen
Global $aLabel[1][1]
_Test()
[/autoit] [autoit][/autoit] [autoit]Func _Test()
_GDIPlus_Startup()
Local $ix = 25, $iy = 25, $hBtn_Neu, $aStart[2], $aZiel[2], $t, $p, $aPosTest[2], $aBlock[2], $iMsg, $iTimer, $iTime, $iVersuche
Local $hGUI = GUICreate('Test', 400, 445), $hGFX = _GDIPlus_GraphicsCreateFromHWND($hGUI), $hBRU = _GDIPlus_BrushCreateSolid()
ReDim $aLabel[$iy][$ix]
_Area_SetSize($ix, $iy)
$hBtn_Neu = GUICtrlCreateButton('Neuer Weg', 10, 410, 100, 25)
GUISetState()
$iTimer = TimerInit()
While True
$iMsg = GUIGetMsg()
If TimerDiff($iTimer) > 1500 Then
$iMsg = $hBtn_Neu
$iTimer = TimerInit()
EndIf
Switch $iMsg
Case -3
ExitLoop
Case $hBtn_Neu
_Area_Reset()
_Area_SetSize($ix, $iy)
$aStart[0] = $ix - 1
$aZiel[0] = 0
$aStart[1] = $iy - 1
$aZiel[1] = 0
_Area_SetPath($aStart, $aZiel)
ConsoleWrite('Start: [' & $__aAreaStart[0] & '|' & $__aAreaStart[1] & ']')
ConsoleWrite(' - Ziel: [' & $__aAreaZiel[0] & '|' & $__aAreaZiel[1] & ']' & @CRLF)
For $i = 0 To Int(($ix ^ 2 + $iy ^ 2) ^ 0.5 / 2) Step 1
$aBlock[0] = Random(0, $ix - 1, 1)
$aBlock[1] = Random(0, $iy - 1, 1)
If ($aBlock[0] = $__aAreaStart[0] And $aBlock[1] = $__aAreaStart[1]) Or ($aBlock[0] = $__aAreaZiel[0] And $aBlock[1] = $__aAreaZiel[1]) Then
ContinueLoop
Else
_Area_SetTitle($aBlock[0], $aBlock[1], 1)
_Area_SetTitle($aBlock[0]+1, $aBlock[1], 1)
_Area_SetTitle($aBlock[0], $aBlock[1]+1, 1)
_Area_SetTitle($aBlock[0]+1, $aBlock[1]+1, 1)
_Area_SetTitle($aBlock[0]+2, $aBlock[1], 1)
_Area_SetTitle($aBlock[0], $aBlock[1]+2, 1)
_Area_SetTitle($aBlock[0]+2, $aBlock[1]+1, 1)
_Area_SetTitle($aBlock[0]+1, $aBlock[1]+2, 1)
_Area_SetTitle($aBlock[0]+2, $aBlock[1]+2, 1)
EndIf
Next
For $i = 0 To Int(($ix ^ 2 + $iy ^ 2) ^ 0.5 / 2) Step 1
$aBlock[0] = Random(0, $ix - 1, 1)
$aBlock[1] = Random(0, $iy - 1, 1)
If ($aBlock[0] = $__aAreaStart[0] And $aBlock[1] = $__aAreaStart[1]) Or ($aBlock[0] = $__aAreaZiel[0] And $aBlock[1] = $__aAreaZiel[1]) Then
ContinueLoop
Else
_Area_SetTitle($aBlock[0], $aBlock[1], 1)
_Area_SetTitle($aBlock[0]+1, $aBlock[1], 1)
_Area_SetTitle($aBlock[0], $aBlock[1]+1, 1)
_Area_SetTitle($aBlock[0]+1, $aBlock[1]+1, 1)
EndIf
Next
For $i = 0 To Int(($ix ^ 2 + $iy ^ 2) ^ 0.5 / 2) Step 1
$aBlock[0] = Random(0, $ix - 1, 1)
$aBlock[1] = Random(0, $iy - 1, 1)
If ($aBlock[0] = $__aAreaStart[0] And $aBlock[1] = $__aAreaStart[1]) Or ($aBlock[0] = $__aAreaZiel[0] And $aBlock[1] = $__aAreaZiel[1]) Then
ContinueLoop
Else
_Area_SetTitle($aBlock[0], $aBlock[1], 1)
EndIf
Next
For $i = 0 To $ix - 1 Step 1
For $e = 0 To $iy - 1 Step 1
If $__aArea[$e][$i] Then
DllCall($ghGDIPDll, "int", "GdipSetSolidFillColor", "handle", $hBRU, "dword", 0xFF404040)
Else
If $i = $aStart[0] And $e = $aStart[1] Then
DllCall($ghGDIPDll, "int", "GdipSetSolidFillColor", "handle", $hBRU, "dword", 0xFF4040DD)
ElseIf $i = $aZiel[0] And $e = $aZiel[1] Then
DllCall($ghGDIPDll, "int", "GdipSetSolidFillColor", "handle", $hBRU, "dword", 0xFFDD4040)
Else
DllCall($ghGDIPDll, "int", "GdipSetSolidFillColor", "handle", $hBRU, "dword", 0xFFD0D0D0)
EndIf
EndIf
DllCall($ghGDIPDll, "int", "GdipFillRectangleI", "handle", $hGFX, "handle", $hBRU, "int", $i * (400 / $ix), "int", $e * (400 / $iy), "int", (400 / $ix), "int", (400 / $iy))
Next
Next
_Area_Calc()
$t = TimerInit()
$p = _Area_FindPath()
$t = TimerDiff($t)
ConsoleWrite('Pfad: ' & $p & ' (' & Round($t, 2) & 'ms)' & @CRLF)
If Int(StringLeft($p, 3)) > 0 Then
$iTime += $t
$iVersuche += 1
ToolTip('Dauer: ' & Round($iTime / $iVersuche, 2) & 'ms')
EndIf
$aPosTest = $__aAreaStart
$p = StringSplit($p, '', 2)
For $i = 0 To UBound($p) - 2 Step 1
Switch $p[$i]
Case 1 ; Links
$aPosTest[0] -= 1
Case 2 ; Oben
$aPosTest[1] -= 1
Case 3 ; Rechts
$aPosTest[0] += 1
Case 4 ; Unten
$aPosTest[1] += 1
EndSwitch
DllCall($ghGDIPDll, "int", "GdipSetSolidFillColor", "handle", $hBRU, "dword", 0xFF154015 + Int('0xFF' & Hex(Int(($i*0.75)^0.8), 2) & Hex(Int(($i*2)^0.83), 2) & Hex(Int(($i*0.75)^0.8), 2)))
DllCall($ghGDIPDll, "int", "GdipFillRectangleI", "handle", $hGFX, "handle", $hBRU, "int", $aPosTest[0] * (400 / $ix), "int", $aPosTest[1] * (400 / $iy), "int", (400 / $ix), "int", (400 / $iy))
Next
EndSwitch
WEnd
_GDIPlus_BrushDispose($hBRU)
_GDIPlus_GraphicsDispose($hGFX)
_GDIPlus_Shutdown()
EndFunc ;==>_Test
Func _Area_FindPath()
$__aMap = $__aArea
Return __Area_FindPathRec()
EndFunc
Func __Area_FindPathRec($aPos = $__aAreaStart, $aPosLast = $__aAreaStart)
Local $aList = __Area_GetRichtung($aPos, $aPosLast), $tPos[2], $sTemp, $iSum = 1*($aList[0][1] > 0) + 1*($aList[1][1] > 0) + 1*($aList[2][1] > 0) + 1*($aList[3][1] > 0)
If $iSum < 2 Then
$__aMap[$aPos[1]][$aPos[0]] += 1
If $iSum = 0 Then Return -1
EndIf
__Area_SortArray2D($aList)
For $i = 0 To UBound($aList) - 1 Step 1
If Not $aList[$i][1] Then ContinueLoop
$tPos = $aPos
Switch $aList[$i][0]
Case 1 ; Links
$tPos[0] -= 1
Case 2 ; Oben
$tPos[1] -= 1
Case 3 ; Rechts
$tPos[0] += 1
Case 4 ; Unten
$tPos[1] += 1
EndSwitch
If $tPos[0] = $__aAreaZiel[0] And $tPos[1] = $__aAreaZiel[1] Then Return $aList[$i][0]
$aPosLast = $aPos
$sTemp = __Area_FindPathRec($tPos, $aPosLast)
If $sTemp > 0 Then Return $aList[$i][0] & $sTemp
Next
EndFunc ;==>_Area_FindPath
Func __Area_SortArray2D(ByRef $a)
Local $c[2], $t[2]
For $i = 0 To UBound($a) - 1 Step 1
$t[0] = $a[$i][0]
$t[1] = $a[$i][1]
For $e = $i - 1 To 0 Step -1
$c[0] = $a[$e][0]
$c[1] = $a[$e][1]
If $t[1] >= $c[1] Then ExitLoop
$a[$e + 1][0] = $c[0]
$a[$e + 1][1] = $c[1]
Next
$a[$e + 1][0] = $t[0]
$a[$e + 1][1] = $t[1]
Next
EndFunc ;==>__Area_SortArray2D
Func __Area_GetRichtung($aPos, $aPosLast)
Local $a[4][2] = [[1],[2],[3],[4]] ; LORU
If ($aPos[0] > 0) And Not $__aMap[$aPos[1]][$aPos[0] - 1] And Not ((($aPos[0] - 1) = $aPosLast[0]) And ($aPos[1] = $aPosLast[1])) Then $a[0][1] = $__aAreaMap[$aPos[1]][$aPos[0] - 1]
If ($aPos[1] > 0) And Not $__aMap[$aPos[1] - 1][$aPos[0]] And Not (($aPos[0] = $aPosLast[0]) And (($aPos[1] - 1) = $aPosLast[1])) Then $a[1][1] = $__aAreaMap[$aPos[1] - 1][$aPos[0]]
If ($aPos[0] < $__aAreaUx - 1) And Not $__aMap[$aPos[1]][$aPos[0] + 1] And Not ((($aPos[0] + 1) = $aPosLast[0]) And ($aPos[1] = $aPosLast[1])) Then $a[2][1] = $__aAreaMap[$aPos[1]][$aPos[0] + 1]
If ($aPos[1] < $__aAreaUy - 1) And Not $__aMap[$aPos[1] + 1][$aPos[0]] And Not (($aPos[0] = $aPosLast[0]) And (($aPos[1] + 1) = $aPosLast[1])) Then $a[3][1] = $__aAreaMap[$aPos[1] + 1][$aPos[0]]
Return $a
EndFunc ;==>__Area_GetRichtung
Func _Area_Calc()
If Not ($__aAreaStart[0] < $__aAreaUx And $__aAreaStart[1] < $__aAreaUy And $__aAreaZiel[0] < $__aAreaUx And $__aAreaZiel[1] < $__aAreaUy) Then Return -1
For $x = 0 To $__aAreaUx - 1 Step 1
For $y = 0 To $__aAreaUy - 1 Step 1
$__aAreaMap[$y][$x] = (($__aAreaZiel[0] - $x) ^ 2 + ($__aAreaZiel[1] - $y) ^ 2) ^ 0.5 + 1
Next
Next
EndFunc ;==>_Area_Calc
Func _Area_SetPath($aStart, $aZiel)
$__aAreaStart = $aStart
$__aAreaZiel = $aZiel
EndFunc ;==>_Area_SetPath
Func _Area_SetTitle($x, $y, $bBlocked)
If $x < $__aAreaUx And $y < $__aAreaUy Then $__aArea[$y][$x] = $bBlocked
EndFunc ;==>_Area_SetTitle
Func _Area_Reset()
ReDim $__aArea[1][1]
$__aArea[0][0] = ''
$__aAreaMap = $__aArea
$__aAreaUx = 1
$__aAreaUy = 1
$__aAreaStart[0] = ''
$__aAreaStart[1] = ''
$__aAreaZiel[0] = ''
$__aAreaZiel[1] = ''
EndFunc ;==>_Area_Reset
Func _Area_SetSize($x, $y)
_Area_Reset()
ReDim $__aArea[$y][$x]
$__aAreaMap = $__aArea
$__aAreaUx = $x
$__aAreaUy = $y
EndFunc ;==>_Area_SetSize
Edit: Hier mal das was ich bisher habe. Nach meiner Überprüfung findet die Funktion immer einen Weg, sofern einer existiert. (aber nur selten den optimalen, was aber nicht weiter schlimm ist). Wie kann man verifizieren, dass dies tatsächlich der Fall ist ? Habe hunderte Male den Weg in einer Zufallslandschaft finden lassen, und beim Fehlschlag immer visuell festgestellt, dass kein Weg möglich war. Leider sind 100 Beispiele kein Beweis.
lg
Mars