Skip to content
Draft
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
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,14 @@
"prerequisites": [],
"difficulty": 4
},
{
"slug": "relative-distance",
"name": "Relative Distance",
"uuid": "6d5cdbb3-f592-4b18-9cd3-23a28723e9cf",
"practices": [],
"prerequisites": [],
"difficulty": 4
},
{
"slug": "flower-field",
"name": "Flower Field",
Expand Down
39 changes: 39 additions & 0 deletions exercises/practice/relative-distance/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Instructions

Your task is to determine the degree of separation between two individuals in a family tree.
This is similar to the pop culture idea that every Hollywood actor is [within six degrees of Kevin Bacon][six-bacons].

- You will be given an input, with all parent names and their children.
- Each name is unique, a child _can_ have one or two parents.
- The degree of separation is defined as the shortest number of connections from one person to another.
- If two individuals are not connected, return a value that represents "no known relationship."
Please see the test cases for the actual implementation.

## Example

Given the following family tree:

```text
┌──────────┐ ┌──────────┐ ┌───────────┐
│ Helena │ │ Erdős ├─────┤ Shusaku │
└───┬───┬──┘ └─────┬────┘ └────┬──────┘
┌───┘ └───────┐ └───────┬───────┘
┌─────┴────┐ ┌────┴───┐ ┌─────┴────┐
│ Isla ├─────┤ Tariq │ │ Kevin │
└────┬─────┘ └────┬───┘ └──────────┘
│ │
┌────┴────┐ ┌────┴───┐
│ Uma │ │ Morphy │
└─────────┘ └────────┘
```

The degree of separation between Tariq and Uma is 2 (Tariq → Isla → Uma).
There's no known relationship between Isla and Kevin, as there is no connection in the given data.
The degree of separation between Uma and Isla is 1.

~~~~exercism/note
Isla and Tariq are siblings and have a separation of 1.
Similarly, this implementation would report a separation of 2 from you to your father's brother.
~~~~

[six-bacons]: https://en.m.wikipedia.org/wiki/Six_Degrees_of_Kevin_Bacon
12 changes: 12 additions & 0 deletions exercises/practice/relative-distance/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Introduction

You've been hired to develop **Noble Knots**, the hottest new dating app for nobility!
With centuries of royal intermarriage, things have gotten… _complicated_.
To avoid any _oops-we're-twins_ situations, your job is to build a system that checks how closely two people are related.

Noble Knots is inspired by Iceland's "[Islendinga-App][islendiga-app]," which is backed up by a database that traces all known family connections between Icelanders from the time of the settlement of Iceland.
Your algorithm will determine the **degree of separation** between two individuals in the royal family tree.

Will your app help crown a perfect match?

[islendiga-app]: https://web.archive.org/web/20250816223614/http://www.islendingaapp.is/information-in-english/
19 changes: 19 additions & 0 deletions exercises/practice/relative-distance/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"meatball133"
],
"files": {
"solution": [
"src/relative_distance.cr"
],
"test": [
"spec/relative_distance_spec.cr"
],
"example": [
".meta/src/example.cr"
]
},
"blurb": "Given a family tree, calculate the degree of separation.",
"source": "vaeng",
"source_url": "https://github.com/exercism/problem-specifications/pull/2537"
}
63 changes: 63 additions & 0 deletions exercises/practice/relative-distance/.meta/src/example.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require "set"

class RelativeDistance
@graph : Hash(String, Set(String))

def initialize(family_tree : Hash(String, Array(String)))
@family_tree = family_tree
@graph = build_graph(@family_tree)
end

def degree_of_separation?(person_a : String, person_b : String) : Int32?
return 0 if person_a == person_b
return nil unless @graph.has_key?(person_a) && @graph.has_key?(person_b)

visited = Set(String).new
visited << person_a

queue = [{person_a, 0}] of Tuple(String, Int32)
head = 0

while head < queue.size
current, distance = queue[head]
head += 1

@graph[current].each do |neighbor|
next if visited.includes?(neighbor)

next_distance = distance + 1
return next_distance if neighbor == person_b

visited << neighbor
queue << {neighbor, next_distance}
end
end

nil
end

private def build_graph(family_tree : Hash(String, Array(String))) : Hash(String, Set(String))
graph = Hash(String, Set(String)).new { |hash, key| hash[key] = Set(String).new }

family_tree.each do |parent, children|
graph[parent]

children.each do |child|
graph[child]
graph[parent] << child
graph[child] << parent
end

# Siblings are considered 1 degree apart.
children.each_with_index do |child, i|
(i + 1...children.size).each do |j|
sibling = children[j]
graph[child] << sibling
graph[sibling] << child
end
end
end

