New Exercise Proposal: Prism

Hello everyone, I’m back with a new exercise.
This time, I’m proposing a dish based on… angles!

From my experience on this platform, there are many exercises that cover the basics of programming. I’d like to add a touch of game dev for new users.

The challenge is to calculate how many prisms are irradiated by a laser beam.
Starting from a known position (x, y) and a firing angle (α), the laser will hit the nearest prism along the calculated axis.
These prisms have the ability to retransmit the beam with a certain angle of refraction (γ), which is also given as input.
The difficulty of the exercise lies solely in finding the next prism, calculating the angle of impact (β) and finally the angle of retransmission (γ + β) .

Here is a small illustrative drawing I made myself:

For now, this is the basic idea. I am always open to advice and criticism.

To make it more difficult, it could become an exercise in finding the right angle to hit as many prisms as possible. I also thought about adding different types of prisms, but that would defeat the purpose of this exercise, which is to work with angles!

Let me know what you think!
Ciao

2 Likes

Could you give an example or two of the input and output?

{
  "input": {
    "start": { "x": 0, "y": 0, "angle": 0 },
    "prisms": [
      { "x": 10, "y": 0, "angle": 90 },       // P1
      { "x": 20, "y": 0, "angle": 0 },        // P2
      { "x": 10, "y": 10, "angle": -90 },     // P3
      { "x": 30, "y": 10, "angle": 45 }       // P4
    ]
  },
  "expected": {
    "total": 3,
  }
}

P1 → P3 → P4
Angles are treated as in mathematics:
taking the graph of a circle, zero is positioned at the far right on the x-axis. With a positive angle, we move counterclockwise; with a negative angle, we move clockwise.

1 Like

If each prism included a unique ID, the return value could be a sequence of IDs which would provide more information.

This sounds intriguing to me. There’s some geometry involved here but it’s not very much. Coding wise this shouldn’t be overly difficult; I would imagine this would be about selecting the min element from a collection that meets some criteria then updating that condition until no element matches.

I think it could be an interesting addition. A story/introducing would also be needed.

Introduction

You’re a researcher at PRISM (Precariously Redirected Illumination Safety Management), working with a precision laser calibration system that tests experimental crystal prisms. These crystals are being developed for next-generation optical computers, and each one has unique refractive properties based on its molecular structure. The lab’s laser system can damage crystals if they receive unexpected illumination, so precise path prediction is critical.


Your Task

Before activating the laser array, you must predict:

  1. Total crystals illuminated: How many crystal samples will be exposed to the laser beam
  2. Illumination sequence: The exact order in which crystals will be hit, identified by their sample IDs

Example Test Case

Consider this crystal array configuration:

{
  "input": {
    "start": { "x": 0, "y": 0, "angle": 0 },
    "prisms": [
      { "id": 1, "x": 10, "y": 0, "angle": 90 },
      { "id": 2, "x": 20, "y": 0, "angle": 0 },
      { "id": 3, "x": 10, "y": 10, "angle": -90 },
      { "id": 4, "x": 30, "y": 10, "angle": 45 }
    ]
  },
  "expected": {
    "total": 3,
    "sequence": [1, 3, 4]
  }
}

What’s Happening

The laser starts at the origin (0, 0) and fires horizontally to the right at angle 0°. Here’s the step-by-step beam path:

Step 1: The beam travels along the x-axis (y = 0) and first encounters Crystal #1 at position (10, 0). This crystal has a refraction angle of 90°, which means it bends the beam perpendicular to its current path. The beam, originally traveling at 0°, is now redirected to 90° (straight up).

Step 2: The beam now travels vertically upward from position (10, 0) and strikes Crystal #3 at position (10, 10). This crystal has a refraction angle of -90°, bending the beam by -90° relative to its current direction. The beam was traveling at 90°, so after refraction it’s now at 0° (90° + (-90°) = 0°), traveling horizontally to the right again.

Step 3: From position (10, 10), the beam travels horizontally and encounters Crystal #4 at position (30, 10). This crystal refracts the beam by 45°, changing its direction to 45°. The beam continues into empty space beyond the array.


The only addition I would make would be to also take into account the total number of prisms affected, as one of them could be illuminated more than once.
Perhaps instead of the total, I would put a list of frequencies, but even as it is, I don’t mind it at all.

2 Likes

An ordered sequence of prisms affected would be able to handle the case of repeats.

This sounds good to me. Let’s see what other maintainers say.

I like the exercise. If you want to make it more challenging, you could make the crystals a solid, maybe a sphere with some radius. Then the student would need to account for any point along the path that intersects the solid. But the exercise is already fine as you have proposed.

Looks good. It has some affinity with Rectangles (find the next point moving along some line)

As much as I like the idea, it’s better to stick with 2D so as not to complicate the exercise too much.


That’s fine with me. Shall we add different types of prisms to spice things up a bit?
(Obviously, we’ll have to think carefully about this. I’d say a maximum of 3 or 4 types, no more.)


