Simple recursivity and A* search – Laurent Luce's Blog (2024)

This post describes how to solve mazes using 2 algorithms implemented in Python: a simple recursive algorithm and the A* search algorithm.

Maze

The maze we are going to use in this article is 6 cells by 6 cells. The walls are colored in blue. The starting cell is at the bottom left (x=0 and y=0) colored in green. The ending cell is at the top right (x=5 and y=5) colored in green. We can only move horizontally or vertically 1 cell at a time.

Simple recursivity and A* search – Laurent Luce's Blog (1)

Recursive walk

We use a nested list of integers to represent the maze. The values are the following:

  • 0: empty cell
  • 1: unreachable cell: e.g. wall
  • 2: ending cell
  • 3: visited cell
grid = [[0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0], [0, 1, 1, 0, 0, 1], [0, 1, 0, 0, 1, 0], [0, 1, 0, 0, 0, 2]]

This is a very simple algorithm which does the job even if it is not an efficient algorithm. It walks the maze recursively by visiting each cell and avoiding walls and already visited cells.

The search function accepts the coordinates of a cell to explore. If it is the ending cell, it returns True. If it is a wall or an already visited cell, it returns False. The neighboring cells are explored recursively and if nothing is found at the end, it returns False so it backtracks to explore new paths. We start at cell x=0 and y=0.

def search(x, y): if grid[x][y] == 2: print 'found at %d,%d' % (x, y) return True elif grid[x][y] == 1: print 'wall at %d,%d' % (x, y) return False elif grid[x][y] == 3: print 'visited at %d,%d' % (x, y) return False print 'visiting %d,%d' % (x, y) # mark as visited grid[x][y] = 3 # explore neighbors clockwise starting by the one on the right if ((x < len(grid)-1 and search(x+1, y)) or (y > 0 and search(x, y-1)) or (x > 0 and search(x-1, y)) or (y < len(grid)-1 and search(x, y+1))): return True return Falsesearch(0, 0)

Let’s see what happens when we run the script.

$ python maze.pyvisiting 0,0wall at 1,0visiting 0,1wall at 1,1visited at 0,0visiting 0,2...

First cell visited is (0,0). Its neighbors are explored starting by the one on the right (1,0). search(1,0) returns False because it is a wall. There is no cell below and on the left so the one at the top (0,1) is explored. Right of that is a wall and below is already visited so the one at the top (0,2) is explored. This is what we have so far:

Simple recursivity and A* search – Laurent Luce's Blog (2)

Because the neighbor on the right is explored first, this algorithm is going to explore the dead-end at the bottom-right first.

...visiting 1,2visiting 2,2wall at 3,2visiting 2,1wall at 3,1visiting 2,0visiting 3,0visiting 4,0visiting 5,0...

Simple recursivity and A* search – Laurent Luce's Blog (3)

The algorithm is going to backtrack because there is nothing else to explore as we are in a dead-end and we are going to end up at cell (1, 2) again where there is more to explore.

...visited at 4,0wall at 5,1visited at 3,0wall at 4,1visited at 2,0wall at 3,1wall at 1,0visited at 2,1wall at 1,1visited at 2,2visited at 1,2wall at 2,3wall at 1,1visited at 0,2visiting 1,3...

Simple recursivity and A* search – Laurent Luce's Blog (4)

Let’s continue, we end up in a second dead-end at cell (4, 2).

...wall at 2,3visited at 1,2visiting 0,3visited at 1,3visited at 0,2visiting 0,4visiting 1,4visiting 2,4visiting 3,4wall at 4,4visiting 3,3visiting 4,3visiting 5,3visiting 5,2wall at 5,1visiting 4,2visited at 5,2wall at 4,1wall at 3,2visited at 4,3...

Simple recursivity and A* search – Laurent Luce's Blog (5)

Backtracking happens one more time to go back to cell (5, 3) and we are now on our way to the exit.

...visiting 5,4visited at 5,3wall at 4,4found at 5,5

Simple recursivity and A* search – Laurent Luce's Blog (6)

The full walk looks like this:

Simple recursivity and A* search – Laurent Luce's Blog (7)

A* search

We are going to look at a more sophisticated algorithm called A* search. This is based on costs to move around the grid. Let’s assume the cost to move horizontally or vertically 1 cell is equal to 10. Again, we cannot move diagonally here.

Before we start describing the algorithm, let’s define 2 variables: G and H. G is the cost to move from the starting cell to a given cell.

Simple recursivity and A* search – Laurent Luce's Blog (8)

