Skip to content

Instantly share code, notes, and snippets.

@foyez
Created January 4, 2021 07:23
Show Gist options
  • Select an option

  • Save foyez/9bec2545f93135a8f23d69b48d538a9a to your computer and use it in GitHub Desktop.

Select an option

Save foyez/9bec2545f93135a8f23d69b48d538a9a to your computer and use it in GitHub Desktop.
BFS & DFS implementation in JS
const board = ['#####B#', '##### #', '#### #', '#### ##', ' ##', 'A######']
class Node {
constructor(state, parent, action) {
this.state = state
this.parent = parent
this.action = action
}
}
class StackFrontier {
constructor() {
this.frontier = []
}
add(node) {
this.frontier.push(node)
}
containsState(state) {
return this.frontier.some(
(node) => JSON.stringify(node.state) === JSON.stringify(state),
)
}
empty() {
return this.frontier.length === 0
}
remove() {
if (this.empty()) {
console.log('frontier is empty')
return
} else {
const node = this.frontier.pop()
// this.frontier
return node
}
}
}
class QueueFrontier extends StackFrontier {
remove() {
if (this.empty()) {
console.log('frontier is empty')
} else {
const node = this.frontier.shift()
return node
}
}
}
class Hunter {
constructor(board) {
this.height = board.length
const lineLengths = board.map((line) => line.length)
this.width = Math.max(...lineLengths)
// keep track of walls
this.walls = []
for (let i = 0; i < this.height; i++) {
const row = []
for (let j = 0; j < this.width; j++) {
if (board[i][j] === 'A') {
this.start = [i, j]
row.push(false)
} else if (board[i][j] === 'B') {
this.goal = [i, j]
row.push(false)
} else if (board[i][j] === ' ') {
row.push(false)
} else {
row.push(true)
}
}
this.walls.push(row)
}
this.solution = null
}
neighbors(state) {
const [row, col] = state
const candidates = [
['up', [row - 1, col]],
['down', [row + 1, col]],
['left', [row, col - 1]],
['right', [row, col + 1]],
]
const result = []
for (const [action, [r, c]] of candidates) {
if (
r >= 0 &&
r < this.height &&
c >= 0 &&
c < this.width &&
!this.walls[r][c]
) {
// console.log(this.walls[r][c])
result.push([action, [r, c]])
}
}
return result
}
solve() {
this.numExplored = 0
const start = new Node(this.start, null, null)
const frontier = new StackFrontier()
frontier.add(start)
this.explored = new Set()
// keep looping until solution found
while (true) {
// If nothing left in frontier, then no path
if (frontier.empty()) {
console.log('no solution')
return
}
// choose a node from the frontier
let node = frontier.remove()
this.numExplored += 1
// If node is the goal, then we have a solution
if (JSON.stringify(node.state) === JSON.stringify(this.goal)) {
// console.log(node.state, this.goal)
const actions = []
const cells = []
while (node.parent) {
actions.push(node.action)
cells.push(node.state)
node = node.parent
}
actions.reverse()
cells.reverse()
this.solution = [actions, cells]
console.log('SOLUTION:')
console.log(this.solution)
return
}
// Mark node as explored
this.explored.add(JSON.stringify(node.state))
// Add neighbors to frontier
for (const [action, state] of this.neighbors(node.state)) {
if (
!frontier.containsState(state) &&
!this.explored.has(JSON.stringify(state))
) {
const child = new Node(state, node, action)
frontier.add(child)
}
}
}
}
print() {
const solution = this.solution
? this.solution[1].map((item) => JSON.stringify(item))
: null
console.log()
let str = ''
for (const [i, row] of this.walls.entries()) {
for (const [j, col] of row.entries()) {
if (col) {
str += '█'
} else if (JSON.stringify([i, j]) === JSON.stringify(this.start)) {
str += 'A'
} else if (JSON.stringify([i, j]) === JSON.stringify(this.goal)) {
str += 'B'
} else if (solution && solution.includes(JSON.stringify([i, j]))) {
str += '*'
} else {
str += ' '
}
}
str += '\n'
}
console.log(str)
}
}
var hunter = new Hunter(board)
console.log('Board:')
hunter.print()
console.log('Solving...')
hunter.solve()
console.log('States Explored: ', hunter.numExplored)
console.log('Solution: ')
hunter.print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment