Optimal multiplayer maze generation algorithm - algorithm

I'm working on a simple multiplayer game in which 2-4 players are placed at separate entrypoints in a maze and need to reach a goal point. Generating a maze in general is very easy, but in this case the goal of the game is to reach the goal before everyone else and I don't want the generation algorithm to drastically favor one player over others.
So I'm looking for a maze generation algorithm where the optimal path for each player from the startpoint to the goal is no more than 10% more steps than the average path. This way the players are on more or less an equal playing field. Can anyone think up such an algorithm?
(I've got one idea as it stands, but it's not well thought out and seems far less than optimal -- I'll post it as an answer.)

An alternative to freespace's answer would be to generate a random maze, then assign each cell a value representing the number of moves to reach the end of the maze (you can do both at once if you decide that you're starting at the 'end'). Then pick a distance (perhaps the highest one with n points at that distance?) and place the players at squares with that value.

What about first selecting the position of the players and goal and an equal length path and afterwards build a maze respecting the defined paths? If the paths do not intersect this should easily work, I presume

I would approach this by setting the goal and each player's entry point, then generating paths of similar length for each of them to the goal. Then I would start adding false branches along these paths, being careful to avoid linking to other player's paths, or having a branch connect back to the path. So essentially every branch is a dead end.
This way, you guarantee the paths are similar in length. However it won't allow players to interact with each other. You can however put this in, by creating links between branches such that branch entry points on either path are at a similar distance away from the goal. And on this branch you can branch off more dead ends for fun and profit :-)