H is an estimation of the cost to move from a given cell to the ending cell. How do we calculate that if we don’t know the path to the ending cell? To simplify, we just calculate the distance if no walls were present. There are other ways to do the estimation but this one is good enough for this example.

Simple recursivity and A* search – Laurent Luce's Blog (9)

We use 2 lists: an open list containing the cells to explore and a closed list containing the processed cells. We start with the starting cell in the open list and nothing in the closed list.

Let’s follow 1 round of this algorithm by processing our first cell from the open list. It is the starting cell. We remove it from the list and append it to the closed list. We retrieve the list of adjacent cells and we start processing them. The starting cell has 2 adjacent cells: (1, 0) and (0, 1). (1, 0) is a wall so we drop that one. (0, 1) is reachable and not in the closed list so we process it. We calculate G and H for it. G = 10 as we just need to move 1 cell up from the starting cell. H = 90: 5 cells right and 4 cells up to reach the ending cell. We call the sum F = G + H = 10 + 90 = 100. We set the parent of this adjacent cell to be the cell we just removed from the open list: e.g. (0, 0). Finally, we add this adjacent cell to the open list. This is what we have so far. The arrow represents the pointer to the parent cell.

Simple recursivity and A* search – Laurent Luce's Blog (10)

We continue with the cell in the open list having the lowest F = G + H. Only one cell is in the open list so it makes it easy. We remove it from the open list and we get its adjacent cells. Again, only one adjacent cell is reachable: (0, 2). We end up with the following after this 2nd round.

Simple recursivity and A* search – Laurent Luce's Blog (11)

3nd round result looks like this. Cells in green are in the open list. Cells in red are in the closed list.

Simple recursivity and A* search – Laurent Luce's Blog (12)

Let’s detail the next round. We have 2 cells in the open list: (1, 2) and (0, 3). Both have the same F value so we pick the last one added which is (0, 3). This cell has 3 reachable adjacent cells: (1, 3), (0, 2) and (0, 4). We process (1, 3) and (0, 4). (0, 2) is in the closed list so we don’t process that one again. We end up with:

Simple recursivity and A* search – Laurent Luce's Blog (13)

Let’s fast forward to:

Simple recursivity and A* search – Laurent Luce's Blog (14)

We have (1, 2), (1, 3) and (3, 3) in the open list. (1, 3) is processed next because it is the last one added with the lowest F value = 100. (1, 3) has 1 adjacent cell which is not in the closed list. It is (1, 2) which is in the open list. When an adjacent cell is in the open list, we check if its F value would be less if the path taken was going through the cell currently processed e.g. through (1, 3). Here it is not the case so we don’t update G and H of (1, 2) and its parent. This trick makes the algorithm more efficient when this condition exists.

Let’s take a break and look at a diagram representing the algorithm steps and conditions:

Simple recursivity and A* search – Laurent Luce's Blog (15)

We continue processing the cells remaining in the open list. Fast forward to:

Simple recursivity and A* search – Laurent Luce's Blog (16)

We have 2 cells in the open list: (3, 3) and (2, 0). The next cell removed from the open list is (3, 3) because its F is equal to 120. This proves that this algorithm is better than the first one we saw. We don’t end up exploring the dead end at (5, 0) and we continue walking from (3, 3) instead which is better.

Fast forward again to:

Simple recursivity and A* search – Laurent Luce's Blog (17)

The next cell processed from the open list is (5, 5) and it is the ending cell so we have found our path. It is easy to display the path. We just have to follow the parent pointers up to the starting cell. Our path is highlighted in green on the following diagram:

Simple recursivity and A* search – Laurent Luce's Blog (18)

You can read more about this algorithm here.

A* implementation

The basic object here is a cell so we write a class for it. We store the coordinates x and y, the values of G and H plus the sum F.

class Cell(object): def __init__(self, x, y, reachable): """ Initialize new cell @param x cell x coordinate @param y cell y coordinate @param reachable is cell reachable? not a wall? """ self.reachable = reachable self.x = x self.y = y self.parent = None self.g = 0 self.h = 0 self.f = 0

Next is our main class named AStar. Attributes are the open list heapified (keep cell with lowest F at the top), the closed list which is a set for fast lookup, the cells list (grid definition) and the size of the grid.

class AStar(object): def __init__(self): self.opened = [] heapq.heapify(self.opened) self.closed = set() self.cells = [] self.grid_height = 6 self.grid_width = 6 ...

