From 528850a6dc971988302f48ddead6b6e04b07fb20 Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Wed, 16 Sep 2026 11:12:34 +0200 Subject: [PATCH 1/4] Generate exercice files with `configlet create` --- config.json | 8 ++++ .../relative-distance/.docs/instructions.md | 39 +++++++++++++++++++ .../relative-distance/.docs/introduction.md | 12 ++++++ .../relative-distance/.meta/config.json | 17 ++++++++ .../relative-distance/.meta/example.php | 0 .../relative-distance/.meta/tests.toml | 31 +++++++++++++++ .../relative-distance/RelativeDistance.php | 0 .../RelativeDistanceTest.php | 0 8 files changed, 107 insertions(+) create mode 100644 exercises/practice/relative-distance/.docs/instructions.md create mode 100644 exercises/practice/relative-distance/.docs/introduction.md create mode 100644 exercises/practice/relative-distance/.meta/config.json create mode 100644 exercises/practice/relative-distance/.meta/example.php create mode 100644 exercises/practice/relative-distance/.meta/tests.toml create mode 100644 exercises/practice/relative-distance/RelativeDistance.php create mode 100644 exercises/practice/relative-distance/RelativeDistanceTest.php diff --git a/config.json b/config.json index aeba2635c..f187cfaea 100644 --- a/config.json +++ b/config.json @@ -1394,6 +1394,14 @@ "practices": [], "prerequisites": [], "difficulty": 7 + }, + { + "slug": "relative-distance", + "name": "Relative Distance", + "uuid": "d63fd448-07ec-4884-a9fd-64763be95378", + "practices": [], + "prerequisites": [], + "difficulty": 1 } ], "foregone": [ diff --git a/exercises/practice/relative-distance/.docs/instructions.md b/exercises/practice/relative-distance/.docs/instructions.md new file mode 100644 index 000000000..64ca4e437 --- /dev/null +++ b/exercises/practice/relative-distance/.docs/instructions.md @@ -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.wikipedia.org/wiki/Six_Degrees_of_Kevin_Bacon diff --git a/exercises/practice/relative-distance/.docs/introduction.md b/exercises/practice/relative-distance/.docs/introduction.md new file mode 100644 index 000000000..34073b40a --- /dev/null +++ b/exercises/practice/relative-distance/.docs/introduction.md @@ -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/ diff --git a/exercises/practice/relative-distance/.meta/config.json b/exercises/practice/relative-distance/.meta/config.json new file mode 100644 index 000000000..c3eb6835a --- /dev/null +++ b/exercises/practice/relative-distance/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [], + "files": { + "solution": [ + "RelativeDistance.php" + ], + "test": [ + "RelativeDistanceTest.php" + ], + "example": [ + ".meta/example.php" + ] + }, + "blurb": "Given a family tree, calculate the degree of separation.", + "source": "vaeng", + "source_url": "https://github.com/exercism/problem-specifications/pull/2537" +} diff --git a/exercises/practice/relative-distance/.meta/example.php b/exercises/practice/relative-distance/.meta/example.php new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/practice/relative-distance/.meta/tests.toml b/exercises/practice/relative-distance/.meta/tests.toml new file mode 100644 index 000000000..66c91ba09 --- /dev/null +++ b/exercises/practice/relative-distance/.meta/tests.toml @@ -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" diff --git a/exercises/practice/relative-distance/RelativeDistance.php b/exercises/practice/relative-distance/RelativeDistance.php new file mode 100644 index 000000000..e69de29bb diff --git a/exercises/practice/relative-distance/RelativeDistanceTest.php b/exercises/practice/relative-distance/RelativeDistanceTest.php new file mode 100644 index 000000000..e69de29bb From d68b6db8fb350968b3b03ebfb84ad5aa212b578b Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Wed, 16 Sep 2026 15:41:03 +0200 Subject: [PATCH 2/4] Create `test` and `example` files --- .../relative-distance/.meta/example.php | 53 ++++ .../relative-distance/RelativeDistance.php | 30 ++ .../RelativeDistanceTest.php | 267 ++++++++++++++++++ 3 files changed, 350 insertions(+) diff --git a/exercises/practice/relative-distance/.meta/example.php b/exercises/practice/relative-distance/.meta/example.php index e69de29bb..9edcfe672 100644 --- a/exercises/practice/relative-distance/.meta/example.php +++ b/exercises/practice/relative-distance/.meta/example.php @@ -0,0 +1,53 @@ + $children) { + foreach ($children as $child) { + $neighbors[$parent][$child] = true; + $neighbors[$child][$parent] = true; + } + + for ($i = 0; $i < count($children); $i++) { + for ($j = $i + 1; $j < count($children); $j++) { + $neighbors[$children[$i]][$children[$j]] = true; + $neighbors[$children[$j]][$children[$i]] = true; + } + } + } + + return $neighbors; +} + +function degreeOfSeparation(array $familyTree, string $personA, string $personB): int +{ + $neighbors = buildNeighborhood($familyTree); + + if (!isset($neighbors[$personA], $neighbors[$personB])) { + return -1; + } + + $seen = [$personA => true]; + $queue = [[$personA, 0]]; + + while ($queue) { + [$person, $degree] = array_shift($queue); + + if ($person === $personB) { + return $degree; + } + + foreach (array_keys($neighbors[$person]) as $next) { + if (!isset($seen[$next])) { + $seen[$next] = true; + $queue[] = [$next, $degree + 1]; + } + } + } + + return -1; +} diff --git a/exercises/practice/relative-distance/RelativeDistance.php b/exercises/practice/relative-distance/RelativeDistance.php index e69de29bb..0ac94d5ea 100644 --- a/exercises/practice/relative-distance/RelativeDistance.php +++ b/exercises/practice/relative-distance/RelativeDistance.php @@ -0,0 +1,30 @@ +. + * + * To disable strict typing, comment out the directive below. + */ + +declare(strict_types=1); + +function degreeOfSeparation(array $familyTree, string $personA, string $personB): int +{ + throw new \BadFunctionCallException("Implement the degreeOfSeparation function"); +} diff --git a/exercises/practice/relative-distance/RelativeDistanceTest.php b/exercises/practice/relative-distance/RelativeDistanceTest.php index e69de29bb..42da6e58a 100644 --- a/exercises/practice/relative-distance/RelativeDistanceTest.php +++ b/exercises/practice/relative-distance/RelativeDistanceTest.php @@ -0,0 +1,267 @@ + ["Tomoko"], + "Tomoko" => ["Aditi"] + ]; + $expected = 1; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Vera", "Tomoko")); + } + + /** + * uuid 30d17269-83e9-4f82-a0d7-8ef9656d8dce + */ + #[TestDox('Sibling relationship')] + public function testSiblingRelationship(): void + { + $familyTree = [ + "Dalia" => ["Olga", "Yassin"] + ]; + $expected = 1; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Olga", "Yassin")); + } + + /** + * uuid 8dffa27d-a8ab-496d-80b3-2f21c77648b5 + */ + #[TestDox('Two degrees of separation, grandchild')] + public function testTwoDegreesOfSeparationGrandchild(): void + { + $familyTree = [ + "Khadija" => ["Mateo"], + "Mateo" => ["Rami"] + ]; + $expected = 2; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Khadija", "Rami")); + } + + /** + * uuid 34e56ec1-d528-4a42-908e-020a4606ee60 + */ + #[TestDox('Unrelated individuals')] + public function testUnrelatedIndividuals(): void + { + $familyTree = [ + "Priya" => ["Rami"], + "Kaito" => ["Elif"] + ]; + $expected = -1; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Priya", "Kaito")); + } + + /** + * uuid 93ffe989-bad2-48c4-878f-3acb1ce2611b + */ + #[TestDox('Complex graph, cousins')] + public function testComplexGraphCousins(): void + { + $familyTree = [ + "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"] + ]; + $expected = 9; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Dimitri", "Fabio")); + } + + /** + * uuid 2cc2e76b-013a-433c-9486-1dbe29bf06e5 + */ + #[TestDox('Complex graph, no shortcut, far removed nephew')] + public function testComplexGraphNoShortcutFarRemovedNephew(): void + { + $familyTree = [ + "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"] + ]; + $expected = 14; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Lucia", "Jun")); + } + + /** + * uuid 46c9fbcb-e464-455f-a718-049ea3c7400a + */ + #[TestDox('Complex graph, some shortcuts, cross-down and cross-up, cousins several times removed, with unrelated family tree')] + public function testComplexGraphSomeShortcutsCrossDownAndCrossUpCousinsSeveralTimesRemovedWithUnrelatedFamilyTree(): void + { + $familyTree = [ + "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"] + ]; + $expected = 12; + + $this->assertEquals($expected, degreeOfSeparation($familyTree, "Wyatt", "Xia")); + } +} From 64a54e603e693c296726e8a13850a9dfbf8821fe Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Fri, 18 Sep 2026 08:57:11 +0200 Subject: [PATCH 3/4] Set the difficulty rank to `4` --- config.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.json b/config.json index f187cfaea..1ef332ef2 100644 --- a/config.json +++ b/config.json @@ -1093,6 +1093,14 @@ "transforming" ] }, + { + "slug": "relative-distance", + "name": "Relative Distance", + "uuid": "d63fd448-07ec-4884-a9fd-64763be95378", + "practices": [], + "prerequisites": [], + "difficulty": 4 + }, { "slug": "roman-numerals", "name": "Roman Numerals", @@ -1394,14 +1402,6 @@ "practices": [], "prerequisites": [], "difficulty": 7 - }, - { - "slug": "relative-distance", - "name": "Relative Distance", - "uuid": "d63fd448-07ec-4884-a9fd-64763be95378", - "practices": [], - "prerequisites": [], - "difficulty": 1 } ], "foregone": [ From dab8f75963cbd1a1684a8b0db817133dcac99459 Mon Sep 17 00:00:00 2001 From: resu-xuniL Date: Fri, 18 Sep 2026 08:58:07 +0200 Subject: [PATCH 4/4] Add GitHub handle to `config.json` --- exercises/practice/relative-distance/.meta/config.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exercises/practice/relative-distance/.meta/config.json b/exercises/practice/relative-distance/.meta/config.json index c3eb6835a..b3ad31bbc 100644 --- a/exercises/practice/relative-distance/.meta/config.json +++ b/exercises/practice/relative-distance/.meta/config.json @@ -1,5 +1,7 @@ { - "authors": [], + "authors": [ + "resu-xuniL" + ], "files": { "solution": [ "RelativeDistance.php"