Correct loop termination bedeviled me. A spoiler which isn’t really a spoiler is that I’m pretty sure parts 1 and 2 can call the same subprograms, but testing for different things. I didn’t implement it that way, though.
A trap in part 2 (depending on how you do things) can be that after repairing the smudge, there are one or two reflection lines!
There is even a kind warning about it in the text: “The old reflection line won’t necessarily continue being valid after the smudge is fixed.” but is formulated (as often) in a deceptive way. A better (too obvious, of course) hint would be instead “The old reflection line will perhaps continue being valid after the smudge is fixed.”
A smarter way for solving part 2 (that I didn’t program, but guessed later by browsing the subreddit) is not to repair anything but count the anomalies (zero for a perfect reflection like in part 1, one for the smudge of part 2).
I encountered the trap while debugging my part 2, and it threw me for a loop a while. I had written up a Validate
subprogram to fix the detected change and test it for symmetry. While debugging, I noticed that there were 2 lines of symmetry in some places, and I lost at least a half-hour trying to figure that out.
The way I solved part 2 was precisely by counting the number of anomalies, storing the location of the first, returning that if there were no others, and returning failure if there were two. So your “smarter way” is what I was hinting at in my spoiler.