import Foundation func readInput(_ filePath: String) throws -> Disk { let content = try String(contentsOfFile: filePath, encoding: .ascii) .trimmingCharacters(in: .whitespacesAndNewlines) .map { Int(String([$0]))! } var pos = 0 var data: [(Int, Int)] = [] var free: [(Int, Int)] = [] for (i, x) in content.enumerated() { if i.isMultiple(of: 2) { data.append((pos, x)) } else if x > 0 { free.append((pos, x)) } pos += x } return Disk(data: data, free: free) } func sumRange(start: Int, len: Int) -> Int { return len * (start + start + len - 1) / 2 } struct Disk { var data: [(Int, Int)] var free: [(Int, Int)] var csum: Int = 0 var firstFree: Int = 0 // emulate a Deque, avoid O(n) removal from front var compact = false mutating func evolve() { var (iFree, freeBlock) = free[firstFree] var (iData, dataBlock) = data.last! let fileNo = data.count - 1 while (dataBlock >= freeBlock) { if iFree > iData { if dataBlock == 0 { data.removeLast() } data.enumerated().forEach { fileNo, d in let (i, len) = d csum += fileNo * sumRange(start: i, len: len) } compact = true return } csum += fileNo * sumRange(start: iFree, len: freeBlock) dataBlock -= freeBlock data[fileNo] = (iData, dataBlock) firstFree += 1 (iFree, freeBlock) = free[firstFree] } if dataBlock > 0 { // not needed; only for sanity csum += fileNo * sumRange(start: iFree, len: dataBlock) free[firstFree] = (iFree + dataBlock, freeBlock - dataBlock) } data.removeLast() } } var disk = try readInput(CommandLine.arguments[1]) while !disk.compact { disk.evolve() //print(disk) } print(disk.csum)