I'v got a table (2d array), c x r. Need to generate a random pattern of connected cells inside of it. No self-crossings and no diagonal-moves. See related picture for example. ex. 1 с = 6, r = 7, the pattern is shown in numbers.
I'w wrote a function for this and it works fine, but I'm looking for hard optimization. In the code below you can see that if the pattern gets into a dead end it just rebuilds itself from the start. That is very inefficient if the pattern length is close or equals to the number of cells, c*r (42 in the example). So some smart solution is needed for this, like moving the whole pattern symmetrically when it runs out of possible moves or to add some analytics to the function so it never cathes up in the dead ends. Again, for the low values of c, r and patternLength my example works fine, but I'm looking for algorithmic perfection and high performance even on pretty high numbers.
function ClassLogic:generatePattern()
--[[ subfunctions ]]
--choosing next point for the pattern
local move = function( seq )
--getting the last sequence point
local last = seq[#seq]
-- checking the nearness of walls
local
wallLeft,
wallRight,
wallUp,
wallDown =
(last.c==1),
(last.c==config.tableSize.c),
(last.r==1),
(last.r==config.tableSize.r)
-- checking the nearness of already sequenced points
local
spLeft,
spRight,
spUp,
spDown =
(utilities.indexOfTable( seq, { c = last.c - 1, r = last.r } )~=-1),
(utilities.indexOfTable( seq, { c = last.c + 1, r = last.r } )~=-1),
(utilities.indexOfTable( seq, { c = last.c, r = last.r - 1 } )~=-1),
(utilities.indexOfTable( seq, { c = last.c, r = last.r + 1 } )~=-1)
local leftRestricted = (wallLeft or spLeft)
local rightRestricted = (wallRight or spRight)
local upRestricted = (wallUp or spUp)
local downRestricted = (wallDown or spDown)
if ( leftRestricted and rightRestricted and upRestricted and downRestricted ) then
-- dead end
print('d/e')
return nil
else
-- go somewhere possible
local possibleDirections = {}
if (not leftRestricted) then possibleDirections[#possibleDirections+1] = 1 end
if (not rightRestricted) then possibleDirections[#possibleDirections+1] = 2 end
if (not upRestricted) then possibleDirections[#possibleDirections+1] = 3 end
if (not downRestricted) then possibleDirections[#possibleDirections+1] = 4 end
local direction = possibleDirections[math.random( 1, #possibleDirections )]
if (direction==1) then
--next point is left
return { c = last.c - 1, r = last.r }
elseif (direction==2) then
--next point is right
return { c = last.c + 1, r = last.r }
elseif (direction==3) then
--next point is up
return { c = last.c, r = last.r - 1 }
elseif (direction==4) then
--next point is down
return { c = last.c, r = last.r + 1 }
end
end
end
--[[ subfunctions end ]]
-- choose random entry point
local entry = { c = math.random( 1, config.tableSize.c ),
r = math.random( 1, config.tableSize.r ) }
-- start points sequence
local pointSequence = { [1] = entry }
-- building the pattern
local succeed = false
while (not succeed) do
for i = 2, self.patternLength do
local nextPoint = move( pointSequence )
if (nextPoint~=nil) then
pointSequence[i] = nextPoint
if (i==self.patternLength) then succeed = true end
else
pointSequence = { [1] = entry }
break
end
end
end
return pointSequence
end
Any ideas or approaches on how this could be realized would be highly appreciated. Maybe some recursive backtracker or a pathfinding or a random-walk algorithms?