Skip to content

Commit

Permalink
[New Exercise]: POV
Browse files Browse the repository at this point in the history
  • Loading branch information
glaxxie committed Jan 25, 2024
1 parent b30e920 commit ba381dc
Show file tree
Hide file tree
Showing 7 changed files with 478 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -1088,6 +1088,14 @@
"prerequisites": [],
"difficulty": 8
},
{
"slug": "pov",
"name": "Pov",
"uuid": "a66c762e-77fa-413d-a84b-03eb099700c2",
"practices": [],
"prerequisites": [],
"difficulty": 10
},
{
"slug": "scale-generator",
"name": "Scale Generator",
Expand Down
41 changes: 41 additions & 0 deletions exercises/practice/pov/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Instructions

Reparent a tree on a selected node.

A [tree][wiki-tree] is a special type of [graph][wiki-graph] where all nodes are connected but there are no cycles.
That means, there is exactly one path to get from one node to another for any pair of nodes.

This exercise is all about re-orientating a tree to see things from a different point of view.
For example family trees are usually presented from the ancestor's perspective:

```text
+------0------+
| | |
+-1-+ +-2-+ +-3-+
| | | | | |
4 5 6 7 8 9
```

But there is no inherent direction in a tree.
The same information can be presented from the perspective of any other node in the tree, by pulling it up to the root and dragging its relationships along with it.
So the same tree from 6's perspective would look like:

```text
6
|
+-----2-----+
| |
7 +-----0-----+
| |
+-1-+ +-3-+
| | | |
4 5 8 9
```

This lets us more simply describe the paths between two nodes.
So for example the path from 6-9 (which in the first tree goes up to the root and then down to a different leaf node) can be seen to follow the path 6-2-0-3-9.

This exercise involves taking an input tree and re-orientating it from the point of view of one of the nodes.

[wiki-graph]: https://en.wikipedia.org/wiki/Tree_(graph_theory)
[wiki-tree]: https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)
115 changes: 115 additions & 0 deletions exercises/practice/pov/.meta/Pov.example.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<#
.SYNOPSIS
Given a tree, reorientate it based on a selected node.
.DESCRIPTION
A tree is a special type of graph where all nodes are connected but there are no cycles.
That means, there is exactly one path to get from one node to another for any pair of nodes.
Implement a class to represent the Tree along with these methods:
- FromPov: accept a value of a node, return a new Tree object orientated based on that node.
Throw error if the value/node does't exist in the tree.
- Path: accept the source and destination values of two nodes, return an array of string represent the path from source to destination.
Throw error if the path doesn't exist.
- Equals: equality method to compare trees for the test suite.
.EXAMPLE
Read instructions for visual example.
#>

Class Tree {
[string] $Value
[Tree[]] $Children
[hashtable] hidden $Graph = @{}

Tree([string] $value) {
$this.Value = $value
$this.Children = @()
}

Tree([string] $value, [Tree[]] $Children) {
$this.Value = $value
$this.Children = $Children
}

[void] hidden CreateGraph($tree, $parent) {
<#
.DESCRIPTION
Helper function, creating a hashtable representing the graph for the original tree.
#>
$this.Graph[$tree.Value] = @{
Parent = $parent
Children = @($tree.Children | ForEach-Object {$_.Value})
}
foreach ($child in $tree.Children) {
$this.CreateGraph($child, $tree.Value)
}
}

[void] hidden PovGraph([string]$root) {
<#
.DESCRIPTION
Helper function, representing the graph for the tree with a new root.
#>
if ($this.Graph.Count -eq 0) {$this.CreateGraph($this, $null)}
if (-not $this.Graph.ContainsKey($root)) {Throw "Tree could not be reoriented"}
if ($null -eq $this.Graph[$root].Parent) {return}

$curParent = $this.Graph[$root].Parent
$this.Graph[$root].Parent = $null
$this.Graph[$root].Children += @($curParent)

while ($null -ne $curParent) {
$tempParent = $this.Graph[$curParent].Parent
$this.Graph[$curParent].Parent = $root
$this.Graph[$curParent].Children = ($this.Graph[$curParent].Children -ne $root) + ($null -eq $tempParent ? @() : @($tempParent) )
$root = $curParent
$curParent = $tempParent
}
}

[Tree] FromPov([string] $node) {
if ($this.Graph.Count -eq 0) {$this.PovGraph($node)}

return [Tree]::new(
$node,
@( $this.Graph[$node].Children | ForEach-Object {$this.FromPov($_)})
)
}

[string[]] Path([string] $from, [string] $to) {
if ($this.Graph.Count -eq 0) {$this.CreateGraph($this, $null)}
if (-not $this.Graph.ContainsKey($from) -or -not $this.Graph.ContainsKey($to)) {
Throw "No path found"
}
$this.PovGraph($from)
$path = @()
$parent = $to
while ($parent) {
$path+= $parent
$parent = $this.Graph[$parent].Parent
}
[array]::Reverse($path)
return $path
}

[bool] Equals($other) {
if($this.Value -ne $other.Value) {
return $false
}

if($this.Children.Count -ne $other.Children.Count) {
return $false
}

$sortedOwnChildren = $this.Children | Sort-Object { $_.Value }
$sortedTheirChildren = $other.Children | Sort-Object { $_.Value }

for ($i = 0; $i -lt $sortedOwnChildren.Count; $i++) {
if ($sortedOwnChildren[$i] -ne $sortedTheirChildren[$i]) {
return $false
}
}
return $true
}
}
19 changes: 19 additions & 0 deletions exercises/practice/pov/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"glaxxie"
],
"files": {
"solution": [
"Pov.ps1"
],
"test": [
"Pov.tests.ps1"
],
"example": [
".meta/Pov.example.ps1"
]
},
"blurb": "Reparent a graph on a selected node.",
"source": "Adaptation of exercise from 4clojure",
"source_url": "https://www.4clojure.com/"
}
55 changes: 55 additions & 0 deletions exercises/practice/pov/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# 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.

