_TITLE "PathFinder 4, No diagonal movements" 'QB64 X 64 version 1.2 20180228/86 from git b301f92
' version 4: Pathfinder or Path Maker!
' ================================ Instructions ==============================================
' ctrl + Arrow will haul a green obstacle
' shift + Arrow will push a green obstacle as long as there is a space on the other side of it.
' With a new map the destination for paths is set the same as the blue door, see target.
' The destination can be reset by clicking the map.
' The blue square is the exit or door to another map, so is pressing n for new.
' A yellow path will appear between the white hero player and the destination if the way is not
' blocked. If it is, you can usually clear a path to the door by moving obstacles around.
' When you clear a path the yellow path will light up. esc to quit.
' Oh yeah, you can haul the door around too.
'
' It is strangely fun to move the green obstacles around, like sculpting with a bull dozer.
' I am thinking maybe some sort of landscaping game.
'=============================================================================================
' History of development:
' started 2018-08-11 when Colbalt asked about A* pathfinder
' He has now 2018-08-12 posted a QB64 version that is nice!
' 2018-08-11 started PathFinder 1
' 2018-08-12 almost working but buggy backtract to point A after point B is found.
' 2018-08-13 PathFinder 1a.bas I think I have a fix for buggy path BackTrack but major surgery so this new version
' 2018-08-14 Pathfinder 2: 2 parts
' Part 1: creates a map, a random Home point and backtracts all the points available to move over.
' Part 2: allows clicking variaous points of map to see if path is found.
' 2018-08-15 PathFinder 3: Make a function that returns a path from A to B given A, B
' The map is shared as well as it's dimensions mapw by maph (constants).
' The function will create a copy of map and work with that so main map remains intact.
' Well I suppose we need aa random map maker for starters.
' And then of course a sub to display the map.
' OK I am now working under the influence of Steve McNeil's programs. ;)
' Thanks Steve some great ideas for simplfying the PathFinder for actual game use.
' 2018-08-17 With Pathfinder 3 I want to start working code towards game using MAP(s)
' I am also under the influence of PMACKAY's game series Mr Bash or Dash or whatever he decides to call the Diamond Miner.
' Thanks PMACKAY, I love the little game you started and have learned from your questions and setup.
'2018-08-18 Pathfinder 3a, I have 3 working with ticks but it changes all after running through the whole tick session.
' 3a tests whether I can do this with Steve's version which changes immediately and not after whole tick session.
' It is allot simpler and will run faster than saving all changes in a container and reprocessing into step array.
' I tried this before but I think I know what I did wrong then, I did not check if value was = current tick.
' Before I think I just checked if the map had a value or not written in, no only current tick values should parent a new cell.
' Finally found the frick'in bug!!! had cy instead of cx, OK let's see if can draw the path from Steps array.
' OK now prepStepMap in called in the Path routine and working.
'2018-08-19 PathFinder 4 add the needed Push Mode to compliment the Haul mode
'2018-08-23 PathFinder 4 DO PATHS USING ONLY VERTCAL AND HORIZONTAL MOVEMENT
' Also use _keydown for push / haul modes, forget having to toggle modes On and Off.
' Use left hand Ctrl+ Arrow to Haul and Shift + Arrow to Push, when both down, neither work.
' Extracted Hero from being emedded in the map.
' Draw the Hero tile on a second layer over the floor = spacer tile.
'
' Decided to use integer values with Descriptive CONST names, thanks Tempodibasic
' Using ALL CAP's for CONSTants, user defined TYPE, and SHARED variables,
' but short mnuemonic abrev because I hate typing long descriptions,
' so if you forget what the cap'd variable name means, check back to this main section.
CONST WW
= 1060 ' Window Width in pixels CONST WH
= 720 ' Window Height in pixels CONST SQ
= 30 ' Square Size in pixels CONST MAPW
= 30 ' MAP number of squares Wide or Width CONST MAPH
= 20 ' MAP number of squares High or Height
' MAP ITEMS
' CONST HERO = -1 no longer embedded in map
'following Steve's example
'The MAP:
'There will be a 1 SQ Border around the Map, the actual squares for game action run 1 to MAPW, 1 to MAPH
DIM SHARED HDC
AS COORDINATE
'Hero Destination Coordinate
'This will be a map used for getting paths from HC = Hero Coordinate to HDC = Hero Destination Coordinate
XOFF = (WW - SQ * (MAPW + 2)) / 2
YOFF = (WH - SQ * (MAPH + 2)) / 2
HAULMODE = 0: PUSHMODE = 0
'this part sets up a sample map, and here the door is created
RandomMap .7
'set door as first destination for pathing
HDC.X = MX.X: HDC.Y = MX.Y
'find free space to start hero
testx = Rand(1, MAPW): testy = Rand(1, MAPH)
HC.X = testx: HC.Y = testy
'this part displays the ability to find a path to blue square anywhere in the maze
_TITLE "ctrl+Arrow <haul, shift+Arrow >push, Click to set destination, n or blue door for new map, esc=quit"
t.X = xm / SQ: t.Y = ym / SQ
HDC.X = t.X: HDC.Y = t.Y
IF PUSHMODE
AND HAULMODE
THEN PUSHMODE
= FALSE: HAULMODE
= FALSE
IF MAP
(HC.X
, HC.Y
- 1) = SPACER
THEN IF HAULMODE
AND MAP
(HC.X
, HC.Y
+ 1) < -2 THEN MAP(HC.X, HC.Y) = MAP(HC.X, HC.Y + 1)
MAP(HC.X, HC.Y + 1) = SPACER
HC.Y = HC.Y - 1
HC.Y = HC.Y - 1
ELSEIF MAP
(HC.X
, HC.Y
- 2) = SPACER
AND MAP
(HC.X
, HC.Y
- 1) < -2 AND PUSHMODE
THEN MAP(HC.X, HC.Y - 2) = MAP(HC.X, HC.Y - 1)
MAP(HC.X, HC.Y - 1) = MAP(HC.X, HC.Y)
HC.Y = HC.Y - 1
IF MAP
(HC.X
, HC.Y
+ 1) = SPACER
THEN IF HAULMODE
= TRUE
AND MAP
(HC.X
, HC.Y
- 1) < -2 THEN MAP(HC.X, HC.Y) = MAP(HC.X, HC.Y - 1)
MAP(HC.X, HC.Y - 1) = SPACER
HC.Y = HC.Y + 1
HC.Y = HC.Y + 1
ELSEIF MAP
(HC.X
, HC.Y
+ 2) = SPACER
AND MAP
(HC.X
, HC.Y
+ 1) < -2 AND PUSHMODE
THEN MAP(HC.X, HC.Y + 2) = MAP(HC.X, HC.Y + 1)
MAP(HC.X, HC.Y + 1) = MAP(HC.X, HC.Y)
HC.Y = HC.Y + 1
IF MAP
(HC.X
- 1, HC.Y
) = SPACER
THEN IF HAULMODE
AND MAP
(HC.X
+ 1, HC.Y
) < -2 THEN MAP(HC.X, HC.Y) = MAP(HC.X + 1, HC.Y)
MAP(HC.X + 1, HC.Y) = SPACER
HC.X = HC.X - 1
HC.X = HC.X - 1
ELSEIF MAP
(HC.X
- 2, HC.Y
) = SPACER
AND MAP
(HC.X
- 1, HC.Y
) < -2 AND PUSHMODE
THEN MAP(HC.X - 2, HC.Y) = MAP(HC.X - 1, HC.Y)
MAP(HC.X - 1, HC.Y) = SPACER
HC.X = HC.X - 1
IF MAP
(HC.X
+ 1, HC.Y
) = SPACER
THEN IF HAULMODE
= TRUE
AND MAP
(HC.X
- 1, HC.Y
) < -2 THEN MAP(HC.X, HC.Y) = MAP(HC.X - 1, HC.Y)
MAP(HC.X - 1, HC.Y) = SPACER
HC.X = HC.X + 1
HC.X = HC.X + 1
ELSEIF MAP
(HC.X
+ 2, HC.Y
) = SPACER
AND MAP
(HC.X
+ 1, HC.Y
) < -2 AND PUSHMODE
THEN MAP(HC.X + 2, HC.Y) = MAP(HC.X + 1, HC.Y)
MAP(HC.X + 1, HC.Y) = MAP(HC.X, HC.Y)
HC.X = HC.X + 1
displayMap
SUB path
(ptB
AS COORDINATE
) 'path is from hero (DIM SHARED HC as COORDINATE) to ptB (DIM SHARED HDC
prepStepMap ptB
dist = STEPMAP(HC.X, HC.Y) 'STEPMAP is DIM SHARED as INTEGER
STEPSI = 0 'DIM SHARED, no path is signaled if still 0 after this sub
cx = HC.X: cy = HC.Y
'count dist down to destination
cf = 0
IF STEPMAP
(cx
, cy
- 1) = dist
- 1 THEN cf = 1: cy = cy - 1
cf = 1: cx = cx - 1
cf = 1: cx = cx + 1
cf = 1: cy = cy + 1
IF cf
= 0 THEN 'lost path, this should not happen until it is done 'add next step to steps array, set next search target
STEPSI = STEPSI + 1: STEPS(STEPSI).X = cx: STEPS(STEPSI).Y = cy
dist = dist - 1
'this is Steve McNeil's method optimised to skip over needless or redundant checks
SUB prepStepMap
(target
AS COORDINATE
) STEPMAP(x, y) = 0
STEPMAP(target.X, target.Y) = 1: tick = 1: changes = 1
'from an ever broadening area around destination find neighbor to step from
t = tick: tick = tick + 1: changes = 0
ystart = max(target.Y - tick, 1): ystop = min(target.Y + tick, MAPH)
xstart = max(target.X - tick, 1): xstop = min(target.X + tick, MAPW)
'check out the neighbors
IF STEPMAP
(x
, y
- 1) = t
OR STEPMAP
(x
- 1, y
) = t
OR STEPMAP
(x
+ 1, y
) = t
OR STEPMAP
(x
, y
+ 1) = t
THEN 'there is a step close by to step from
STEPMAP(x, y) = tick: changes = 1 'next step
SUB displayMap
'all drawing instructions conatined here 'MAP is shared, 1 based with width = mapw, height = maph that are constants
'CASE HERO: k = _RGB32(255, 255, 255) 'not embedded in map anymore
LINE (x
* SQ
+ XOFF
, y
* SQ
+ YOFF
)-STEP(SQ
- 2, SQ
- 2), k
, BF
'loads steps array with steps from hero to destination from prepared stepmap array,
'stepsI is number of steps to destination unless = 0
path HDC
LINE (STEPS
(s
).X
* SQ
+ XOFF
+ 10, STEPS
(s
).Y
* SQ
+ YOFF
+ 10)-STEP(SQ
- 20, SQ
- 20), _RGB32(255, 255, 0), BF
'draw target or destination
LINE (HDC.X
* SQ
+ XOFF
+ i
- 1, HDC.Y
* SQ
+ YOFF
+ i
- 1)-STEP(SQ
- 2 * i
, SQ
- 2 * i
), _RGB32(255, 255, 255), BF
LINE (HDC.X
* SQ
+ XOFF
+ i
, HDC.Y
* SQ
+ YOFF
+ i
)-STEP(SQ
- 2 * i
- 2, SQ
- 2 * i
- 2), _RGB32(255, 0, 0), BF
'draw hero
LINE (HC.X
* SQ
+ XOFF
+ 1, HC.Y
* SQ
+ YOFF
+ 1)-STEP(SQ
- 4, SQ
- 4), _RGB32(255, 255, 255), BF
'special keys = powers
'someone might start with this a build a map or levels editor!
'load a shared MAP(1 to mapw, 1 to maph)
SUB RandomMap
(obstacleDensity!
) ' obstacleDensity! = fraction of map squares to make obstacles 'MAP is shared, 1 based with width = mapw, height = maph that are constants
'clear last map
MAP(x, y) = SPACER
'borders
MAP(x, 0) = BORDER
MAP(x, MAPH + 1) = BORDER
MAP(0, y) = BORDER
MAP(MAPW + 1, y) = BORDER
'convert this part into walls, buildings, jewels, potions...
'with these obstacles there is no guarantee a path will exist
FOR I
= 1 TO MAPW
* MAPH
* obstacleDensity!
ox = Rand(1, MAPW): oy = Rand(1, MAPH)
MAP(ox, oy) = OBSTACLE
'door, exit to next map near a border?
wall = Rand(1, 4)
CASE 1: MX.X
= 1: MX.Y
= Rand
(2, MAPH
- 2) CASE 2: MX.X
= MAPW: MX.Y
= Rand
(2, MAPH
- 2) CASE 3: MX.X
= Rand
(2, MAPW
- 2): MX.Y
= 1 CASE 4: MX.X
= Rand
(2, MAPW
- 2): MX.Y
= MAPH
MAP(MX.X, MX.Y) = DOOR
'handy functions
Rand%
= INT(RND * (hi%
- lo%
+ 1)) + lo%