Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit b516787

Browse files
Merge pull request TheAlgorithms#714 from defaude/test/Backtracking/RatInAMaze
Rewrote "Rat in a maze" algorithm + added tests
2 parents b793105 + 28f13bb commit b516787

File tree

2 files changed

+200
-49
lines changed

2 files changed

+200
-49
lines changed

‎Backtracking/RatInAMaze.js

Lines changed: 108 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,126 @@
11
/*
22
* Problem Statement:
3-
* - Given a NxN grid, find whether rat in cell (0,0) can reach target(N-1,N-1);
4-
* - In grid "0" represent blocked cell and "1" represent empty cell
3+
* - Given a NxN grid, find whether rat in cell [0, 0] can reach the target in cell [N-1, N-1]
4+
* - The grid is represented as an array of rows. Each row is represented as an array of 0 or 1 values.
5+
* - A cell with value 0 can not be moved through. Value 1 means the rat can move here.
6+
* - The rat can not move diagonally.
57
*
6-
* Reference for this problem:
7-
* - https://www.geeksforgeeks.org/rat-in-a-maze-backtracking-2/
8+
* Reference for this problem: https://www.geeksforgeeks.org/rat-in-a-maze-backtracking-2/
89
*
10+
* Based on the original implementation contributed by Chiranjeev Thapliyal (https://github.com/chiranjeev-thapliyal).
911
*/
1012