I would suggest waiting at least until the end of this/next week in case anyone wants to express their opinion on the matter.

I really appreciate your support, thank you all.

You’ve got the buy in of three maintainers if you want to start a PR.

I think the tests should start with simple cases: multiples of 90 degrees and a small number of prisms (2-4). But I fully support test cases with a larger number of prisms and/or different angles that aren’t a factor of 90! I don’t think there is a whole lot of value in adding too many prisms, though; that doesn’t really change too much about the exercise.

One thing which hasn’t been mentioned yet is loops. I would suggest not mentioning loops in the instructions and not introducing them to the tests.

I would also suggest the tests having a single expected value: the prism ID sequence, without a count.

Before proceeding with the PR, I need to clarify something with you.
We have a problem with decimal numbers!

If we want to reach any point in 2D with a directed line (knowing the starting position and any chosen angle), we need to use a tolerance because the trigonometric formulas are truncated (i.e., incorrect).

We can settle for using only points with integer (x,y) values and angles that can be represented with direction vectors [x,y], also integers.
This leaves us with another problem:
angles with [x,y] do not necessarily have their own representation in degrees without decimal places (e.g., [2,1] = 26.565…°), thus having other truncated values.

I am writing this to find out whether you prefer simple tests (with integer angles in degrees) or something more meticulous (vectors), taking these aspects into account.

this:

{
  "input": {
    "start": { "x": 0, "y": 0, "angle": 0 },
    "prisms": [
      { "id": 1, "x": 10, "y": 0, "angle": 90 },
      { "id": 2, "x": 20, "y": 0, "angle": 0 },
      { "id": 3, "x": 10, "y": 10, "angle": -90 },
      { "id": 4, "x": 30, "y": 10, "angle": 45 }
    ]
  },
  "expected": {
    "sequence": [1, 3, 4]
  }
}

would become:

{
  "input": {
    "start": { "x": 0, "y": 0, "angle": [1, 0] },
    "prisms": [
      { "id": 1, "x": 10, "y": 0, "angle": [0, 1] },
      { "id": 2, "x": 20, "y": 0, "angle": [1, 0] },
      { "id": 3, "x": 10, "y": 10, "angle": [0, -1] },
      { "id": 4, "x": 30, "y": 10, "angle": [1, 1] }
    ]
  },
  "expected": {
    "sequence": [1, 3, 4]
  }
}

It is almost impossible to avoid some floating-point imprecision in this problem because the equation for the laser’s path will likely use trigonometry.

Having 2D prisms, i.e., prisms with sides, would reduce this problem somehow if we are careful to not get too close to the edges.

We could define a shape for the prisms (circles, squares, whatever) and then each prism could be determined by a set of vertex points.

My personal thought is to stick to integers. The floating point scneario could be used to tag non-integer tests. Using integers and degrees does limit us to factors of 45 degrees and eliminates the need for sin or cosin.

To sum up, I could do:

  1. simple tests with multiple angles of 45°
  2. other tests with angles that can be represented as vectors of integers or not, marking them as “scenarios”: [“floating-point”]

Does that sound good?


The tests should apply to every type of language, regardless of the type of tolerance you want to use.
I will try to make the tests unambiguous to make life easier for those who will have to implement the code.
As soon as I can, I will open the draft PR.


If anything is unclear to me or to you, please let me know!

1 Like

Of course, but every language can implement a “set of vertex points”. This is a set of vertex points for a square:

[[0, 1], [2, 1], [0, 3], [2, 3]]

A list of four unique vertices.

And now, regardless of floating-point imprecision in any equation, passing through [1, 2] clearly hits the prism.

We might have a problem with the edges, but right in the middle of any side or large square (or any shape) is a clear hit regardless if your float returns 1.01 or 0.99.

I would suggest keeping things simple here and not diving into geometry too much. If prisms are integer coordinate points and angles are defined as n*45 or integer dx, dy, there’s no need for sin, cos and floats. Once you’re defining arbitrary polygons, things get very complicated very quickly.

If you do want to support arbitrary angles and floats, you could reduce the boundary problem a bit by declaring that prisms are, say, circles with a radius of 1 and ensure the tests are set up such that the laser either hits close to the center of the prism or does not get close to the prism (for some generous definitions of close).

I don’t have a strong opinion about the shape of the polygons, and I agree that we should make things simple. A unit circle is fine by me.

It’s just that we have very few exercises that deal with floating-point values and I can’t remember any that deals with angles or more complex geometry. Having an exercise about angles and not using trigonometry seems like a wasted opportunity.

But I’ll support the majority here. I approve the exercise either way.

Going full on with angles and floats is definitely an option, too. This just may limit what tracks could pick it up. Or the tests could be split into integer vs float cases and tracks can pick which tests to implement.

Here’s the piece I was missing. I didn’t remember that you could choose which tests to implement based on the type of track.


I think that’s the wisest choice here. I’ll try to do the tests with a tolerance of 1/1000 so that we have a margin of error of ± 0.001 degrees.
I’ll start like that and then we’ll see whether to be more strict or not.