68 lines
2.0 KiB
Swift
68 lines
2.0 KiB
Swift
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)
|