• 5 Posts
  • 12 Comments
Joined 2 years ago
cake
Cake day: November 7th, 2023

help-circle
  • Nim

    Nothing fancy. Simple iterative tree construction and sort, using the std/algorithm and a custom < operator on types.

    type
      LeafNode = ref object
        value: int
      Node = ref object
        value: int
        left, right: LeafNode
        center: Node
      Sword = object
        id, quality: int
        levels: seq[int]
        fishbone: Node
    
    proc add(node: var Node, val: int) =
      var curNode = node
      while not curNode.isNil:
        if val < curNode.value and curNode.left.isNil:
          curNode.left = LeafNode(value: val)
          return
        elif val > curNode.value and curNode.right.isNil:
          curNode.right = LeafNode(value: val)
          return
        elif curNode.center.isNil:
          curNode.center = Node(value: val)
          return
        else: curNode = curNode.center
      node = Node(value: val)
    
    proc calcQuality(sword: Sword): int =
      var res = ""
      var curNode = sword.fishbone
      while not curNode.isNil:
        res &= $curNode.value
        curNode = curNode.center
      return parseInt(res)
    
    proc getLevels(s: Sword): seq[int] =
      var curNode = s.fishbone
      while not curNode.isNil:
        var strVal = ""
        strVal &= (if curNode.left.isNil:  "" else: $curNode.left.value)
        strVal &= $curNode.value
        strVal &= (if curNode.right.isNil: "" else: $curNode.right.value)
        result.add parseInt(strVal)
        curNode = curNode.center
    
    proc `<`(s1, s2: seq[int]): bool =
      for i in 0..min(s1.high, s2.high):
        if s1[i] != s2[i]: return s1[i] < s2[i]
      s1.len < s2.len
    
    proc `<`(s1, s2: Sword): bool =
      if s1.quality != s2.quality: s1.quality < s2.quality
      elif s1.levels != s2.levels: s1.levels < s2.levels
      else: s1.id < s2.id
    
    proc parseSwords(input: string): seq[Sword] =
      for line in input.splitLines:
        let numbers = line.split({':',','}).mapIt(parseInt(it))
        var node= Node(value: numbers[1])
        for num in numbers.toOpenArray(2, numbers.high):
          node.add num
        result &= Sword(id: numbers[0], fishbone: node)
    
    proc solve_part1*(input: string): Solution =
      let swords = parseSwords(input)
      result := swords[0].calcQuality()
    
    proc solve_part2*(input: string): Solution =
      let qualities = parseSwords(input).mapIt(it.calcQuality())
      result := qualities.max - qualities.min
    
    proc solve_part3*(input: string): Solution =
      var swords = parseSwords(input)
      for i in 0..swords.high:
        swords[i].levels = swords[i].getLevels()
        swords[i].quality = swords[i].calcQuality()
      swords.sort(Descending)
      for pos, id in swords.mapit(it.id):
        result.intVal += (pos+1) * id
    

    Full solution at Codeberg: solution.nim


  • Nim

    For part 3 I parse gears as tuples, with regular gears having same value on both ends e.g.

    3|5 -> (3, 5)
    3   -> (3, 3)
    
    proc parseGears(input: string): seq[int] =
      for line in input.splitLines():
        result.add parseInt(line)
    
    proc parseNestedGears(input: string): seq[(int, int)] =
      for line in input.splitLines():
        let nested = line.split('|').mapIt(it.parseInt)
        result.add:
          if nested.len == 1: (nested[0], nested[0])
          else: (nested[0], nested[1])
    
    proc solve_part1*(input: string): Solution =
      let gears = parseGears(input)
      result := 2025 * gears[0] div gears[^1]
    
    proc solve_part2*(input: string): Solution =
      let gears = parseGears(input)
      result := ceil(10000000000000'f64 / (gears[0] / gears[^1])).int
    
    proc solve_part3*(input: string): Solution =
      let gears = parseNestedGears(input)
      let ratios = (0..gears.high-1).mapIt(gears[it][1] / gears[it+1][0])
      result := int(100 * ratios.prod)
    

    Full solution at Codeberg: solution.nim


  • Nim

    Reading this day’s quest I was at first a bit confused what it asks me to calculate. Took me about a minute to realize that most of descriptions are not important and actual calculations for each part are very simple:

    part 1 wants sum of each unique crate size
    part 2 is same but only 20 smallest
    part 3 is max count, because you can always put most crates in one large set and you only need 1 extra set per duplicate crate

    proc solve_part1*(input: string): Solution =
      var seen: set[int16]
      for num in input.split(','):
        let num = parseInt(num).int16
        if num in seen: continue
        else:
          seen.incl num
          result.intVal += num
    
    proc solve_part2*(input: string): Solution =
      var set = input.split(',').mapIt(parseInt(it).uint16)
      set.sort()
      result := set.deduplicate(isSorted=true)[0..<20].sum()
    
    proc solve_part3*(input: string): Solution =
      var cnt = input.split(',').toCountTable()
      result := cnt.largest.val
    

    Full solution at Codeberg: solution.nim


  • For quest 2, I decided to implement my own Complex type and operators, because I expected parts 2 and 3 to have something unconventional, but alas it’s just a regular Mandelbrot fractal.

    Nim again, Nim forever:

    type Complex = tuple[x,y: int]
    proc `$`(c: Complex): string = &"[{c.x},{c.y}]"
    proc `+`(a,b: Complex): Complex = (a.x+b.x, a.y+b.y)
    proc `-`(a,b: Complex): Complex = (a.x-b.x, a.y-b.y)
    proc `*`(a,b: Complex): Complex = (a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x)
    proc `/`(a,b: Complex): Complex = (a.x div b.x, a.y div b.y)
    proc `/`(a: Complex, b: int): Complex = (a.x div b, a.y div b)
    
    proc parseInput(input: string): Complex =
      let parts = input.split({'=','[',',',']'})
      (parseInt(parts[2]), parseInt(parts[3]))
    
    proc isStable(point: Complex, iter: int): bool =
      var num: Complex
      for _ in 1..iter:
        num = (num * num) / 100_000 + point
        if num.x notin -1_000_000 .. 1_000_000 or
           num.y notin -1_000_000 .. 1_000_000:
          return false
      true
    
    proc solve_part1*(input: string): Solution =
      let start = parseInput(input)
      var point: Complex
      for _ in 1..3:
        point = (point * point) / 10 + start
      result := $point
    
    proc solve_part2*(input: string): Solution =
      let start = parseInput(input)
      for y in 0..100:
        for x in 0..100:
          let point: Complex = (start.x + 10 * x, start.y + 10 * y)
          if point.isStable(iter=100): inc result.intVal
    
    proc solve_part3*(input: string): Solution =
      let start = parseInput(input)
      for y in 0..1000:
        for x in 0..1000:
          let point: Complex = (start.x + x, start.y + y)
          if point.isStable(iter=100): inc result.intVal
    

    Full solution at Codeberg: solution.nim


  • So far I really like the difficulty level of EC, compared to AOC.
    Quest 1 is pretty straightforward:

    • part 1: add offsets to index then clamp it between 0..limit e.g. min(limit, max(0, index))
    • part 2: add offsets and then modulo to figure out last index
    • part 3: swap first element with element at index mod names.len until done =)

    My solution in Nim:

    proc parseInput(input: string): tuple[names: seq[string], values: seq[int]] =
      let lines = input.splitLines()
      result.names = lines[0].split(',')
    
      let values = lines[2].multiReplace({"R":"","L":"-"})
      for num in values.split(','):
        result.values.add parseInt(num)
    
    proc solve_part1*(input: string): Solution =
      let (names, values) = parseInput(input)
      var pos = 0
      for value in values:
        pos = min(names.high, max(0, pos + value))
      result := names[pos]
    
    proc solve_part2*(input: string): Solution =
      let (names, values) = parseInput(input)
      let pos = values.sum()
      result := names[pos.euclMod(names.len)]
    
    proc solve_part3*(input: string): Solution =
      var (names, values) = parseInput(input)
      for value in values:
        swap(names[0], names[euclMod(value, names.len)])
      result := names[0]
    

    Full solution at Codeberg: solution.nim




  • If you are me, there is no brain space for remembering new commands. I can already barely hold on to few dozens that I use often. And occasionally when I need “that one that does that niche thing… how was it?” program - I just sit there sifting through logs for couple minutes.

    Today it was od (tbh it’s od almost half the time; not really the best name to memorize (I really need to make a note or something, so I stop forgetting it, lol))

    Also, for this reason I went to great lengths to keep my ~/.zsh_history protected from being randomly deleted/overwritten by mistake, as it happened a couple of times. Currently it’s sitting at around 30_000 lines, oldest command is 2 years old.