Skip to content

Commit 438bc2b

Browse files
authored
Add new exercise: prism (#339)
* Add new exercise: prism * Drop a space after the `#` * Quote `"true"` * Put the ID first * Sort prisms by id
1 parent 8c79bc3 commit 438bc2b

10 files changed

Lines changed: 1135 additions & 0 deletions

File tree

config.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,14 @@
540540
"prerequisites": [],
541541
"difficulty": 4
542542
},
543+
{
544+
"slug": "prism",
545+
"name": "Prism",
546+
"uuid": "865a0901-4bdf-44c5-a0e9-5b87c104cb9d",
547+
"practices": [],
548+
"prerequisites": [],
549+
"difficulty": 4
550+
},
543551
{
544552
"slug": "roman-numerals",
545553
"name": "Roman Numerals",
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Instructions
2+
3+
Before activating the laser array, you must predict the exact order in which crystals will be hit, identified by their sample IDs.
4+
5+
## Example Test Case
6+
7+
Consider this crystal array configuration:
8+
9+
```json
10+
{
11+
"start": { "x": 0, "y": 0, "angle": 0 },
12+
"prisms": [
13+
{ "id": 3, "x": 30, "y": 10, "angle": 45 },
14+
{ "id": 1, "x": 10, "y": 10, "angle": -90 },
15+
{ "id": 2, "x": 10, "y": 0, "angle": 90 },
16+
{ "id": 4, "x": 20, "y": 0, "angle": 0 }
17+
]
18+
}
19+
```
20+
21+
## What's Happening
22+
23+
The laser starts at the origin `(0, 0)` and fires horizontally to the right at angle 0°.
24+
Here's the step-by-step beam path:
25+
26+
**Step 1**: The beam travels along the x-axis (y = 0) and first encounters **Crystal #2** at position `(10, 0)`.
27+
This crystal has a refraction angle of 90°, which means it bends the beam perpendicular to its current path.
28+
The beam, originally traveling at 0°, is now redirected to 90° (straight up).
29+
30+
**Step 2**: The beam now travels vertically upward from position `(10, 0)` and strikes **Crystal #1** at position `(10, 10)`.
31+
This crystal has a refraction angle of -90°, bending the beam by -90° relative to its current direction.
32+
The beam was traveling at 90°, so after refraction it's now at 0° (90° + (-90°) = 0°), traveling horizontally to the right again.
33+
34+
**Step 3**: From position `(10, 10)`, the beam travels horizontally and encounters **Crystal #3** at position `(30, 10)`.
35+
This crystal refracts the beam by 45°, changing its direction to 45°.
36+
The beam continues into empty space beyond the array.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Introduction
2+
3+
You're a researcher at **PRISM** (Precariously Redirected Illumination Safety Management), working with a precision laser calibration system that tests experimental crystal prisms.
4+
These crystals are being developed for next-generation optical computers, and each one has unique refractive properties based on its molecular structure.
5+
The lab's laser system can damage crystals if they receive unexpected illumination, so precise path prediction is critical.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"authors": [
3+
"IsaacG"
4+
],
5+
"files": {
6+
"solution": [
7+
"prism.awk"
8+
],
9+
"test": [
10+
"test-prism.bats"
11+
],
12+
"example": [
13+
".meta/example.awk"
14+
]
15+
},
16+
"blurb": "Calculate the path of a laser through reflective prisms.",
17+
"source": "FraSanga",
18+
"source_url": "https://github.com/exercism/problem-specifications/pull/2625"
19+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# The first line of input is the starting x, y and angle.
2+
# Any remaining input is the prisms: x, y, angle and ID.
3+
# The program is expected to print out prism IDs, space separated on one line.
4+
5+
function abs(n) {
6+
return (n > 0) ? n : -n
7+
}
8+
9+
# Starting position
10+
NR == 1 { x = $1; y = $2; a = $3; }
11+
12+
# Prism positions
13+
NR > 1 { prisms[$1]["x"] = $2; prisms[$1]["y"] = $3; prisms[$1]["a"] = $4; }
14+
15+
# Compute the path
16+
END {
17+
# Loop for hits until no more are found.
18+
deg_to_rad = 3.141592 / 180
19+
do {
20+
found = 0
21+
distance = 0
22+
for (id in prisms) {
23+
# Do not count a prism we just hit.
24+
if (prisms[id]["x"] == x && prisms[id]["y"] == y) {
25+
continue
26+
}
27+
dx = prisms[id]["x"] - x
28+
dy = prisms[id]["y"] - y
29+
r = sqrt(dx * dx + dy * dy)
30+
# How close did we get to the prism?
31+
hx = abs(cos(a * deg_to_rad) * r - dx)
32+
hy = abs(sin(a * deg_to_rad) * r - dy)
33+
if (hx < 0.1 && hy < 0.1) {
34+
# Track the closest prism along the laser path
35+
if (!found || r < distance) {
36+
fid = id
37+
distance = r
38+
}
39+
found = 1
40+
}
41+
}
42+
if (found) {
43+
x = prisms[fid]["x"]
44+
y = prisms[fid]["y"]
45+
a += prisms[fid]["a"]
46+
out[count++] = fid
47+
}
48+
} while (found == 1)
49+
50+
# Print the prisms we hit, space separated IDs
51+
if (count) {
52+
res = out[0]
53+
for (j = 1; j < count; j++) {
54+
res = res " " out[j]
55+
}
56+
print res
57+
}
58+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
3+
if (( BASH_VERSINFO[0] < 4 )); then
4+
echo "[Failure] This script requires bash version 4+" >&2
5+
exit 1
6+
fi
7+
8+
if (( $# != 1 )) || [[ ${1##*/} != canonical-data.json ]]; then
9+
echo "Usage: $0 <canonical-data.json for prism>"
10+
exit 1
11+
fi
12+
13+
printf '%s\n' '#!/usr/bin/env bats' 'load bats-extra'
14+
15+
jq -r '
16+
.cases |
17+
# Add an index to each case, similar to Python enumerate()
18+
[foreach .[] as $case ({"index": 0, "case": ""}; {"index": .index + 1, "case": $case})] |
19+
map(
20+
[
21+
"",
22+
"@test \"\(.case.description)\" {",
23+
# Do not skip the first test
24+
if .index == 1 then
25+
" # [[ $BATS_RUN_SKIPPED == \"true\" ]] || skip"
26+
else
27+
" [[ $BATS_RUN_SKIPPED == \"true\" ]] || skip"
28+
end,
29+
" run gawk -f prism.awk <<EOF",
30+
# Start position
31+
(.case.input.start | "\(.x) \(.y) \(.angle)"),
32+
# Prism list
33+
if (.case.input.prisms | length > 0) then
34+
(.case.input.prisms | sort_by(.id) | map("\(.id) \(.x) \(.y) \(.angle)") | join("\n"))
35+
else
36+
empty
37+
end,
38+
"EOF",
39+
" assert_success",
40+
" assert_output \"" + (.case.expected.sequence | join(" ")) + "\"",
41+
"}"
42+
] |
43+
join("\n")
44+
)
45+
| join("\n")' "$1"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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+
[ec65d3b3-f7bf-4015-8156-0609c141c4c4]
13+
description = "zero prisms"
14+
15+
[ec0ca17c-0c5f-44fb-89ba-b76395bdaf1c]
16+
description = "one prism one hit"
17+
18+
[0db955f2-0a27-4c82-ba67-197bd6202069]
19+
description = "one prism zero hits"
20+
21+
[8d92485b-ebc0-4ee9-9b88-cdddb16b52da]
22+
description = "going up zero hits"
23+
24+
[78295b3c-7438-492d-8010-9c63f5c223d7]
25+
description = "going down zero hits"
26+
27+
[acc723ea-597b-4a50-8d1b-b980fe867d4c]
28+
description = "going left zero hits"
29+
30+
[3f19b9df-9eaa-4f18-a2db-76132f466d17]
31+
description = "negative angle"
32+
33+
[96dacffb-d821-4cdf-aed8-f152ce063195]
34+
description = "large angle"
35+
36+
[513a7caa-957f-4c5d-9820-076842de113c]
37+
description = "upward refraction two hits"
38+
39+
[d452b7c7-9761-4ea9-81a9-2de1d73eb9ef]
40+
description = "downward refraction two hits"
41+
42+
[be1a2167-bf4c-4834-acc9-e4d68e1a0203]
43+
description = "same prism twice"
44+
45+
[df5a60dd-7c7d-4937-ac4f-c832dae79e2e]
46+
description = "simple path"
47+
48+
[8d9a3cc8-e846-4a3b-a137-4bfc4aa70bd1]
49+
description = "multiple prisms floating point precision"
50+
51+
[e077fc91-4e4a-46b3-a0f5-0ba00321da56]
52+
description = "complex path with multiple prisms floating point precision"

0 commit comments

Comments
 (0)