DEV Community is a community of 793,259 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Advent of code day 5; finally a fun one!

I really enjoyed this one. It may also be because I was able to do it at a decent time in the day instead of near midnight...

This exercise allows me to show the advantages of 42 multiple 'for-in'; allowing to iterate on multiple collections at ones as if we where using a functional zip.

The core to the solution is to expand the line in a list of points.
I have found a couple of difficulties: at the start I did not understood that there was supposed to be diagonal lines, so my code originally looked like this:

method Point.List expand() = {
(x1,y1)=\p1 //extract coordinates from points
(x2,y2)=\p2
if x1==x2 return \()(
for y in Range(y1.min(y2) to=y2.max(y1)+1I) \add(\(x=x1,y=y)))
X[y1==y2] return \()(
for x in Range(x1.min(x2) to=x2.max(x1)+1I) \add(\(x=x,y=y1)))
}

I was saved by my assertion! 'X[y1==y2]': checking that my assumption was right: lines are either vertical or horizontal.
This is not the case, there are other kinds of lines and we just have to ignore them.
So the code became

method Point.List expand() = {
(x1,y1)=\p1 //extract coordinates from points
(x2,y2)=\p2
if x1==x2 return \()(
for y in Range(y1.min(y2) to=y2.max(y1)+1I) \add(\(x=x1,y=y)))
if y1==y2 return \()(
for x in Range(x1.min(x2) to=x2.max(x1)+1I) \add(\(x=x,y=y1)))
return \()
}

and this was passing part 1.
For part 2, I made a method to avoid repeating the Range-max pattern all over, and also to reverse the range on need to expand the diagonal lines in the right way; The final code is as follows:

Point = Data.AddList:Data:{I x, I y }
Point p1, Point p2
class method This (S that) = (
s = that.replace(S" -> " with=S",").split(S",")
ns = I.List()(for n in s \add(\(string=n)))
This(
p1=Point(x=ns.val(0I) y=ns.val(1I)),
p2=Point(x=ns.val(2I) y=ns.val(3I))
)
)
R = {class method I.List (I that, I to)=(
if that<to \()(for i in Range(that to=to+1I)\add(i))
else     \()(for i in Range(to to=that+1I).reverse() \add(i))
)}
method Point.List expand() = {
(x1,y1)=\p1
(x2,y2)=\p2
if x1==x2 return \()(for y in R(y1 to=y2) \add(\(x=x1,y=y)))
if y1==y2 return \()(for x in R(x1 to=x2) \add(\(x=x,y=y1)))
return \()(for x in R(x1,to=x2), y in R(y1,to=y2)
}
}
PMap = Collection.map(key=Point,val=I)
Main = (
fs = Fs.Real.#\$of()
lines = Line.List()(for s in input.split(S.nl()) \add(\(s)))
map = PMap()
for l in lines, for p in l.expand() (
opt = map.val(key=p)
if opt map.put(key=p, val=opt.val()+1I)
else map.put(key=p,val=1I)
)