Code viewer for World: London Underground (clone ...

// Cloned by Abdelshafa Abdala on 19 Oct 2021 from World "London Underground" by Tony Forde 
// Please leave this clone trail here.
 
// Breadth-first search

// Port from:
// https://github.com/nature-of-code/NOC-S17-2-Intelligence-Learning/tree/master/week1-graphs/P2_six_degrees_kevin_bacon

// Coding Challenge #68.1: Breadth-First Search Part 1
// https://www.youtube.com/watch?v=piBq7VD0ZSo

// Coding Challenge #68.2: Breadth-First Search Part 2
// https://www.youtube.com/watch?v=-he67EEM6z0

  AB.headerRHS(); 

  AB.newDiv("selectdiv");
  
  $("#selectdiv").css({
      "margin": "30",
      });

  $("#selectdiv").html(" Pick a station: <span id='stationlist'></span> ");



// Daniel Shiffman
// Nature of Code: Intelligence and Learning
// https://github.com/shiffman/NOC-S17-2-Intelligence-Learning

// Graph object

function Graph() 
{
  // store nodes by key and also in an array
  this.graph = {};
  this.nodes = [];
  // Start and end
  this.start = null;
  this.end = null;
}

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

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

// Add a node
Graph.prototype.addNode = function(label) 
{
  var n = new Node(label);
  // Add to both the graph object and the nodes array
  this.graph[label] = n;
  this.nodes.push(n);
  return n;
}

// Clear all searched and parent values
Graph.prototype.clear = function() 
{
  for (var i = 0; i < this.nodes.length; i++) 
  {
    this.nodes[i].searched = false;
    this.nodes[i].parent = null;
  }
}

// Daniel Shiffman
// Nature of Code: Intelligence and Learning
// https://github.com/shiffman/NOC-S17-2-Intelligence-Learning

// Node object
function Node(label) 
{
  // Has a label
  this.label = label;
  // zero or more edges
  this.edges = [];
  // No parent and not searched by default
  this.parent = null;
  this.searched = false;
}

// Connect one or more neighbors
Node.prototype.connect = function(neighbor)         // one Node (movie) has a connection to another Node (person)
{
  // This is a fancy way of having a function
  // that can accept a variable number of arguments
  for (var i = 0; i < arguments.length; i++) 
  {
    this.edges.push(arguments[i]);              // increase the size of the array 
    // Connect both ways
    arguments[i].edges.push(this);
  }
}






// Daniel Shiffman
// Nature of Code: Intelligence and Learning
// https://github.com/shiffman/NOC-S17-2-Intelligence-Learning

// The data
var data;

// The graph
var graph;
// A lookup table for stations
// (redundant, the graph could handle this)
var stations;
// Dropdown menu for stations
var stationlist1;
var stationlist2;

function preload() 
{
  data = loadJSON('/uploads/fordea23/london_underground.json');
}

function setup() 
{ 

 noCanvas();


  // Create the graph
  graph = new Graph();
  // A separate lookup table for stations
  stations = {};
  // For all trainlines
  var trainlines = data.trainlines;
  
  for (var i = 0; i < trainlines.length; i++) 
  {
    // Line Name and station names list
    var movie = trainlines[i].line_name;
    var station_names = trainlines[i].station_names;
    // Add the movie to the graph
    var movieNode = graph.addNode(movie);       // Node movie 
    // Go through all the station names list
    for (var j = 0; j < station_names.length; j++) 
    {
      var name = station_names[j];
      var stationNode;
      
      // Does the station exist already?
      if (stations[name]) stationNode = stations[name];
      // If not add a new node
      else 
      {
        stationNode = graph.addNode(name);        // Node person 
        stations[name] = stationNode;
      }
      
      // Connect movie and station
      movieNode.connect(stationNode);             // movie connects to person 
    }
  }

  // Create dropdown
  stationlist1 = createSelect();           // https://p5js.org/reference/#/p5/createSelect 
  stationlist1.parent('stationlist');
  stationlist2 = createSelect();           // https://p5js.org/reference/#/p5/createSelect 
  stationlist2.parent('stationlist');
  var allstations = Object.keys(stations);
  allstations.sort();

  // Add all the stations
  for (var i = 0; i < allstations.length; i++) 
    stationlist1.option(allstations[i]);
  // Set up an event
  stationlist1.changed(findRoute);

  for (var i = 0; i < allstations.length; i++) 
    stationlist2.option(allstations[i]);
  // Set up an event
  stationlist2.changed(findRoute);
}


function findRoute() 
{
  // Clear everyone from having been searched
  graph.clear();
  // Start and end
  var start = stations[stationlist1.value()];
  var end = stations[stationlist2.value()];
  graph.setStart(start);
  graph.setEnd(end);
  // Run the search!
  bfs();
}


function bfs() 
{
  // Create a queue ad path
  var queue = [];
  var path = [];

  // Get started
  queue.push(graph.start);

  while (queue.length > 0)      // inside the loop we keep adding things to queue
  {
    var node = queue.shift();               // remove first item
    console.log ("======" );
    console.log ("start of search of " + node.label);
    
    // Are we done?
    if (node == graph.end) 
    {
      console.log ("found goal node");
      // Figure out the path
      path.push(node);
      var next = node.parent;
      while (next) 
      {
        path.push(next);
        next = next.parent;
      }
      break;
    } 
    else 
    {
     console.log ("checking neighbours of " + node.label);
     // Check all neighbors
      var next = node.edges;
      // console.log ("checking " + next.length + " neighbours");
      for (var i = 0; i < next.length; i++) 
      {
        var neighbor = next[i];
        // Any neighbor not already searched add to queue
        if (!neighbor.searched) 
        {
          console.log ("adding unsearched neighbour " + neighbor.label + " to queue");
          queue.push(neighbor);
          // Update the parent
          neighbor.parent = node;
          neighbor.searched = true; // *** ADDED FIX HERE: Just this one line
        }
      }
      // Mark node as searched
      node.searched = true;
     console.log ("end of search of " + node.label);
    // console.log ("======" );
   }
  }         // end of while loop
  

// now output path 

  console.log('finished search for path');
  
  var s = '<h1> Path length ' + path.length + '</h1>';
  
  for (var i = path.length-1; i >= 0; i--) 
  {
    s = s + path[i].label + "<br>";
  }
  
  AB.newDiv("theoutput");
  
  $("#theoutput").css({
      "margin": "30",
      "padding": "30",
      "display": "inline-block",
      "border": "1px solid black"
      });

  $("#theoutput").html(s);


}