Day 15: Warehouse Woes

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • janAkali
    link
    fedilink
    English
    215 hours ago

    Nim

    Very fiddly solution with lots of debugging required.

    Code
    type
      Vec2 = tuple[x,y: int]
      Box = array[2, Vec2]
      Dir = enum
        U = "^"
        R = ">"
        D = "v"
        L = "<"
    
    proc convertPart2(grid: seq[string]): seq[string] =
      for y in 0..grid.high:
        result.add ""
        for x in 0..grid[0].high:
          result[^1] &= (
            if grid[y][x] == 'O': "[]"
            elif grid[y][x] == '#': "##"
            else: "..")
    
    proc shiftLeft(grid: var seq[string], col: int, range: HSlice[int,int]) =
      for i in range.a ..< range.b:
        grid[col][i] = grid[col][i+1]
      grid[col][range.b] = '.'
    
    proc shiftRight(grid: var seq[string], col: int, range: HSlice[int,int]) =
      for i in countDown(range.b, range.a+1):
        grid[col][i] = grid[col][i-1]
      grid[col][range.a] = '.'
    
    proc box(pos: Vec2, grid: seq[string]): array[2, Vec2] =
      if grid[pos.y][pos.x] == '[':
        [pos, (pos.x+1, pos.y)]
      else:
        [(pos.x-1, pos.y), pos]
    
    proc step(grid: var seq[string], bot: var Vec2, dir: Dir) =
      var (x, y) = bot
      case dir
      of U:
        while (dec y; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y-1][bot.x] == 'O': swap(grid[bot.y-1][bot.x], grid[y][x])
        dec bot.y
      of R:
        while (inc x; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y][bot.x+1] == 'O': swap(grid[bot.y][bot.x+1], grid[y][x])
        inc bot.x
      of L:
        while (dec x; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y][bot.x-1] == 'O': swap(grid[bot.y][bot.x-1], grid[y][x])
        dec bot.x
      of D:
        while (inc y; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y+1][bot.x] == 'O': swap(grid[bot.y+1][bot.x], grid[y][x])
        inc bot.y
    
    proc canMoveVert(box: Box, grid: seq[string], boxes: var HashSet[Box], dy: int): bool =
      boxes.incl box
      var left, right = false
      let (lbox, rbox) = (box[0], box[1])
      let lbigBox = box((lbox.x, lbox.y+dy), grid)
      let rbigBox = box((rbox.x, lbox.y+dy), grid)
    
      if grid[lbox.y+dy][lbox.x] == '#' or
         grid[rbox.y+dy][rbox.x] == '#': return false
      elif grid[lbox.y+dy][lbox.x] == '.': left = true
      else:
        left = canMoveVert(box((lbox.x,lbox.y+dy), grid), grid, boxes, dy)
    
      if grid[rbox.y+dy][rbox.x] == '.': right = true
      elif lbigBox == rbigBox: right = left
      else:
        right = canMoveVert(box((rbox.x, rbox.y+dy), grid), grid, boxes, dy)
    
      left and right
    
    proc moveBoxes(grid: var seq[string], boxes: var HashSet[Box], d: Vec2) =
      for box in boxes:
        grid[box[0].y][box[0].x] = '.'
        grid[box[1].y][box[1].x] = '.'
      for box in boxes:
        grid[box[0].y+d.y][box[0].x+d.x] = '['
        grid[box[1].y+d.y][box[1].x+d.x] = ']'
      boxes.clear()
    
    proc step2(grid: var seq[string], bot: var Vec2, dir: Dir) =
      case dir
      of U:
        if grid[bot.y-1][bot.x] == '#': return
        if grid[bot.y-1][bot.x] == '.': dec bot.y
        else:
          var boxes: HashSet[Box]
          if canMoveVert(box((x:bot.x, y:bot.y-1), grid), grid, boxes, -1):
            grid.moveBoxes(boxes, (0, -1))
            dec bot.y
      of R:
        var (x, y) = bot
        while (inc x; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y][bot.x+1] == '[': grid.shiftRight(bot.y, bot.x+1..x)
        inc bot.x
      of L:
        var (x, y) = bot
        while (dec x; grid[y][x] != '#' and grid[y][x] != '.'): discard
        if grid[y][x] == '#': return
        if grid[bot.y][bot.x-1] == ']': grid.shiftLeft(bot.y, x..bot.x-1)
        dec bot.x
      of D:
        if grid[bot.y+1][bot.x] == '#': return
        if grid[bot.y+1][bot.x] == '.': inc bot.y
        else:
          var boxes: HashSet[Box]
          if canMoveVert(box((x:bot.x, y:bot.y+1), grid), grid, boxes, 1):
            grid.moveBoxes(boxes, (0, 1))
            inc bot.y
    
    
    proc solve(input: string): AOCSolution[int, int] =
      let chunks = input.split("\n\n")
      var grid = chunks[0].splitLines()
      let movements = chunks[1].splitLines().join().join()
    
      var robot: Vec2
      for y in 0..grid.high:
        for x in 0..grid[0].high:
          if grid[y][x] == '@':
            grid[y][x] = '.'
            robot = (x,y)
    
      block p1:
        var grid = grid
        var robot = robot
        for m in movements:
          let dir = parseEnum[Dir]($m)
          step(grid, robot, dir)
        for y in 0..grid.high:
          for x in 0..grid[0].high:
            if grid[y][x] == 'O':
              result.part1 += 100 * y + x
    
      block p2:
        var grid = grid.convertPart2()
        var robot = (robot.x*2, robot.y)
        for m in movements:
          let dir = parseEnum[Dir]($m)
          step2(grid, robot, dir)
          #grid.inspect(robot)
    
        for y in 0..grid.high:
          for x in 0..grid[0].high:
            if grid[y][x] == '[':
              result.part2 += 100 * y + x
    

    Codeberg Repo