We create a simple method initializing the list of cells to match our example with the walls at the same locations.

 def init_grid(self): walls = ((0, 5), (1, 0), (1, 1), (1, 5), (2, 3), (3, 1), (3, 2), (3, 5), (4, 1), (4, 4), (5, 1)) for x in range(self.grid_width): for y in range(self.grid_height): if (x, y) in walls: reachable = False else: reachable = True self.cells.append(Cell(x, y, reachable)) self.start = self.get_cell(0, 0) self.end = self.get_cell(5, 5)

Our heuristic compute method:

 def get_heuristic(self, cell): """ Compute the heuristic value H for a cell: distance between this cell and the ending cell multiply by 10. @param cell @returns heuristic value H """ return 10 * (abs(cell.x - self.end.x) + abs(cell.y - self.end.y))

We need a method to return a particular cell based on x and y coordinates.

 def get_cell(self, x, y): """ Returns a cell from the cells list @param x cell x coordinate @param y cell y coordinate @returns cell """ return self.cells[x * self.grid_height + y]

Next is a method to retrieve the list of adjacent cells to a specific cell.

 def get_adjacent_cells(self, cell): """ Returns adjacent cells to a cell. Clockwise starting from the one on the right. @param cell get adjacent cells for this cell @returns adjacent cells list """ cells = [] if cell.x < self.grid_width-1: cells.append(self.get_cell(cell.x+1, cell.y)) if cell.y > 0: cells.append(self.get_cell(cell.x, cell.y-1)) if cell.x > 0: cells.append(self.get_cell(cell.x-1, cell.y)) if cell.y < self.grid_height-1: cells.append(self.get_cell(cell.x, cell.y+1)) return cells

Simple method to print the path found. It follows the parent pointers to go from the ending cell to the starting cell.

 def display_path(self): cell = self.end while cell.parent is not self.start: cell = cell.parent print 'path: cell: %d,%d' % (cell.x, cell.y)

We need a method to calculate G and H and set the parent cell.

 def update_cell(self, adj, cell): """ Update adjacent cell @param adj adjacent cell to current cell @param cell current cell being processed """ adj.g = cell.g + 10 adj.h = self.get_heuristic(adj) adj.parent = cell adj.f = adj.h + adj.g

The main method implements the algorithm itself.

 def process(self): # add starting cell to open heap queue heapq.heappush(self.opened, (self.start.f, self.start)) while len(self.opened): # pop cell from heap queue f, cell = heapq.heappop(self.opened) # add cell to closed list so we don't process it twice self.closed.add(cell) # if ending cell, display found path if cell is self.end: self.display_path() break # get adjacent cells for cell adj_cells = self.get_adjacent_cells(cell) for adj_cell in adj_cells: if adj_cell.reachable and adj_cell not in self.closed: if (adj_cell.f, adj_cell) in self.opened: # if adj cell in open list, check if current path is # better than the one previously found for this adj # cell. if adj_cell.g > cell.g + 10: self.update_cell(adj_cell, cell) else: self.update_cell(adj_cell, cell) # add adj cell to open list heapq.heappush(self.opened, (adj_cell.f, adj_cell))

You can checkout the code on GitHub: git clone https://laurentluce@github.com/laurentluce/python-algorithms.git.

That’s it for now. I hope you enjoyed the article. Please write a comment if you have any feedback.

Simple recursivity and A* search – Laurent Luce's Blog (2024)

FAQs

What is a simple recursive algorithm? ›

Recursive Algorithm is a problem-solving approach that solves a problem by solving smaller instances of the same problem. These algorithms make the process of coding certain tasks simpler and cleaner, improving the readability and understanding of the code.

What is the algorithm for solving a maze in Python? ›

To solve the maze, we use the breadth-first search (BFS) algorithm. Unlike DFS, which goes as deep as possible into the maze before backtracking, BFS explores all neighbors of the current cell before moving on. This guarantees that it will find the shortest path from the starting point to the target.

What is an example of recursion in real life? ›

For example, imagine you are sorting 100 documents with names on them. First, you place documents into piles by the first letter, then sort each pile. Feedback loops in a hierarchical organization is another example of recursion.

What is the example of simple recursion? ›

A classic example of recursion

The factorial of a number is computed as that number times all of the numbers below it up to and including 1. For example, factorial(5) is the same as 5*4*3*2*1 , and factorial(3) is 3*2*1 .

What is the a * algorithm in maze-solving? ›