graph
end
end
12 changes: 12 additions & 0 deletions exercises/practice/relative-distance/.meta/test_template.ecr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "spec"
require "../src/*"

describe "<%-= to_capitalized(@json["exercise"].to_s) %>" do
<%- @json["cases"].as_a.each do |cases| %>
<%= status()%> "<%-= cases["description"] %>" do
family_tree = <%= cases["input"]["familyTree"].to_s == "{}" ? "{} of String => Array(String)" : to_s_deep(cases["input"]["familyTree"]) %>
relative_distance = <%= to_capitalized(@json["exercise"].to_s) %>.new(family_tree)
relative_distance.<%= cases["property"].to_s.underscore %>?("<%= cases["input"]["personA"] %>", "<%= cases["input"]["personB"] %>").should eq(<%= cases["expected"].as_i? ? cases["expected"] : "nil" %>)
end
<% end %>
end
31 changes: 31 additions & 0 deletions exercises/practice/relative-distance/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[4a1ded74-5d32-47fb-8ae5-321f51d06b5b]
description = "Direct parent-child relation"

[30d17269-83e9-4f82-a0d7-8ef9656d8dce]
description = "Sibling relationship"

[8dffa27d-a8ab-496d-80b3-2f21c77648b5]
description = "Two degrees of separation, grandchild"

[34e56ec1-d528-4a42-908e-020a4606ee60]
description = "Unrelated individuals"

[93ffe989-bad2-48c4-878f-3acb1ce2611b]
description = "Complex graph, cousins"

[2cc2e76b-013a-433c-9486-1dbe29bf06e5]
description = "Complex graph, no shortcut, far removed nephew"

[46c9fbcb-e464-455f-a718-049ea3c7400a]
description = "Complex graph, some shortcuts, cross-down and cross-up, cousins several times removed, with unrelated family tree"
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
require "spec"
require "../src/*"

describe "RelativeDistance" do
it "Direct parent-child relation" do
family_tree = {"Vera" => ["Tomoko"], "Tomoko" => ["Aditi"]}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Vera", "Tomoko").should eq(1)
end

pending "Sibling relationship" do
family_tree = {"Dalia" => ["Olga", "Yassin"]}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Olga", "Yassin").should eq(1)
end

pending "Two degrees of separation, grandchild" do
family_tree = {"Khadija" => ["Mateo"], "Mateo" => ["Rami"]}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Khadija", "Rami").should eq(2)
end

pending "Unrelated individuals" do
family_tree = {"Priya" => ["Rami"], "Kaito" => ["Elif"]}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Priya", "Kaito").should eq(nil)
end

pending "Complex graph, cousins" do
family_tree = {
"Aiko" => ["Bao", "Carlos"], "Bao" => ["Dalia", "Elias"], "Carlos" => ["Fatima", "Gustavo"],
"Dalia" => ["Hassan", "Isla"], "Elias" => ["Javier"], "Fatima" => ["Khadija", "Liam"], "Gustavo" => ["Mina"],
"Hassan" => ["Noah", "Olga"], "Isla" => ["Pedro"], "Javier" => ["Quynh", "Ravi"], "Khadija" => ["Sofia"],
"Liam" => ["Tariq", "Uma"], "Mina" => ["Viktor", "Wang"], "Noah" => ["Xiomara"], "Olga" => ["Yuki"],
"Pedro" => ["Zane", "Aditi"], "Quynh" => ["Boris"], "Ravi" => ["Celine"], "Sofia" => ["Diego", "Elif"],
"Tariq" => ["Farah"], "Uma" => ["Giorgio"], "Viktor" => ["Hana", "Ian"], "Wang" => ["Jing"],
"Xiomara" => ["Kaito"], "Yuki" => ["Leila"], "Zane" => ["Mateo"], "Aditi" => ["Nia"], "Boris" => ["Oscar"],
"Celine" => ["Priya"], "Diego" => ["Qi"], "Elif" => ["Rami"], "Farah" => ["Sven"], "Giorgio" => ["Tomoko"],
"Hana" => ["Umar"], "Ian" => ["Vera"], "Jing" => ["Wyatt"], "Kaito" => ["Xia"], "Leila" => ["Yassin"],
"Mateo" => ["Zara"], "Nia" => ["Antonio"], "Oscar" => ["Bianca"], "Priya" => ["Cai"], "Qi" => ["Dimitri"],
"Rami" => ["Ewa"], "Sven" => ["Fabio"], "Tomoko" => ["Gabriela"], "Umar" => ["Helena"], "Vera" => ["Igor"],
"Wyatt" => ["Jun"], "Xia" => ["Kim"], "Yassin" => ["Lucia"], "Zara" => ["Mohammed"],
}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Dimitri", "Fabio").should eq(9)
end

