Day 9: Disk Fragmenter

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
    arrow-up
    2
    ·
    edit-2
    3 days ago

    Nim

    Wrote ugly-ass code today, but it was surprisingly easy to debug and fast.

    Solution:
    Part 1: Parse data into a sequence of blocks and empty space like in example (I use -1 for empty space) and two indexes. First index goes 0 -> end, second index starts at the end. When we encounter empty space -> we use value from second index and decrement it (while skipping empty spaces). Repeat until both indexes meet at some point.

    Part 2: Parse data into sequence of block objects and try to insert each data block into each empty space block before it. Somehow it all just worked without too many bugs.

    Runtime (final version): 123 ms

    type
      BlockKind = enum Data, Space
      Block = object
        size: int
        case kind: BlockKind
        of Data:
          index: int
        of Space:
          discard
    
    func parseBlocks(input: string): tuple[blocks: seq[Block], id: int] =
      for i, c in input:
        let digit = c.ord - '0'.ord
        if i mod 2 == 0:
          result.blocks.add Block(kind: Data, size: digit, index: result.id)
          if i < input.high: inc result.id
        else:
          result.blocks.add Block(kind: Space, size: digit)
    
    proc solve(input: string): AOCSolution[int, int] =
      block p1:
        var memBlocks = newSeqOfCap[int](100_000)
    
        var indBlock = 0
        for i, c in input:
          let digit = c.ord - '0'.ord
          if i mod 2 == 0:
            memBlocks.add (indBlock).repeat(digit)
            inc indBlock
          else:
            memBlocks.add -1.repeat(digit)
    
        var ind = 0
        var revInd = memBlocks.high
        while ind <= revInd:
          if memBlocks[ind] == -1:
            while memBlocks[revInd] == -1: dec revInd
            result.part1 += ind * memBlocks[revInd]
            dec revInd
          else:
            result.part1 += ind * memBlocks[ind]
          inc ind
    
      block p2:
        var (memBlocks, index) = parseBlocks(input)
        var revInd = memBlocks.high
        while revInd > 0:
          doAssert memBlocks[revInd].kind == Data
    
          var spaceInd = -1
          let blockSize = memBlocks[revInd].size
          for ind in 0..revInd:
            if memBlocks[ind].kind == Space and memBlocks[ind].size >= blockSize:
              spaceInd = ind; break
    
          if spaceInd != -1:
            let bSize = memBlocks[revInd].size
            let diffSize = memBlocks[spaceInd].size - bSize
            swap(memBlocks[spaceInd], memBlocks[revInd])
            if diffSize != 0:
              memBlocks[revInd].size = bSize
              memBlocks.insert(Block(kind: Space, size: diffSize), spaceInd + 1)
              inc revInd # shift index bc we added object
    
          dec index
          # skip space blocks and data blocks with higher index
          while (dec revInd; revInd < 0 or
                 memBlocks[revInd].kind != Data or
                 memBlocks[revInd].index != index): discard
    
        var unitIndex = 0
        for b in memBlocks:
          case b.kind
          of Data:
            for _ in 1..b.size:
              result.part2 += unitIndex * b.index
              inc unitIndex
          of Space:
            unitIndex += b.size
    

    Codeberg repo