~ 5 min read

Solving an ASCII Maze with a Neural Network in JavaScript

share this story on
A step-by-step guide to training a neural network to solve an ASCII maze using JavaScript and brain.js.

Wouldn’t it be cool if we could use neural networks to solve a maze? Well, we can! You’ll also find the complete code on GitHub.

Basically we’re going to train a neural network to find the path from the start (S) to the end (E) of a maze represented in ASCII format. The maze will be represented as a grid of characters, where # represents walls, (space) represents open paths, S is the start point, and E is the end point.

Imagine something like this. Actually, you don’t need to imagine. The maze is in ASCII 😆 so you can literally take a look:

{error: 0.004700880403111688, iterations: 75}
Solution found!

Path through the maze:
# # # # # # #
S * * * #   #
# # # * #   #
#     * * * #
#   # # # * #
#       # * E
# # # # # # #

Ever wondered how artificial intelligence can navigate complex mazes? Let’s set out to explore how to train a neural network to solve mazes using JavaScript and the brain.js library. By the end, you’ll have a practical understanding of maze representation, training data generation, and neural network application in Node.js. Let’s dive in!

This is what we are going to build:

Neural Network Solving a Maze GIF

Prerequisites

Before we start, ensure you have the following:

  • Node.js: Version 14 or higher.
  • npm: Version 6 or higher.
  • brain.js: Install via npm install brain.js.
  • Basic understanding of JavaScript and Node.js.

Represent the Maze in Code

To solve a maze, we first need to represent it in a way that our neural network can understand. We’ll use a 2D array where each cell can be a wall (#), a start point (S), an end point (E), or a path ( ).

1. Define the Maze Structure

Why this matters: A clear representation allows us to map positions to coordinates, which is crucial for processing and training.

const maze = [
  ['#', '#', '#', '#', '#', '#'],
  ['#', 'S', ' ', ' ', 'E', '#'],
  ['#', ' ', '#', ' ', '#', '#'],
  ['#', ' ', '#', ' ', ' ', '#'],
  ['#', '#', '#', '#', '#', '#']
];

2. Map Positions to Coordinates

Mapping positions helps in tracking the agent’s movement through the maze.

function getCoordinates(maze) {
  const coordinates = [];
  maze.forEach((row, y) => {
    row.forEach((cell, x) => {
      if (cell !== '#') {
        coordinates.push({ x, y });
      }
    });
  });
  return coordinates;
}

const coordinates = getCoordinates(maze);
console.log('Maze Coordinates:', coordinates);

Generate Training Data

Training data is essential for teaching the neural network how to navigate the maze. We’ll create examples based on valid moves from each non-wall cell.

3. Create Training Examples

Why this matters: Training examples guide the neural network in learning valid moves and optimal paths.

function generateTrainingData(maze) {
  const trainingData = [];
  const directions = ['up', 'down', 'left', 'right'];

  coordinates.forEach(({ x, y }) => {
    directions.forEach(direction => {
      const input = { x, y, direction };
      const output = isValidMove(maze, x, y, direction);
      trainingData.push({ input, output });
    });
  });

  return trainingData;
}

function isValidMove(maze, x, y, direction) {
  // Logic to determine if a move is valid
  // Returns 1 for valid, 0 for invalid
}

const trainingData = generateTrainingData(maze);
console.log('Sample Training Data:', trainingData.slice(0, 5));

Set Up the Neural Network

With our training data ready, it’s time to set up the neural network using brain.js.

4. Configure the Neural Network

Why this matters: A well-configured network can efficiently learn and predict the best moves.

const brain = require('brain.js');
const net = new brain.NeuralNetwork({
  hiddenLayers: [3]
});

net.train(trainingData, {
  errorThresh: 0.005, // Aim for a low error threshold
  iterations: 20000,
  log: true,
  logPeriod: 100
});

Solve the Maze

Now, let’s use the trained neural network to solve the maze.

5. Traverse the Maze

Why this matters: This step demonstrates the neural network’s ability to make decisions and find the path to the exit.

function solveMaze(maze, net) {
  let position = { x: 1, y: 1 }; // Starting position
  const path = [];

  while (maze[position.y][position.x] !== 'E') {
    const output = net.run(position);
    const direction = getBestDirection(output);
    position = move(position, direction);
    path.push(position);
  }

  return path;
}

const solution = solveMaze(maze, net);
console.log('Solution Path:', solution);

Visualize the Process

Visualizing the maze and the path taken by the neural network can provide insights into its decision-making process.

6. Visualize Maze States

Why this matters: Visualization helps in understanding how the neural network navigates the maze.

function visualizeMazeAtPosition(maze, currentPosition) {
  const visualMaze = maze.map(row => [...row]);
  const { x, y } = currentPosition;
  visualMaze[y][x] = '*';
  console.log('\nPath through the maze:');
  visualMaze.forEach(row => console.log(row.join(' ')));
}

visualizeMazeAtPosition(maze, { x: 1, y: 1 });

Troubleshooting & Pitfalls

  • Common Error: Neural network not converging.
    • Fix: Increase the number of iterations or adjust the learning rate.
  • Pitfall: Infinite loops in maze traversal.
    • Fix: Implement a mechanism to avoid revisiting positions.

Automate and Extend

Consider automating the training and solving process using CI/CD pipelines. You can also extend this project by experimenting with larger mazes or more complex neural networks.

Conclusion & Next Steps

By following this guide, you’ve learned how to train a neural network to solve mazes using Node.js and brain.js. Feel free to experiment with different mazes and explore further applications of neural networks in game AI or robotics.

Don’t forget you can check out the complete code on my GitHub profile at the following code repository: lirantal/neural-network-solves-maze.

Happy coding and deploying!