DEV Community

Marco Servetto
Marco Servetto

Posted on

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)))
  }
Enter fullscreen mode Exit fullscreen mode

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 \()
  }
Enter fullscreen mode Exit fullscreen mode

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:

reuse [L42.is/AdamsTowel]
Fs = Load:{reuse[L42.is/FileSystem]}
Point = Data.AddList:Data:{I x, I y }
Line = Data.AddList:Data:{
  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) 
      \add(\(x=x,y=y)))
    }
  }
PMap = Collection.map(key=Point,val=I)
Main = (
  fs = Fs.Real.#$of()
  input = fs.read(\"input")
  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)
    )
  res=Match.Count()(for (val) in map \add(val>1I))
  Debug(res)
  )
Enter fullscreen mode Exit fullscreen mode

See you tomorrow for the next one!
If you like those posts, consider checking out the main 42 website
(https://L42.is) or the youtube channel (https://www.youtube.com/MarcoServetto)

Discussion (0)