pending "Complex graph, no shortcut, far removed nephew" do
family_tree = {
"Aiko" => ["Bao", "Carlos"], "Bao" => ["Dalia", "Elias"], "Carlos" => ["Fatima", "Gustavo"],
"Dalia" => ["Hassan", "Isla"], "Elias" => ["Javier"], "Fatima" => ["Khadija", "Liam"], "Gustavo" => ["Mina"],
"Hassan" => ["Noah", "Olga"], "Isla" => ["Pedro"], "Javier" => ["Quynh", "Ravi"], "Khadija" => ["Sofia"],
"Liam" => ["Tariq", "Uma"], "Mina" => ["Viktor", "Wang"], "Noah" => ["Xiomara"], "Olga" => ["Yuki"],
"Pedro" => ["Zane", "Aditi"], "Quynh" => ["Boris"], "Ravi" => ["Celine"], "Sofia" => ["Diego", "Elif"],
"Tariq" => ["Farah"], "Uma" => ["Giorgio"], "Viktor" => ["Hana", "Ian"], "Wang" => ["Jing"],
"Xiomara" => ["Kaito"], "Yuki" => ["Leila"], "Zane" => ["Mateo"], "Aditi" => ["Nia"], "Boris" => ["Oscar"],
"Celine" => ["Priya"], "Diego" => ["Qi"], "Elif" => ["Rami"], "Farah" => ["Sven"], "Giorgio" => ["Tomoko"],
"Hana" => ["Umar"], "Ian" => ["Vera"], "Jing" => ["Wyatt"], "Kaito" => ["Xia"], "Leila" => ["Yassin"],
"Mateo" => ["Zara"], "Nia" => ["Antonio"], "Oscar" => ["Bianca"], "Priya" => ["Cai"], "Qi" => ["Dimitri"],
"Rami" => ["Ewa"], "Sven" => ["Fabio"], "Tomoko" => ["Gabriela"], "Umar" => ["Helena"], "Vera" => ["Igor"],
"Wyatt" => ["Jun"], "Xia" => ["Kim"], "Yassin" => ["Lucia"], "Zara" => ["Mohammed"],
}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Lucia", "Jun").should eq(14)
end

pending "Complex graph, some shortcuts, cross-down and cross-up, cousins several times removed, with unrelated family tree" do
family_tree = {
"Aiko" => ["Bao", "Carlos"], "Bao" => ["Dalia"], "Carlos" => ["Fatima", "Gustavo"], "Dalia" => ["Hassan", "Isla"],
"Fatima" => ["Khadija", "Liam"], "Gustavo" => ["Mina"], "Hassan" => ["Noah", "Olga"], "Isla" => ["Pedro"],
"Javier" => ["Quynh", "Ravi"], "Khadija" => ["Sofia"], "Liam" => ["Tariq", "Uma"], "Mina" => ["Viktor", "Wang"],
"Noah" => ["Xiomara"], "Olga" => ["Yuki"], "Pedro" => ["Zane", "Aditi"], "Quynh" => ["Boris"],
"Ravi" => ["Celine"], "Sofia" => ["Diego", "Elif"], "Tariq" => ["Farah"], "Uma" => ["Giorgio"],
"Viktor" => ["Hana", "Ian"], "Wang" => ["Jing"], "Xiomara" => ["Kaito"], "Yuki" => ["Leila"],
"Zane" => ["Mateo"], "Aditi" => ["Nia"], "Boris" => ["Oscar"], "Celine" => ["Priya"], "Diego" => ["Qi"],
"Elif" => ["Rami"], "Farah" => ["Sven"], "Giorgio" => ["Tomoko"], "Hana" => ["Umar"], "Ian" => ["Vera"],
"Jing" => ["Wyatt"], "Kaito" => ["Xia"], "Leila" => ["Yassin"], "Mateo" => ["Zara"], "Nia" => ["Antonio"],
"Oscar" => ["Bianca"], "Priya" => ["Cai"], "Qi" => ["Dimitri"], "Rami" => ["Ewa"], "Sven" => ["Fabio"],
"Tomoko" => ["Gabriela"], "Umar" => ["Helena"], "Vera" => ["Igor"], "Wyatt" => ["Jun"], "Xia" => ["Kim"],
"Yassin" => ["Lucia"], "Zara" => ["Mohammed"],
}
relative_distance = RelativeDistance.new(family_tree)
relative_distance.degree_of_separation?("Wyatt", "Xia").should eq(12)
end
end
3 changes: 3 additions & 0 deletions exercises/practice/relative-distance/src/relative_distance.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class RelativeDistance
# Write your code for the 'RelativeDistance' exercise in this file.
end
Loading