11-
// Helper function to find if current cell is out of boundary
12-
const outOfBoundary = (grid, currentRow, currentColumn) => {
13-
if (currentRow < 0 || currentColumn < 0 || currentRow >= grid.length || currentColumn >= grid[0].length) {
14-
return true
15-
} else {
16-
return false
17-
}
13+
/**
14+
* Checks if the given grid is valid.
15+
*
16+
* A grid needs to satisfy these conditions:
17+
* - must not be empty
18+
* - must be a square
19+
* - must not contain values other than {@code 0} and {@code 1}
20+
*
21+
* @param grid The grid to check.
22+
* @throws TypeError When the given grid is invalid.
23+
*/
24+
function validateGrid (grid) {
25+
if (!Array.isArray(grid) || grid.length === 0) throw new TypeError('Grid must be a non-empty array')
26+
27+
const allRowsHaveCorrectLength = grid.every(row => row.length === grid.length)
28+
if (!allRowsHaveCorrectLength) throw new TypeError('Grid must be a square')
29+
30+
const allCellsHaveValidValues = grid.every(row => {
31+
return row.every(cell => cell === 0 || cell === 1)
32+
})
33+
if (!allCellsHaveValidValues) throw new TypeError('Grid must only contain 0s and 1s')
1834
}
1935

20-
const printPath = (grid, currentRow, currentColumn, path) => {
21-
// If cell is out of boundary, we can't proceed
22-
if (outOfBoundary(grid, currentRow, currentColumn)) return false
36+
function isSafe (grid, x, y) {
37+
const n = grid.length
38+
return x >= 0 && x < n && y >= 0 && y < n && grid[y][x] === 1
39+
}
2340

24-
// If cell is blocked then you can't go ahead
25-
if (grid[currentRow][currentColumn] === 0) return false
41+
/**
42+
* Attempts to calculate the remaining path to the target.
43+
*
44+
* @param grid The full grid.
45+
* @param x The current X coordinate.
46+
* @param y The current Y coordinate.
47+
* @param solution The current solution matrix.
48+
* @param path The path we took to get from the source cell to the current location.
49+
* @returns {string|boolean} Either the path to the target cell or false.
50+
*/
51+
function getPathPart (grid, x, y, solution, path) {
52+
const n = grid.length
2653

27-
// If we reached target cell, then print path
28-
if (currentRow === targetRow&& currentColumn === targetColumn) {
29-
console.log(path)
30-
return true
54+
// are we there yet?
55+
if (x === n-1&& y === n-1&&grid[y][x]===1) {
56+
solution[y][x]=1
57+
return path
3158
}
3259

33-
// R,L,D,U are directions `Right, Left, Down, Up`
34-
const directions = [
35-
[1, 0, 'D'],
36-
[-1, 0, 'U'],
37-
[0, 1, 'R'],
38-
[0, -1, 'L']
39-
]
40-
41-
for (let i = 0; i < directions.length; i++) {
42-
const nextRow = currentRow + directions[i][0]
43-
const nextColumn = currentColumn + directions[i][1]
44-
const updatedPath = path + directions[i][2]
45-
46-
grid[currentRow][currentColumn] = 0
47-
if (printPath(grid, nextRow, nextColumn, updatedPath)) return true
48-
grid[currentRow][currentColumn] = 1
49-
}
60+
// did we step on a 0 cell or outside the grid?
61+
if (!isSafe(grid, x, y)) return false
62+
63+
// are we walking onto an already-marked solution coordinate?
64+
if (solution[y][x] === 1) return false
65+
66+
// none of the above? let's dig deeper!
67+
68+
// mark the current coordinates on the solution matrix
69+
solution[y][x] = 1
70+
71+
// attempt to move right
72+
const right = getPathPart(grid, x + 1, y, solution, path + 'R')
73+
if (right) return right
74+
75+
// right didn't work: attempt to move down
76+
const down = getPathPart(grid, x, y + 1, solution, path + 'D')
77+
if (down) return down
78+
79+
// down didn't work: attempt to move up
80+
const up = getPathPart(grid, x, y - 1, solution, path + 'U')
81+
if (up) return up
82+
83+
// up didn't work: attempt to move left
84+
const left = getPathPart(grid, x - 1, y, solution, path + 'L')
85+
if (left) return left
86+
87+
// no direction was successful: remove this cell from the solution matrix and backtrack
88+
solution[y][x] = 0
5089
return false
5190
}
5291

53-
// Driver Code
92+
function getPath (grid) {
93+
// grid dimensions
94+
const n = grid.length
95+
96+
// prepare solution matrix
97+
const solution = []
98+
for (let i = 0; i < n; i++) {
99+
const row = Array(n)
100+
row.fill(0)
101+
solution[i] = row
102+
}
103+
104+
return getPathPart(grid, 0, 0, solution, '')
105+
}
54106

55-
const grid = [
56-
[1, 1, 1, 1],
57-
[1, 0, 0, 1],
58-
[0, 0, 1, 1],
59-
[1, 1, 0, 1]
60-
]
107+
/**
108+
* Creates an instance of the "rat in a maze" based on a given grid (maze).
109+
*/
110+
export class RatInAMaze {
111+
constructor (grid) {
112+
// first, let's do some error checking on the input
113+
validateGrid(grid)
61114

62-
consttargetRow=grid.length-1
63-
const targetColumn = grid[0].length-1
115+
// attempt to solve the maze now - all public methods only query the result state later
116+
const solution = getPath(grid)
64117

65-
// Variation 2 : print a possible path to reach from (0, 0) to (N-1, N-1)
66-
// If there is no path possible then it will print "Not Possible"
67-
!printPath(grid, 0, 0, '') && console.log('Not Possible')
118+
if (solution !== false) {
119+
this.path = solution
120+
this.solved = true
121+
} else {
122+
this.path = ''
123+
this.solved = false
124+
}
125+
}
126+
}

‎Backtracking/tests/RatInAMaze.test.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { RatInAMaze } from '../RatInAMaze'
2+
3+
describe('RatInAMaze', () => {
4+
it('should fail for non-arrays', () => {
5+
const values = [undefined, null, {}, 42, 'hello, world']
6+
7+
for (const value of values) {
8+
// we deliberately want to check whether this constructor call fails or not
9+
// eslint-disable-next-line no-new
10+
expect(() => { new RatInAMaze(value) }).toThrow()
11+
}
12+
})
13+
14+
it('should fail for an empty array', () => {
15+
// we deliberately want to check whether this constructor call fails or not
16+
// eslint-disable-next-line no-new
17+
expect(() => { new RatInAMaze([]) }).toThrow()
18+
})
19+
20+
it('should fail for a non-square array', () => {
21+
const array = [
22+
[0, 0, 0],
23+
[0, 0]
24+
]
25+
26+
// we deliberately want to check whether this constructor call fails or not
27+
// eslint-disable-next-line no-new
28+
expect(() => { new RatInAMaze(array) }).toThrow()
29+
})
30+
31+
it('should fail for arrays containing invalid values', () => {
32+
const values = [[[2]], [['a']]]
33+
34+
for (const value of values) {
35+
// we deliberately want to check whether this constructor call fails or not
36+
// eslint-disable-next-line no-new
37+
expect(() => { new RatInAMaze(value) }).toThrow()
38+
}
39+
})
40+
41+
it('should work for a single-cell maze', () => {
42+
const maze = new RatInAMaze([[1]])
43+
expect(maze.solved).toBe(true)
44+
expect(maze.path).toBe('')
45+
})
46+
47+
it('should work for a single-cell maze that can not be solved', () => {
48+
const maze = new RatInAMaze([[0]])
49+
expect(maze.solved).toBe(false)
50+
expect(maze.path).toBe('')
51+
})
52+
53+
it('should work for a simple 3x3 maze', () => {
54+
const maze = new RatInAMaze([[1, 1, 0], [0, 1, 0], [0, 1, 1]])
55+
expect(maze.solved).toBe(true)
56+
expect(maze.path).toBe('RDDR')
57+
})
58+
59+
it('should work for a simple 2x2 that can not be solved', () => {
60+
const maze = new RatInAMaze([[1, 0], [0, 1]])
61+
expect(maze.solved).toBe(false)
62+
expect(maze.path).toBe('')
63+
})
64+
65+
it('should work for a more complex maze', () => {
66+
const maze = new RatInAMaze([
67+
[1, 1, 1, 1, 1, 0, 0],
68+
[0, 0, 0, 0, 1, 0, 0],
69+
[1, 1, 1, 0, 1, 0, 0],
70+
[1, 0, 1, 0, 1, 0, 0],
71+
[1, 0, 1, 1, 1, 0, 0],
72+
[1, 0, 0, 0, 0, 0, 0],
73+
[1, 1, 1, 1, 1, 1, 1]
74+
])
75+
expect(maze.solved).toBe(true)
76+
expect(maze.path).toBe('RRRRDDDDLLUULLDDDDRRRRRR')
77+
})
78+
79+
it('should work for a more complex maze that can not be solved', () => {
80+
const maze = new RatInAMaze([
81+
[1, 1, 1, 1, 1, 0, 1],
82+
[0, 0, 0, 0, 1, 0, 1],
83+
[1, 1, 1, 0, 1, 0, 1],
84+
[1, 0, 1, 0, 1, 0, 1],
85+
[1, 0, 1, 0, 1, 1, 1],
86+
[1, 0, 0, 0, 0, 0, 0],
87+
[1, 1, 1, 1, 1, 1, 1]
88+
])
89+
expect(maze.solved).toBe(false)
90+
expect(maze.path).toBe('')
91+
})
92+
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /