|
| 1 | +package relative_distance |
| 2 | + |
| 3 | +import "core:container/queue" |
| 4 | + |
| 5 | +Name :: string |
| 6 | +Children :: []Name |
| 7 | +FamilyTree :: map[Name]Children |
| 8 | + |
| 9 | +Node :: struct { |
| 10 | + name: string, |
| 11 | + distance: int, |
| 12 | +} |
| 13 | + |
| 14 | +Set :: map[string]bool |
| 15 | + |
| 16 | +Neighbors :: map[string]Set |
| 17 | + |
| 18 | +// First establish 1-degree separation between members of the family (Neighbors graph) |
| 19 | +// - parent to child |
| 20 | +// - child to parent |
| 21 | +// - child to other child |
| 22 | +// Then use BFS (Breath-First-Search) to find a path between the `from` and `to` individuals. |
| 23 | +// Because we use BFS, the path found will be the shortest path. This only works because all the |
| 24 | +// weigths in the Neighbors graph are 1. |
| 25 | +degree_of_separation :: proc(family: FamilyTree, from: string, to: string) -> int { |
| 26 | + |
| 27 | + if from == to { return 0 } |
| 28 | + |
| 29 | + neighbors := collect_neighbors(family) |
| 30 | + defer destroy_neighbors(neighbors) |
| 31 | + |
| 32 | + visited: Set |
| 33 | + visited[from] = true |
| 34 | + defer delete(visited) |
| 35 | + |
| 36 | + to_visit: queue.Queue(Node) |
| 37 | + queue.init(&to_visit) |
| 38 | + defer queue.destroy(&to_visit) |
| 39 | + queue.push_back(&to_visit, Node{from, 0}) |
| 40 | + |
| 41 | + for queue.len(to_visit) > 0 { |
| 42 | + node := queue.pop_front(&to_visit) |
| 43 | + for neighbor in neighbors[node.name] { |
| 44 | + if neighbor == to { |
| 45 | + return node.distance + 1 |
| 46 | + } |
| 47 | + if !visited[neighbor] { |
| 48 | + visited[neighbor] = true |
| 49 | + queue.push(&to_visit, Node{neighbor, node.distance + 1}) |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + // No path found |
| 54 | + return -1 |
| 55 | +} |
| 56 | + |
| 57 | +collect_neighbors :: proc(family: FamilyTree) -> Neighbors { |
| 58 | + |
| 59 | + neighbors: Neighbors |
| 60 | + for parent, children in family { |
| 61 | + for child in children { |
| 62 | + add_neighbor(&neighbors, parent, child) |
| 63 | + add_neighbor(&neighbors, child, parent) |
| 64 | + for other_child in children { |
| 65 | + if other_child != child { |
| 66 | + add_neighbor(&neighbors, child, other_child) |
| 67 | + } |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | + return neighbors |
| 72 | +} |
| 73 | + |
| 74 | +add_neighbor :: proc(neighbors: ^Neighbors, name: string, neighbor: string) { |
| 75 | + |
| 76 | + if name not_in neighbors { |
| 77 | + neighbors[name] = make(Set) |
| 78 | + } |
| 79 | + set := neighbors[name] |
| 80 | + set[neighbor] = true |
| 81 | + neighbors[name] = set |
| 82 | + |
| 83 | +} |
| 84 | + |
| 85 | +destroy_neighbors :: proc(neighbors: Neighbors) { |
| 86 | + |
| 87 | + for key, _ in neighbors { |
| 88 | + set := neighbors[key] |
| 89 | + delete(set) |
| 90 | + } |
| 91 | + delete(neighbors) |
| 92 | +} |
0 commit comments