diff --git a/config.json b/config.json index d9cdc899..eaee52c0 100644 --- a/config.json +++ b/config.json @@ -931,6 +931,14 @@ "prerequisites": [], "difficulty": 5 }, + { + "slug": "relative-distance", + "name": "Relative Distance", + "uuid": "87476418-9bee-4f93-b266-0a3c5f9abef4", + "practices": [], + "prerequisites": [], + "difficulty": 6 + }, { "slug": "satellite", "name": "Satellite", diff --git a/exercises/practice/relative-distance/.docs/instructions.md b/exercises/practice/relative-distance/.docs/instructions.md new file mode 100644 index 00000000..64ca4e43 --- /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 00000000..34073b40 --- /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 00000000..9365fc4d --- /dev/null +++ b/exercises/practice/relative-distance/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "relative-distance.el" + ], + "test": [ + "relative-distance-test.el" + ], + "example": [ + ".meta/example.el" + ] + }, + "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.el b/exercises/practice/relative-distance/.meta/example.el new file mode 100644 index 00000000..b15da44f --- /dev/null +++ b/exercises/practice/relative-distance/.meta/example.el @@ -0,0 +1,42 @@ +;;; relative-distance.el --- Relative Distance (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + +(require 'cl-lib) + +(defun degree-of-separation (family-tree person-a person-b) + (if (string= person-a person-b) + 0 + (let ((neighbors (make-hash-table :test 'equal))) + (cl-labels ((link (a b) + (push b (gethash a neighbors)) + (push a (gethash b neighbors)))) + (cl-loop for entry in family-tree + for parent = (car entry) + do (cl-loop for children on (cdr entry) + for child = (car children) + do (link parent child) + (cl-loop for sibling in (cdr children) + do (link child sibling))))) + + (let ((visited (make-hash-table :test 'equal)) + (current (list person-a)) + (dist 1)) + (puthash person-a t visited) + (catch 'found + (cl-loop while current + do (let ((next nil)) + (cl-loop for person in current + do (cl-loop for neighbor in (gethash person neighbors) + unless (gethash neighbor visited) + do (when (string= neighbor person-b) + (throw 'found dist)) + (puthash neighbor t visited) + (push neighbor next))) + (setq current next) + (setq dist (1+ dist))))))))) + +(provide 'relative-distance) +;;; relative-distance.el ends here diff --git a/exercises/practice/relative-distance/.meta/tests.toml b/exercises/practice/relative-distance/.meta/tests.toml new file mode 100644 index 00000000..66c91ba0 --- /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/relative-distance-test.el b/exercises/practice/relative-distance/relative-distance-test.el new file mode 100644 index 00000000..0d6db63c --- /dev/null +++ b/exercises/practice/relative-distance/relative-distance-test.el @@ -0,0 +1,212 @@ +;;; relative-distance-test.el --- Tests for Relative Distance (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(load-file "relative-distance.el") +(declare-function degree-of-separation "relative-distance.el" (family-tree person-a person-b)) + + +(ert-deftest direct-parent-child-relation () + (should (= 1 (degree-of-separation '( + ("Vera" "Tomoko") + ("Tomoko" "Aditi")) + "Vera" "Tomoko")))) + + +(ert-deftest sibling-relationship () + (should (= 1 (degree-of-separation '( + ("Dalia" "Olga" "Yassin")) + "Olga" "Yassin")))) + + +(ert-deftest two-degrees-of-separation-grandchild () + (should (= 2 (degree-of-separation '( + ("Khadija" "Mateo") + ("Mateo" "Rami")) + "Khadija" "Rami")))) + + +(ert-deftest unrelated-individuals () + (should-not (degree-of-separation '( + ("Priya" "Rami") + ("Kaito" "Elif")) + "Priya" "Kaito"))) + + +(ert-deftest complex-graph-cousins () + (should (= 9 (degree-of-separation '( + ("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")) + "Dimitri" "Fabio")))) + + +(ert-deftest complex-graph-no-shortcut-far-removed-nephew () + (should (= 14 (degree-of-separation '( + ("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")) + "Lucia" "Jun")))) + + +(ert-deftest + complex-graph-some-shortcuts-cross-down-and-cross-up-cousins-several-times-removed-with-unrelated-family-tree + () + (should (= 12 (degree-of-separation '( + ("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")) + "Wyatt" "Xia")))) + + +(provide 'relative-distance-test) +;;; relative-distance-test.el ends here diff --git a/exercises/practice/relative-distance/relative-distance.el b/exercises/practice/relative-distance/relative-distance.el new file mode 100644 index 00000000..ce6bd310 --- /dev/null +++ b/exercises/practice/relative-distance/relative-distance.el @@ -0,0 +1,13 @@ +;;; relative-distance.el --- Relative Distance (exercism) -*- lexical-binding: t; -*- + +;;; Commentary: + +;;; Code: + + +(defun degree-of-separation (family-tree person-a person-b) + (error "Delete this S-Expression and write your own implementation")) + + +(provide 'relative-distance) +;;; relative-distance.el ends here