Skip to content

Commit 1ddcc44

Browse files
authored
[New Exercise]: Piecing It Together (#425)
Add exercise `piecing it together`
1 parent 9598e47 commit 1ddcc44

File tree

8 files changed

+414
-0
lines changed

8 files changed

+414
-0
lines changed

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,6 +1111,14 @@
11111111
"prerequisites": [],
11121112
"difficulty": 5
11131113
},
1114+
{
1115+
"slug": "piecing-it-together",
1116+
"name": "Piecing It Together",
1117+
"uuid": "23b80d70-283c-44be-a360-8a23a42773f4",
1118+
"practices": [],
1119+
"prerequisites": [],
1120+
"difficulty": 5
1121+
},
11141122
{
11151123
"slug": "rectangles",
11161124
"name": "Rectangles",
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Instructions
2+
3+
Given partial information about a jigsaw puzzle, add the missing pieces.
4+
5+
If the information is insufficient to complete the details, or if given parts are in contradiction, the user should be notified.
6+
7+
The full information about the jigsaw puzzle contains the following parts:
8+
9+
- `pieces`: Total number of pieces
10+
- `border`: Number of border pieces
11+
- `inside`: Number of inside (non-border) pieces
12+
- `rows`: Number of rows
13+
- `columns`: Number of columns
14+
- `aspectRatio`: Aspect ratio of columns to rows
15+
- `format`: Puzzle format, which can be `portrait`, `square`, or `landscape`
16+
17+
For this exercise, you may assume square pieces, so that the format can be derived from the aspect ratio:
18+
19+
- If the aspect ratio is less than 1, it's `portrait`
20+
- If it is equal to 1, it's `square`
21+
- If it is greater than 1, it's `landscape`
22+
23+
## Three examples
24+
25+
### Portrait
26+
27+
A portrait jigsaw puzzle with 6 pieces, all of which are border pieces and none are inside pieces. It has 3 rows and 2 columns. The aspect ratio is 1.5 (3/2).
28+
29+
![A 2 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-2x3.svg)
30+
31+
### Square
32+
33+
A square jigsaw puzzle with 9 pieces, all of which are border pieces except for the one in the center, which is an inside piece. It has 3 rows and 3 columns. The aspect ratio is 1 (3/3).
34+
35+
![A 3 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-3x3.svg)
36+
37+
### Landscape
38+
39+
A landscape jigsaw puzzle with 12 pieces, 10 of which are border pieces and 2 are inside pieces. It has 3 rows and 4 columns. The aspect ratio is 1.333333... (4/3).
40+
41+
![A 4 by 3 jigsaw puzzle](https://assets.exercism.org/images/exercises/piecing-it-together/jigsaw-puzzle-4x3.svg)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Introduction
2+
3+
Your best friend has started collecting jigsaw puzzles and wants to build a detailed catalog of their collection — recording information such as the total number of pieces, the number of rows and columns, the number of border and inside pieces, aspect ratio, and puzzle format.
4+
Even with your powers combined, it takes multiple hours to solve a single jigsaw puzzle with one thousand pieces — and then you still need to count the rows and columns and calculate the remaining numbers manually.
5+
The even larger puzzles with thousands of pieces look quite daunting now.
6+
"There has to be a better way!" you exclaim and sit down in front of your computer to solve the problem.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
<#
2+
.SYNOPSIS
3+
Given partial information about a jigsaw puzzle, add the missing pieces.
4+
5+
.DESCRIPTION
6+
Calculate properties of a jigsaw puzzle with given information if possible.
7+
If not possible due to insufficient or incorrect information, the user should be notified.
8+
Read instructions for more information and example.
9+
#>
10+
11+
enum Format {
12+
Unknown
13+
Portrait
14+
Square
15+
Landscape
16+
}
17+
18+
class JigsawPuzzle {
19+
[int]$Pieces
20+
[int]$Border
21+
[int]$Inside
22+
[int]$Rows
23+
[int]$Columns
24+
[double]$AspectRatio
25+
[Format]$Format
26+
27+
GetData([PSCustomObject]$partialData)
28+
{
29+
if ($partialData.PSObject.Properties.Name -contains "AspectRatio") {
30+
$this.ProcessDataFromRatio($partialData)
31+
} elseif ($partialData.PSObject.Properties.Name -contains "Format") {
32+
$this.ProcessDataFromFormat($partialData)
33+
} else {
34+
throw "insufficient data"
35+
}
36+
}
37+
38+
FillFromRowsAndColumns()
39+
{
40+
$this.Pieces = $this.Rows * $this.Columns
41+
$this.Border = 2*$this.Rows + 2*$this.Columns - 4
42+
$this.Inside = $this.Pieces - $this.Border
43+
$this.AspectRatio = $this.Columns / $this.Rows
44+
}
45+
46+
[int[]]FindRowsAndColumn() {
47+
$a, $b = 0, 0
48+
for ($i = 1; $i -le [Math]::Sqrt($this.Pieces); $i++) {
49+
if (($i * $i * $this.AspectRatio) -eq $this.Pieces) {
50+
$a, $b = $i, ($i * $this.AspectRatio)
51+
break
52+
}
53+
}
54+
return $this.Format -eq [Format]::Landscape ? [Math]::Min($a,$b), [Math]::Max($a,$b)
55+
: [Math]::Max($a,$b), [Math]::Min($a,$b)
56+
}
57+
58+
ProcessDataFromRatio([PSCustomObject]$partialData)
59+
{
60+
$properties = $partialData.PSObject.Properties.Name
61+
$this.AspectRatio = $partialData.AspectRatio
62+
$this.Format = switch ($true) {
63+
($partialData.AspectRatio -gt 1) { [Format]::Landscape }
64+
($partialData.AspectRatio -lt 1) { [Format]::Portrait }
65+
Default {[Format]::Square}
66+
}
67+
68+
if ($properties -contains "Pieces") {
69+
$this.Pieces = $partialData.Pieces
70+
$this.Rows, $this.Columns = $this.FindRowsAndColumn()
71+
} elseif ($properties -contains "Border") {
72+
$this.Border = $partialData.Border
73+
$this.Rows = ($this.Border + 4) / (2 * (1 + $this.AspectRatio))
74+
$this.Columns = $this.Rows * $this.AspectRatio
75+
} elseif ($properties -contains "Inside") {
76+
$this.Inside = $partialData.Inside
77+
$r = $this.AspectRatio
78+
$i = $this.Inside
79+
80+
$discriminant = [Math]::Pow((2 * $r + 2), 2) - (16 * $r) + (4 * $r * $i)
81+
$rows1 = ((2 * $r + 2) + [Math]::Sqrt($discriminant)) / (2 * $r)
82+
$rows2 = ((2 * $r + 2) - [Math]::Sqrt($discriminant)) / (2 * $r)
83+
84+
$this.Rows = @($rows1, $rows2) | Where-Object { $_ -gt 0 -and [Math]::Round($_) -eq $_ } | Select-Object -First 1
85+
$this.Columns = $r * $this.Rows
86+
} elseif ($properties -contains "Rows") {
87+
$this.Rows = $partialData.Rows
88+
$this.Columns = $this.AspectRatio * $this.Rows
89+
} elseif ($properties -contains "Columns") {
90+
$this.Columns = $partialData.Columns
91+
$this.Rows = $this.Columns / $this.AspectRatio
92+
}
93+
$this.FillFromRowsAndColumns()
94+
}
95+
96+
ProcessDataFromFormat([PSCustomObject]$partialData)
97+
{
98+
$this.Format = $partialData.Format
99+
$properties = $partialData.PSObject.Properties.Name
100+
if ($this.Format -eq [Format]::Square) {
101+
$this.AspectRatio = 1.0
102+
if ($properties -contains "Pieces") {
103+
$this.Rows = $this.Columns = [Math]::Sqrt($partialData.Pieces)
104+
} elseif ($properties -contains "Rows") {
105+
$this.Rows = $partialData.Rows
106+
$this.Columns = $this.Rows
107+
} elseif ($properties -contains "Columns") {
108+
$this.Columns = $partialData.Columns
109+
$this.Rows = $this.Columns
110+
} elseif ($properties -contains "Border") {
111+
$this.Rows = $this.Columns = ($partialData.Border + 4) / 4
112+
} elseif ($properties -contains "Inside") {
113+
$this.Rows = $this.Columns = [Math]::Sqrt($partialData.Inside) + 2
114+
} else {
115+
throw "bad data"
116+
}
117+
} else {
118+
$otherProperties = ($properties | Where-Object { $_ -ne "Format"}).Count
119+
if ($otherProperties -lt 2) {
120+
throw "insufficient data"
121+
}
122+
$pairs = $this.GenerateRowsAndColumnsFromFormat([PSCustomObject]$partialData)
123+
foreach ($pair in $pairs) {
124+
$tempPieces = $pair[0] * $pair[1]
125+
$tempBorder = 2*$pair[0] + 2*$pair[1] - 4
126+
if ($tempPieces -eq $partialData.Pieces -and $tempBorder -eq $partialData.Border) {
127+
if ($partialData.Format -eq [Format]::Landscape) {
128+
$this.Rows = [Math]::Min($pair[0], $pair[1])
129+
$this.Columns = [Math]::Max($pair[0], $pair[1])
130+
} else {
131+
$this.Rows = [Math]::Max($pair[0], $pair[1])
132+
$this.Columns = [Math]::Min($pair[0], $pair[1])
133+
}
134+
}
135+
}
136+
137+
}
138+
$this.FillFromRowsAndColumns()
139+
$this.ValidateGivenData($partialData)
140+
}
141+
142+
[object[]]GenerateRowsAndColumnsFromFormat([PSCustomObject]$partialData) {
143+
$properties = $partialData.PSObject.Properties.Name
144+
$pairs = @()
145+
if ($properties -contains "Pieces") {
146+
$this.Pieces = $partialData.Pieces
147+
for ($r = 1; $r -le [Math]::Sqrt($this.Pieces); $r++) {
148+
for ($c = $r; $c -le $this.Pieces; $c++) {
149+
if ($r * $c -eq $this.Pieces) {
150+
$pairs += ,@($c, $r)
151+
}
152+
}
153+
}
154+
}elseif ($properties -contains "Border") {
155+
$this.Border = $partialData.Border
156+
$sum = ($this.Border + 4) / 2
157+
for ($c = 1; $c -lt ($sum - 1); $c++) {
158+
$pairs += ,@($c, ($sum - $c))
159+
}
160+
}
161+
return $pairs
162+
}
163+
164+
ValidateGivenData([PSCustomObject]$partialData)
165+
{
166+
foreach ($property in $partialData.psobject.Properties) {
167+
if ($property.Value -ne $this.PSObject.Properties[$property.Name].Value) {
168+
throw "contradictory data"
169+
}
170+
}
171+
}
172+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"glaxxie"
4+
],
5+
"files": {
6+
"solution": [
7+
"PiecingItTogether.ps1"
8+
],
9+
"test": [
10+
"PiecingItTogether.tests.ps1"
11+
],
12+
"example": [
13+
".meta/PiecingItTogether.example.ps1"
14+
]
15+
},
16+
"blurb": "Fill in missing jigsaw puzzle details from partial data",
17+
"source": "atk just started another 1000-pieces jigsaw puzzle when this idea hit him",
18+
"source_url": "https://github.com/exercism/problem-specifications/pull/2554"
19+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# This is an auto-generated file.
2+
#
3+
# Regenerating this file via `configlet sync` will:
4+
# - Recreate every `description` key/value pair
5+
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
6+
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
7+
# - Preserve any other key/value pair
8+
#
9+
# As user-added comments (using the # character) will be removed when this file
10+
# is regenerated, comments can be added via a `comment` key.
11+
12+
[ad626f23-09a2-4f5f-ba22-eec0671fa2a9]
13+
description = "1000 pieces puzzle with 1.6 aspect ratio"
14+
15+
[3e0c5919-3561-42f5-b9ed-26d70c20214e]
16+
description = "square puzzle with 32 rows"
17+
18+
[1126f160-b094-4dc2-bf37-13e36e394867]
19+
description = "400 pieces square puzzle with only inside pieces and aspect ratio"
20+
21+
[a9743178-5642-4cc0-8fdb-00d6b031c3a0]
22+
description = "1500 pieces landscape puzzle with 30 rows and 1.6 aspect ratio"
23+
24+
[f6378369-989c-497f-a6e2-f30b1fa76cba]
25+
description = "300 pieces portrait puzzle with 70 border pieces"
26+
27+
[f53f82ba-5663-4c7e-9e86-57fdbb3e53d2]
28+
description = "puzzle with insufficient data"
29+
30+
[a3d5c31a-cc74-44bf-b4fc-9e4d65f1ac1a]
31+
description = "puzzle with contradictory data"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<#
2+
.SYNOPSIS
3+
Given partial information about a jigsaw puzzle, add the missing pieces.
4+
5+
.DESCRIPTION
6+
Calculate properties of a jigsaw puzzle with given information if possible.
7+
If not possible due to insufficient or incorrect information, the user should be notified.
8+
Read instructions for more information and example.
9+
#>
10+
11+
enum Format {
12+
Portrait
13+
Square
14+
Landscape
15+
}
16+
17+
class JigsawPuzzle {
18+
[int]$Pieces
19+
[int]$Border
20+
[int]$Inside
21+
[int]$Rows
22+
[int]$Columns
23+
[double]$AspectRatio
24+
[Format]$Format
25+
26+
GetData([PSCustomObject]$partialData)
27+
{
28+
Throw "Please implement this function"
29+
}
30+
}

0 commit comments

Comments
 (0)