The easiest solution I can come up with is to randomly generate an entire maze like normal, then randomly pick the goal point and player startpoints. Once this is done, calculate the shortest path from each startpoint to the goal. Find the average and start 'smoothing' (remove/move barriers -- don't know how this will work) the paths that are significantly above it, until all of the paths are within the proper margin. In addition, it could be possible to take the ones that are significantly below the average and insert additional barriers.

Pick your exit point somewhere in the middle
Start your N paths from there, adding 1 to each path per loop,
until they are as long as you want them to be.
There are your N start points, and they are all the same length.
Add additional branches off of the lines, until the maze is full.

Related

Does the removal of a few edges remove all paths to a node?

I'm making a game engine for a board game called Blockade and right now I'm trying to generate all legal moves in a position. The rules aren't exactly the same as the actual game and they don't really matter. The gist is: the board is a matrix and you move a pawn and place a wall every move.
In short, I have to find whether or not a valid path exists from every pawn to every goal after every potential legal move (imagine a pawn doesn't move and a wall is just placed), to rule out illegal moves. Or rather, if I simplify it to a subproblem, whether or not the removal of a few edges (placing a wall) removes all paths to a node.
Brute-forcing it would take O(k*n*m), where n and m are the board dimensions and k is the number of potential legal moves. Searching for a path (worst case; traversing most of the board) is very expensive, but I'm thinking with dynamic programming or some other idea/algorithm it can be done faster since the position is the same the wall placement just changes, or rather, in graph terms, the graph is the same which edges are removed is just changed. Any sort of optimization is welcome.
Edit:
To elaborate on the wall (blockade). A wall is two squares wide/tall (depending on whether it's horizontal or vertical) therefore it will usually remove at least four edges, eg:
p | r
q | t
In this 2x2 matrix, placing a wall in the middle (as shown) will remove jumping from and to:
p and t, q and r, p and r, and q and t
I apologize ahead of time if I don't fully understand your question as it is asked; there seems to be some tacit contextual knowledge you are hinting at in your question with respect to knowledge about how the blockade game works (which I am completely unfamiliar with.)
However, based on a quick scan on wikipedia about the rules of the game, and from what I gather from your question, my understanding is that you are effectively asking how to ensure that a move is legal. Based on what I understand, an illegal move is a wall/blockade placement that would make it impossible for any pawn to reach its goal state.
In this case, I believe a workable solution that would be fairly efficient would be as follows.
Define a path tree of a pawn to be a (possibly but not necessarily shortest) path tree from the pawn to each reachable position. The idea is, you want to maintain a path tree for every pawn so that it can be updated efficiently with every blockade placement. What is described in the previous sentence can be accomplished by observing and implementing the following:
when a blockade is placed it removes 2 edges from the graph, which can sever up to (at most) two edges in all your existing path trees
each pawn's path tree can be efficiently recomputed after edges are severed using the "adoption" algorithm of the Boykov-Komolgrov maxflow algorithm.
once every pawns path tree is recomputed efficiently, simply check that each pawn can still access its goal state, if not mark the move as illegal
repeat for each possible move (reseting graphs as needed during the search)
Here are resources on the adoption algorithm that is critical to doing what is described efficiently:
open-source implementation as part of the BK-maxflow: https://www.boost.org/doc/libs/1_66_0/libs/graph/doc/boykov_kolmogorov_max_flow.html
implementation by authors as part of BK-maxflow: https://pub.ist.ac.at/~vnk/software.html
detailed description of adoption (stage) algorithm of BK maxflow algorithm: section 3.2.3 of https://www.csd.uwo.ca/~yboykov/Papers/pami04.pdf
Note reading the description of the adopton algorithm included in the last
bullet point above would be most critical to understanding how to adopt
orphaned portions of your path-tree efficiently.
In terms of efficiency of this approach, I believe on average you should expect on average O(1) operations for each adopted edge, meaning this approach should take about O(k) time to compute where k is the number of board states which you wish to compute for.
Note, the pawn path tree should actually be a reverse directed tree rooted at the goal nodes, which will allow the computation to be done for all legal pawn placements given a blockade configuration.
A few suggestions:
To check if there's a path from A to B after ever
Every move removes a node from the graph/grid. So what we want to know is if there are critical nodes on the path from A to B (single points that could be blocked to break the path. This is a classic flow problem. For this application you want to set the vertex capacity to 1 and push 2 units of flow (basically just to verify that there are at least 2 paths). If there are 2 paths, no one block can disconnect you from the destination. You can optimize it a bit by using an implicit graph, but if you're new to this maybe create the graph to visualize it better. This should be O(N*M), the size of your grid.
Optimizations
Since this is a game, you know that the setup doesn't change dramatically from one step to another. So, you can keep track of the two paths. If the blocade is not placed on any of the paths, you can ignore it. You already have 2 paths to destination.
If the block does land on one of the paths, cancel only that path and then look for another (reusing the one you already have).
You can also speed up the pawn movement. This can be a bit trick, but what you want is to move the source. I'm assuming the pawn moves only a few cells at a time, maybe instead of finding completely new paths, you can simply adjust them to connect to the new position, speeding up the update.

shortest path to surround a target in a weighted 2d array

I'm having some trouble finding the right approach to coding this.
Take a random-generated 2d array, about 50x50 with each cell having a value 1~99.
Starting at a random position "Green", and the goal is to surround the target "Red" with the lowest amount of actions.
Moving to a neighboring cell takes 1~99 actions depending on it's value.
example small array with low values:
[
Currently the best idea i have is, generate 4 sets of checkpoints based on the diagonals of the target and then using a lot of Dijkstra's to find a path that goes through all of them, as well as the starting point.
One problem i have is this very quickly becomes an extreme numbers of paths.
FROM any starting point "NorthWest-1 to NW-20" TO any ending point in "NE-1 to NE-20", is 400 possibilities. Adding the 3rd and 4th diagonal to that becomes 400 * 20 * 20.
Another problem using diagonal checkpoints is that the problem is not [shortest path from green to a diagonal (orange path)]
[
but rather from "green to any point on the path around red".
Current pseudocode;
take 2 sets of diagonals nearest to Green/start
find the shortest path that connects those diagonals while going through Green
(backtracking through the path is free)
draw a line starting from the target point, in-between the 2 connected diagonals,
set those cells to value infinite to force going around them (and thus around the target)
find the shortest path connecting the now-seperated diagonals
Unfortunately this pseudocode already includes some edge cases where the 'wall' blocks the most efficient path.
If relevant, this will be written in javascript.
Edit, as an edge case it could spiral the target before surrounding, though extremely rare
Edit2; "Surround" means disconnect the target from the rest of the field, regardless of how large the surrounded area is, or even if it includes the starting point (eg, all edges are 0)
Here is another larger field with (probably) optimal path, and 2 fields in text-form:
https://i.imgur.com/yMA14sS.png
https://pastebin.com/raw/YD0AG6YD
For short, let us call paths that surround the target fences. A valid fence is a set of (connected) nodes that makes the target disconnected from the start, but does not include the target. A minimal fence is one that does so while having a minimal cost. A lasso could be a fence that includes a path to the start node. The goal is to build a minimal-cost lasso.
A simple algorithm would be to use the immediate neighborhood of the target as a fence, and run Dijkstra to any of those fence-nodes to build a (probably non-optimal) lasso. Note that, if optimal answers are required, the choice of fence actually influences the choice of path from the start to the fence -- and vice-versa, the choice of path from start to fence can influence how the fence itself is chosen. The problem cannot be split neatly into two parts.
I believe that the following algorithm will yield optimal fences:
Build a path using Dijkstra from start to target (not including the end-points). Let us call this the yellow path.
Build 2 sets of nodes, one on each side of this yellow path, and neighboring it. Call those sets red and blue. Note that, for any given node that neighbors the path, it can either be part of the path, blue set, red set, or is actually an end-point.
For each node in the red set, run Dijkstra to find the shortest path to a node in the blue set that does not cross the yellow path.
For each of those previous paths, see which is shortest after adding the (missing) yellow-path bit to connect the blue and red ends together.
The cost is length(yellowPath) * cost_of_Dijkstra(redStart, anyBlue)
To make a good lasso, it would be enough to run Dijkstra from the start to any fence node. However, I am unsure of whether the final lasso will be optimal or not.
You might want to consider the A* search algorithm instead, you can probably adjust the algorithm to search for all 4 spots at once.
https://en.wikipedia.org/wiki/A*_search_algorithm
Basically A* expands Dijkstra's algorithm by focusing it's search on spots that are "closer" to the destination.
There are a number of other variations for search algorithms that may be more useful for your situation as well in the "Also See" section, though some of them are more suited for video game path planning rather than 2D grid paths.
Edit after reviewing question again:
Seems each spot has a weight. This makes the distance calculation a bit less straightforward. In this case, I would treat it as an optimization. For the heuristic cost function, it may be best to just use the most direct path (diagonal) to the goal as the heuristic cost, and then just use A* search to try to find an even better path.
As for the surround logic. I would treat that as it's own logic and a separate step (likely the second step). Find least cost path to the target first. Then find the cheapest way to surround the path. Honestly, the cheapest way to surround a point is probably worth it's own question.
Once you have both parts, it should be easy enough to merge the two. There will be some point where the two first overlap and that is where they are merged together.

algorithm to generate random path in 2D tilemap

I need to generate a simple random path in 2D tile map. An input parameter is a number of steps. The condition is that each tile has just two adjacent tiles on the path so there are no rooms and no crossroads.
I was looking for some solution on the net and I didn't find anything like this. Drunkard algorithm makes rooms and everything else is maze generation algorithms. Maybe I am not searching by proper keywords.
The important thing is randomness as I need the path completely different every time.
Edit: adding sample image
sample path:
the main feature is that each tile has just 2 neighbors.
Improved version of this would be using specific target tile as the end of the path and minimum and maximum of steps but that's not so important right now.
Thank you for any idea.
create 2D map
so create 2D array of the size of your map and clear it by for example 0
add N random obstacles
for example filled circles in the map
use A* to find shortest path
you can use mine ... C++ A* example
If you want something more complex then you can create random terrain and use A* to find shortest path (while going up will cost more then going down ...). To create random terrain you can use:
Diamond and Square Island generator
which can be use also used for random map generation...
Break this down into sub parts, the only random decision you have to take is whether to go left/right/up/down at a given point on the path without forming a junction, this can be arbitrarily generated using lets say the time stamp of the machine,for example, to check weather the last digit is even or odd and then go left for even and right for odd, up for a modulo 4 etc, thus giving you a fairly random path every time.
Try and slow down the computation on a relatively fast to span this accross a lot of time,to introduce more randomness.
Alternatively, do a DFS like traversal on the 2D map, and store each unique path in a hash map or set, and add a unique number to this map as key, this is the pre processing part, now randomly pick a unique key from a set of all possible solution, remove it from the set, if you need another random unique path, just pick one more at random from the remaining available paths.
I'm creating a random map generator for a rogue-like dungeon crawler, and my approach for the entrance-to-exit path was this:
1. Randomly select a tile as the dungeon entrance. Add it to the path and set it as currentTile
2. Randomly pick a neighboring tile of currentTile that's not already in the path. Add it to the path and set it as currentTile.
3. Repeat step 2 until your path reaches the desired length. If a deadlock occurs, restart from 1.
In your case, you should alter step 2 a bit: you should check that the neighboring tile itself AND all its neighbors (except currentTile, of course) are not already in the path. This could cause your algorithm to reach a deadlock more times probably, but for paths as small as the one in your picture, a few repetitions would not be a problem.

Generating Random Puzzle Boards for Rush Hour Game

If you're not familiar with it, the game consists of a collection of cars of varying sizes, set either horizontally or vertically, on a NxM grid that has a single exit.
Each car can move forward/backward in the directions it's set in, as long as another car is not blocking it. You can never change the direction of a car.
There is one special car, usually it's the red one. It's set in the same row that the exit is in, and the objective of the game is to find a series of moves (a move - moving a car N steps back or forward) that will allow the red car to drive out of the maze.
I've been trying to think how to generate instances for this problem, generating levels of difficulty based on the minimum number to solve the board.
Any idea of an algorithm or a strategy to do that?
Thanks in advance!
The board given in the question has at most 4*4*4*5*5*3*5 = 24.000 possible configurations, given the placement of cars.
A graph with 24.000 nodes is not very large for todays computers. So a possible approach would be to
construct the graph of all positions (nodes are positions, edges are moves),
find the number of winning moves for all nodes (e.g. using Dijkstra) and
select a node with a large distance from the goal.
One possible approach would be creating it in reverse.
Generate a random board, that has the red car in the winning position.
Build the graph of all reachable positions.
Select a position that has the largest distance from every winning position.
The number of reachable positions is not that big (probably always below 100k), so (2) and (3) are feasible.
How to create harder instances through local search
It's possible that above approach will not yield hard instances, as most random instances don't give rise to a complex interlocking behavior of the cars.
You can do some local search, which requires
a way to generate other boards from an existing one
an evaluation/fitness function
(2) is simple, maybe use the length of the longest solution, see above. Though this is quite costly.
(1) requires some thought. Possible modifications are:
add a car somewhere
remove a car (I assume this will always make the board easier)
Those two are enough to reach all possible boards. But one might to add other ways, because of removing makes the board easier. Here are some ideas:
move a car perpendicularly to its driving direction
swap cars within the same lane (aaa..bb.) -> (bb..aaa.)
Hillclimbing/steepest ascend is probably bad because of the large branching factor. One can try to subsample the set of possible neighbouring boards, i.e., don't look at all but only at a few random ones.
I know this is ancient but I recently had to deal with a similar problem so maybe this could help.
Constructing instances by applying random operators from a terminal state (i.e., reverse) will not work well. This is due to the symmetry in the state space. On average you end up in a state that is too close to the terminal state.
Instead, what worked better was to generate initial states (by placing random cars on the grid) and then to try to solve it with some bounded heuristic search algorithm such as IDA* or branch and bound. If an instance cannot be solved under the bound, discard it.
Try to avoid A*. If you have your definition of what you mean is a "hard" instance (I find 16 moves to be pretty difficult) you can use A* with a pruning rule that prevents expansion of nodes x with g(x)+h(x)>T (T being your threshold (e.g., 16)).
Heuristics function - Since you don't have to be optimal when solving it, you can use any simple inadmissible heuristic such as number of obstacle squares to the goal. Alternatively, if you need a stronger heuristic function, you can implement a manhattan distance function by generating the entire set of winning states for the generated puzzle and then using the minimal distance from a current state to any of the terminal state.

Dynamic maze mutation

I have an idea of creating yet another maze game. However, there is a key difference: maze changes on-the-fly during the game. When I think of the problem the following restrictions come into my mind:
there is main route in the maze which never changes
the main route is the only route which leads to the finish
maze mutation should not block paths back to the main route
It also would be nice to control (affect game difficulty):
how much of the maze gets changed during a single mutation
optionally disable restriction #3 (i.e. player can get blocked in the maze for a while)
EDIT:
The question is: can you suggest an algorithm (or give your ideas) for described maze generation/mutation, which will not violate given restrictions?
You could:
Block a path at random (or using some sneaky criteria).
Scan the maze to see if it has been partitioned into 2 regions that are no longer connected.
If disconnected, you can knock down a wall at random so long as it neighbors both regions.
If your maze has only one path between any two points, step 2 will always split the maze and so #3 will always be needed.
Make a graph connecting all the cells of the maze and the walkable connections between them. To modify the maze, first pick a random wall to knock down, which generates a new edge in the graph. Then find a cycle in the graph that contains that edge, and delete a random, non-main-path edge in that cycle, which will erect an edge somewhere else.
This algorithm ensures that if all cells were reachable at the start, they will remain so. You probably want that feature so you can't ever get trapped.
This is probably quite straightforward. Generate the maze using the standard depth-first-search algorithm. Store the list of cells that form the (only) path from start to exit in a list. When you decide you want to mutate the maze, do the following:
Reset the entire maze to the default state (all walls in place), with the exception of any cell along the critical path, and optionally, a few cells within line-of-sight of the player's current location.
Re-execute the breadth-first search algorithm from the start, with one modification: when choosing which unvisited neighbour to explore, prefer edges that already have the wall removed.
The modification in the second step will ensure that the algorithm first explores the existing paths, then adds on side-passages and so forth from there. It's not even strictly necessary to preserve the critical path if you don't want to - you can regenerate the entire maze except where the user's standing, and it'll remain valid.
I think this ought to always produce a valid tree in the same way the original algorithm would, but I'm not 100% sure about the implications of preserving the cells around the user, which may not be on the critical path. I'm positive the reconfigured maze will always be solvable from where the user is standing, though.
This is a pretty neat idea, too. I love the idea of the maze rearranging itself substantially wherever the user isn't looking. If you're doing this in first-person, you could even use the camera view to change the walls behind the user when they're not looking!

Resources