Code viewer for World: Breadth-First Search World
/* Initialise any variables. */
var NONODES = AB.randomIntAtoB (5, 15); 
var NOCONNECTIONS = NONODES * AB.randomIntAtoB(1, 2);
var graph;
var queue = [];

/* Initialise any constants. */
/* The "rest distance" between nodes. */
const springLength = 200;

/* Initialise a graph object. */
function Graph() {
    /* Store all of the nodes by label in this object. */
    this.graph = {};
  
    /* Iterate through all of the nodes using this array. */
    this.nodes = [];

    /* Initialise the start and end nodes. */
    this.start = null;
    this.end = null;
}

/* Set the start node. */
Graph.prototype.setStart = function(node) {
    this.start = node;
}

/* Set the end node. */
Graph.prototype.setEnd = function(node) {
    this.end = node;
}

/* Add a node to the graph. */
Graph.prototype.addNode = function(label) {
    /* Create a new node. */
    var node = new Node(label);
  
    /* Add this newly created node to the set of all nodes. */
    this.graph[label] = node;
    this.nodes.push(node);
  
    /* Return a reference to the node. */
    return node;
}

/* Draw the collection of nodes. */
Graph.prototype.show = function() {
    for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].showEdges();
    }
  
    for (var i = 0; i < this.nodes.length; i++) {
        this.nodes[i].show();
    }
}

/* Simulate some physics by animating the graph. */
Graph.prototype.simulate = function() {
    /* Place the first node in the centre. */
    this.nodes[0].pos.set(width / 2, height / 2);

    /* Position every node with respect to every other node. */
    for (var i = 1; i < this.nodes.length; i++) {
        var node1 = this.nodes[i];
        for (var j = 0; j < this.nodes.length; j++) {
            if (i == j) continue;
            var node2 = this.nodes[j];

            /* Create a vector that points between the above nodes. */
            var force = p5.Vector.sub(node1.pos, node2.pos);
            var dist = force.mag();

            var spring = 0;
            var k = 0.06;
      
            if (node1.isConnected(node2) || node2.isConnected(node1)) {
                spring = k * (springLength - dist);
            }
      
            var separate = 1 / (dist * k);
            force.setMag(spring + separate)
            node1.vel.add(force);
            node1.vel.mult(0.95);
        }
    }

    /* Add a velocity for all nodes. */
    for (var i = 0; i < this.nodes.length; i++) {
        var node = this.nodes[i];
        node.pos.add(node.vel);
    }
}

function Node(label) {
    this.pos = createVector(random(width), random(height));
    this.vel = createVector();
    this.col = color(0);
    this.label = label;
    this.edges = [];
    this.parent = null;
    this.searched = false;
    this.onqueue = false;
}

/* Is the node in question connected to another node? */
Node.prototype.isConnected = function(neighbor) {
    var index = this.edges.indexOf(neighbor);
    return (index >= 0);
}

/* Draw the node in question. */
Node.prototype.show = function() {
    textAlign(CENTER);
    var w = textWidth(this.label);
    stroke(255);
    fill(this.col);
    ellipse(this.pos.x, this.pos.y, w * 2, w * 2);
    fill(255);
    noStroke();
    text(this.label, this.pos.x, this.pos.y);
}

/* Add colour to the node. */
Node.prototype.highlight = function() {
    this.col = color(0, 150, 0);
}

/* Draw any connections for this node. */
Node.prototype.showEdges = function() {
    noFill();
    stroke(255);
    for (var i = 0; i < this.edges.length; i++) {
        line(this.pos.x, this.pos.y, this.edges[i].pos.x, this.edges[i].pos.y);
    }
}


function connect(n, m) {
    if (!n.isConnected(m)) n.edges.push(m);
    if (!m.isConnected(n)) m.edges.push(n);
};

function setup() 
{
    createCanvas(ABWorld.fullwidth(), ABWorld.fullheight());
    graph = new Graph();
    
    var start = graph.addNode('START');
    var dest  = graph.addNode('DEST');
    var i,c,d;
  
    /* Add (NONODES-2) further random nodes. */
    for (i = 2; i <= NONODES-1; i++) 
    {
        var s = Math.random().toString(36).substring(9);
        graph.addNode(s);
    }

    /* Connect start and end to 2 random nodes. */
    for (i = 1; i <= 2; i++) {
        c = AB.randomIntAtoB(2, NONODES-1); 
        connect(graph.nodes[0], graph.nodes[c]);
    }

    for (i = 1; i <= 2; i++) {
        c = AB.randomIntAtoB(2, NONODES-1); 
        connect(graph.nodes[1], graph.nodes[c]);
    }
    
    /* Add some random other connections. */
    for (i = 1; i <= NOCONNECTIONS; i++) {
        c = AB.randomIntAtoB(2, NONODES-1); 
        d = AB.randomIntAtoB(2, NONODES-1); 
        connect(graph.nodes[c], graph.nodes[d]);
    }
    
    graph.setStart(start);
    graph.setEnd(dest);
    bfs();
}

function draw() {
    background(51);
    graph.simulate();
    graph.show();
}

function highlightPath(n) {
    var subclock = 0;
    n.highlight(); 
    
    while (n != graph.start) {
        if (subclock > 1000) break;
        subclock++;
            
        n = n.parent;
        n.highlight(); 
    }
}

function bfs() {
    var clock = 0;
    var i;
    var found = false;
 
    queue.push(graph.start);

    while (queue.length > 0) {
        if (clock > 10000) break;
        clock++;
      
        var person = queue.shift();
        if (!person.searched) {
            console.log("======");
            console.log("Start of search of " + person.label);
            if (person == graph.end) {
                console.log("Found goal node");
                highlightPath(person);
                found = true;
                break;
            } else {
                console.log("Checking neighbours of " + person.label);
                for (i = 0; i < person.edges.length; i++) {
                    var n = person.edges[i];
                    if (!n.onqueue) {
                        console.log("Adding neighbour " + n.label + " to queue");
                        queue.push(n);
                        n.parent = person;
                        n.onqueue = true;
                    }
                }
                
                person.searched = true;
                console.log("End of search of " + person.label);
            }
        }
    }
    
    if (!found) {
        console.log("No path found!");
    }
}