Skip to content

Added solution for degrees of separation assignment #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.PHONY: build

build:
go build
./degrees amitabh-bachchan bunty-behl
Binary file added degrees
Binary file not shown.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module challenge2015

go 1.23.3
17 changes: 17 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"fmt"
"os"
)

func main() {

data := os.Args
if len(data) != 3 {
fmt.Println("Kindly provide correct arguments")
return
}

DegreesofSeperation(data[1], data[2])
}
192 changes: 192 additions & 0 deletions movieBuff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"sync"
)

// Node structure
type Node struct {
ID string
Type string
Parent *Node
Role string
}

func DegreesofSeperation(name1, name2 string) {
fmt.Println("Searching for path between", name1, "and", name2)

start := &Node{ID: name1, Type: "person", Parent: nil}
queue := []*Node{start}
visited := sync.Map{} // concurrent-safe map

jobs := make(chan *Node, 20)
results := make(chan []*Node, 20)

// Start a fixed pool of workers
const workerCount = 10
var wg sync.WaitGroup
for i := 0; i < workerCount; i++ {
wg.Add(1)
go bfsWorker(jobs, results, &visited, name2, &wg)
}

for len(queue) > 0 {
current := queue[0]
queue = queue[1:]

if _, ok := visited.Load(current.ID); ok {
continue
}
visited.Store(current.ID, true)

jobs <- current
newNodes := <-results

for _, node := range newNodes {
if node.ID == name2 {
PrintPath(name1, node)
close(jobs)
wg.Wait()
return
}
queue = append(queue, node)
}
}

close(jobs)
wg.Wait()
fmt.Println("No connection found.")
}

func bfsWorker(jobs <-chan *Node, results chan<- []*Node, visited *sync.Map, target string, wg *sync.WaitGroup) {
defer wg.Done()

for node := range jobs {
var nextNodes []*Node

if node.Type == "person" {
personData, err := GetPersonDetails(node.ID)
if err != nil {
results <- nextNodes
continue
}
if movies, ok := personData["movies"].([]interface{}); ok {
for _, m := range movies {
movie := m.(map[string]interface{})
movieID := movie["url"].(string)

if _, ok := visited.Load(movieID); !ok {
nextNodes = append(nextNodes, &Node{
ID: movieID,
Type: "movie",
Parent: node,
Role: getRole(movie),
})
}
}
}
} else if node.Type == "movie" {
movieData, err := GetMovieDetails(node.ID)
if err != nil {
results <- nextNodes
continue
}
for _, roleType := range []string{"cast", "crew"} {
if arr, ok := movieData[roleType].([]interface{}); ok {
for _, p := range arr {
person := p.(map[string]interface{})
personID := person["url"].(string)

if personID == target {
results <- []*Node{&Node{
ID: personID,
Type: "person",
Parent: node,
Role: getRole(person),
}}
return
}

if _, ok := visited.Load(personID); !ok {
nextNodes = append(nextNodes, &Node{
ID: personID,
Type: "person",
Parent: node,
Role: getRole(person),
})
}
}
}
}
}
results <- nextNodes
}
}

// Helper to safely extract role
func getRole(data map[string]interface{}) string {
if role, ok := data["role"].(string); ok {
return role
}
return ""
}

// Trace the path from target to source
func PrintPath(name1 string, end *Node) {
var path []*Node
var role string
for current := end; current != nil; current = current.Parent {
path = append([]*Node{current}, path...)
}

degree := (len(path) - 1) / 2
fmt.Printf("Degrees of Separation: %d\n\n", degree)

step := 1
for i := 0; i < len(path)-2; i += 2 {
movie := path[i+1]
personA := path[i]
personB := path[i+2]
if name1 == personA.ID {
role = end.Parent.Role
} else {
role = personA.Role
}

fmt.Printf("%d. Movie: %s\n", step, movie.ID)
fmt.Printf(" %s: %s\n", role, personA.ID)
fmt.Printf(" %s: %s\n\n", personB.Role, personB.ID)

step++
}
}

// Fetch person info from MovieBuff
func GetPersonDetails(person string) (map[string]interface{}, error) {
url := "https://data.moviebuff.com/" + person
resp, err := http.Get(url)
if err != nil {
return nil, errors.New("unable to fetch " + person)
}
defer resp.Body.Close()
var personResponse map[string]interface{}
json.NewDecoder(resp.Body).Decode(&personResponse)
return personResponse, nil
}

// Fetch movie info from MovieBuff
func GetMovieDetails(movie string) (map[string]interface{}, error) {
url := "https://data.moviebuff.com/" + movie
resp, err := http.Get(url)
if err != nil {
return nil, errors.New("unable to fetch " + movie)
}
defer resp.Body.Close()
var movieResponse map[string]interface{}
json.NewDecoder(resp.Body).Decode(&movieResponse)
return movieResponse, nil
}