A* search algorithm will find the path between source and destination. Source and destination location must be selected. Then select one of the three elements that can move on the map (amphibious, ground, or water). Maze solving is considering the image as black and white image.

What is the fastest maze-solving algorithm? ›

Trémaux's algorithm, invented by Charles Pierre Trémaux, is an efficient method to find the way out of a maze that requires drawing lines on the floor to mark a path, and is guaranteed to work for all mazes that have well-defined passages, but it is not guaranteed to find the shortest route.

What is the easiest maze algorithm? ›

Randomized depth-first search

This algorithm, also known as the "recursive backtracker" algorithm, is a randomized version of the depth-first search algorithm. Frequently implemented with a stack, this approach is one of the simplest ways to generate a maze using a computer.

What is example algorithms of recursion? ›

Recursion can be equally well applied to computer algorithms: Some Computer related examples include: Adding a list of numbers, Computing the Fibonacci sequence, computing a Factorial, and Sudoku.

What is the simplest type of recursion? ›

In the simplest type of recursion, called primitive recursion, one refers only to the function value assigned to the place n in defining the value at the place n + 1.

What is a recursive in simple terms? ›

Something that is recursive has to do with a procedure or rule that is repeated. Think of something that "reoccurs" over and over again, like those fun house mirrors that are angled to present an infinite number of images. The adjective recursive comes from the Latin recurrere.

What are the famous recursion algorithms? ›

Some simple recursive algorithms that one must know.
  • Factorial of a Number.
  • Greatest Common Divisor.
  • Fibonacci Numbers.
  • Recursive Binary Search. Find middle position. Compare key with data at middle position. ...
  • Linked List Print. If node is null exit. ...
  • Reversing a String. Take the string except for the first character.
Mar 13, 2013

Top Articles
Country Inn and Suites by Radisson Boone NC (Shulls Mills)
J/99 Speedster- A Family Friendly High-Performance 32 ft sailboat
Netr Aerial Viewer
Robinhood Turbotax Discount 2023
Southeast Iowa Buy Sell Trade
877-668-5260 | 18776685260 - Robocaller Warning!
Richard Sambade Obituary
Soap2Day Autoplay
What is international trade and explain its types?
Tribune Seymour
1TamilMV.prof: Exploring the latest in Tamil entertainment - Ninewall
Vocabulario A Level 2 Pp 36 40 Answers Key
Sotyktu Pronounce
Goldsboro Daily News Obituaries
Industry Talk: Im Gespräch mit den Machern von Magicseaweed
Echat Fr Review Pc Retailer In Qatar Prestige Pc Providers – Alpha Marine Group
Puretalkusa.com/Amac
Icommerce Agent
Vipleaguenba
Ge-Tracker Bond
Dtlr Duke St
Mtr-18W120S150-Ul
Gran Turismo Showtimes Near Marcus Renaissance Cinema
Bethel Eportal
Www Va Lottery Com Result
Utexas Iot Wifi
Craigslist Dubuque Iowa Pets
Kabob-House-Spokane Photos
A Christmas Horse - Alison Senxation
Marlene2995 Pagina Azul
Roseann Marie Messina · 15800 Detroit Ave, Suite D, Lakewood, OH 44107-3748 · Lay Midwife
Boondock Eddie's Menu
Caderno 2 Aulas Medicina - Matemática
Bismarck Mandan Mugshots
Wattengel Funeral Home Meadow Drive
Heelyqutii
Body Surface Area (BSA) Calculator
Spn-523318
10 Rarest and Most Valuable Milk Glass Pieces: Value Guide
Alpha Labs Male Enhancement – Complete Reviews And Guide
Tinfoil Unable To Start Software 2022
Garland County Mugshots Today
Quick Base Dcps
UT Announces Physician Assistant Medicine Program
About Us
Petfinder Quiz
Tropical Smoothie Address
Maplestar Kemono
Hdmovie2 Sbs
Argus Leader Obits Today
Inloggen bij AH Sam - E-Overheid
Latest Posts
Article information

Author: Roderick King

Last Updated:

Views: 5247

Rating: 4 / 5 (51 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Roderick King

Birthday: 1997-10-09

Address: 3782 Madge Knoll, East Dudley, MA 63913

Phone: +2521695290067

Job: Customer Sales Coordinator

Hobby: Gunsmithing, Embroidery, Parkour, Kitesurfing, Rock climbing, Sand art, Beekeeping

Introduction: My name is Roderick King, I am a cute, splendid, excited, perfect, gentle, funny, vivacious person who loves writing and wants to share my knowledge and understanding with you.