[1b3cd134-49ad-4a7d-8376-7087b7e70792]
description = "Reroot a tree so that its root is the specified node. -> Results in the same tree if the input tree is a singleton"

[0778c745-0636-40de-9edd-25a8f40426f6]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with a parent and one sibling"

[fdfdef0a-4472-4248-8bcf-19cf33f9c06e]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with a parent and many siblings"

[cbcf52db-8667-43d8-a766-5d80cb41b4bb]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a tree with new root deeply nested in tree"

[e27fa4fa-648d-44cd-90af-d64a13d95e06]
description = "Reroot a tree so that its root is the specified node. -> Moves children of the new root to same level as former parent"

[09236c7f-7c83-42cc-87a1-25afa60454a3]
description = "Reroot a tree so that its root is the specified node. -> Can reroot a complex tree with cousins"

[f41d5eeb-8973-448f-a3b0-cc1e019a4193]
description = "Reroot a tree so that its root is the specified node. -> Errors if target does not exist in a singleton tree"

[9dc0a8b3-df02-4267-9a41-693b6aff75e7]
description = "Reroot a tree so that its root is the specified node. -> Errors if target does not exist in a large tree"

[02d1f1d9-428d-4395-b026-2db35ffa8f0a]
description = "Given two nodes, find the path between them -> Can find path to parent"

[d0002674-fcfb-4cdc-9efa-bfc54e3c31b5]
description = "Given two nodes, find the path between them -> Can find path to sibling"

[c9877cd1-0a69-40d4-b362-725763a5c38f]
description = "Given two nodes, find the path between them -> Can find path to cousin"

[9fb17a82-2c14-4261-baa3-2f3f234ffa03]
description = "Given two nodes, find the path between them -> Can find path not involving root"

[5124ed49-7845-46ad-bc32-97d5ac7451b2]
description = "Given two nodes, find the path between them -> Can find path from nodes other than x"

[f52a183c-25cc-4c87-9fc9-0e7f81a5725c]
description = "Given two nodes, find the path between them -> Errors if destination does not exist"

[f4fe18b9-b4a2-4bd5-a694-e179155c2149]
description = "Given two nodes, find the path between them -> Errors if source does not exist"
36 changes: 36 additions & 0 deletions exercises/practice/pov/Pov.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<#
.SYNOPSIS
Given a tree, reorientate it based on a selected node.
.DESCRIPTION
A tree is a special type of graph where all nodes are connected but there are no cycles.
That means, there is exactly one path to get from one node to another for any pair of nodes.
Implement a class to represent the Tree along with these methods:
- FromPov: accept a value of a node, return a new Tree object orientated based on that node.
Throw error if the value/node does't exist in the tree.
- Path: accept the source and destination values of two nodes, return an array of string represent the path from source to destination.
Throw error if the path doesn't exist.
- Equals: equality method to compare trees for the test suite.
.EXAMPLE
Read instructions for visual example.
#>

Class Tree {
Tree([string] $value, [Tree[]] $Children) {
Throw "Please implement this class"
}

[Tree] FromPov([string] $node) {
Throw "Please implement this function"
}

[string[]] Path([string] $from, [string] $to) {
Throw "Please implement this function"
}

[bool] Equals($other) {
Throw "Please implement this function"
}
}
Loading

0 comments on commit ba381dc

Please sign in to comment.