Code viewer for World: Breadth-first search (Kevi...

// Cloned by Toma on 16 Dec 2023 from World "Breadth-first search (Kevin Bacon) (clone by Juraj Kuruc)" by Juraj Kuruc 
// Please leave this clone trail here.
 


// Cloned by Juraj Kuruc on 29 Oct 2023 from World "Breadth-first search (Kevin Bacon)" by "Coding Train" project 
// 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
// https://www.youtube.com/watch?v=piBq7VD0ZSo

// Hard to visualise graph 


  AB.headerRHS(); 

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

  $("#selectdiv").html(" Pick an actor: <span id='actorlist'></span> ");

  $.getScript ("https://unpkg.com/vis-network/standalone/umd/vis-network.min.js");
  AB.newDiv("theoutput");
  AB.newDiv("visualisation");
  
  $("#visualisation").css({
      "height": "700px",
      "width": "1400px",
      "display": "inline-block"
      });


// 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,isActor) 
{
  var n = new Node(label,isActor);
  // 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].queued = 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,isActor)
{
  // Has a label
  this.id = label;
  this.label = label;
  // zero or more edges
  this.edges = [];
  // No parent and not searched by default
  this.parent = null;
  this.searched = false;
  this.queued = false;
  this.isActor = isActor;
  
  if(isActor) 
    this.group = 1;
  else 
    this.group = 5;
}

// 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 actors
// (redundant, the graph could handle this)
var actors;
// Dropdown menu for actors
var actorlist;

var visData;

function preload() 
{
  data = loadJSON('/uploads/codingtrain/bacon.json');
  //data = loadJSON('/uploads/jk2023/movies_small.json');
  //data = loadJSON('/uploads/jk2023/movies_extra_small1.json');
}


function setup() 
{

 noCanvas();


  // Create the graph
  graph = new Graph();
  // A separate lookup table for actors
  actors = {};
  // For all movies
  var movies = data.movies;
  
  var visNodes = new vis.DataSet({});
  var visEdges = new vis.DataSet({});
  
  for (var i = 0; i < movies.length; i++) 
  {
    // Title and castlist
    var movie = movies[i].title;
    var cast = movies[i].cast;
    // Add the movie to the graph
    var movieNode = graph.addNode(movie,false);       // Node movie 
    visNodes.add(movieNode);
    // Go through all the cast list
    for (var j = 0; j < cast.length; j++) 
    {
      var name = cast[j];
      var actorNode;
      
      // Does the actor exist already?
      if (actors[name]) actorNode = actors[name];
      // If not add a new node
      else 
      {
        actorNode = graph.addNode(name,true);        // Node person 
        actors[name] = actorNode;
        visNodes.add(actorNode);
      }
      
      // Connect movie and actor
      movieNode.connect(actorNode);             // movie connects to person 
      visEdges.add({from:movie,to:name});
    }
  }

  // Create dropdown
  actorlist = createSelect();           // https://p5js.org/reference/#/p5/createSelect 
  actorlist.parent('actorlist');
  
  var allactors = Object.keys(actors);
  // Add all the actors
  for (var i = 0; i < allactors.length; i++) 
    actorlist.option(allactors[i]);
  
  // Set up an event
  actorlist.changed(findbacon);
  
  // Visualize
  // Create the nodes
  container = document.getElementById('visualisation');
  visData = { nodes: visNodes, edges: visEdges };
  options = {};
  network = new vis.Network(container, visData, options);
  
}



function findbacon() 
{
  // Clear everyone from having been searched
  graph.clear();
  // Start and end
  var start = actors[actorlist.value()];
  var end = actors['Kevin Bacon'];
  graph.setStart(start);
  graph.setEnd(end);
  // Run the search!
  bfs();
  network.focus(start.id,{scale:1});
}

function check(n,path){
    if (n != graph.end) {
        return false;
    }
    
    console.log ("found goal node");
    // Figure out the path
    path.push(n);
    var next = n.parent;
    while (next) 
    {
      path.push(next);
      next = next.parent;
    }
    return true;
}

function bfs() 
{
  // Create a queue ad path
  var queue = [];
  var path = [];
  var found =false;
  // Get started
  queue.push(graph.start);

  while (queue.length > 0 && !found)      // 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 (check(node,path)) 
    {
      found =true;
      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(check(node,path)){
            found =true;
            break;
        }
        if (!neighbor.searched && !neighbor.queued)
        {
          console.log ("adding unsearched neighbour " + neighbor.label + " to queue");
          queue.push(neighbor);
          // make sure it won't get added twice
          neighbor.queued = true;
          // Update the parent
          neighbor.parent = node;
        }
      }
      // 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');
  
  // path = bacon + movie + x + movie + x ... + movie + x
  var s ;
  if(!found){
      s = '<h1> No Path found </h1>';
  }else{
      s = '<h1> Path length ' + path.length + '</h1>';
  }

  for (var i = path.length-1; i >= 0; i--) 
  {
    ma = !path[i].isActor ? "<b style=\"color: blue\">"+path[i].label+"</b>":path[i].label;
    s = s + ma + "<br>";
  }
  network.selectNodes(path.map(a => a.id),false);
  
  
  $("#theoutput").css({
      "margin": "30",
      "padding": "30",
      "display": "inline-block",
      "border": "2px solid black"
      });

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