117 lines
3.2 KiB
Smalltalk
117 lines
3.2 KiB
Smalltalk
Object subclass: DriveBase [
|
|
| ids |
|
|
ids [^ids]
|
|
diskSize [^ids size]
|
|
idAt: i [^ids at: i]
|
|
|
|
isEmpty: i [^(ids at: i ifAbsent: [1]) isNil]
|
|
isFull: i [^(ids at: i ifAbsent: [nil]) isNil not]
|
|
|
|
addBlock: id length: len [ len timesRepeat: [ids add: id] ]
|
|
addFreeLength: len [ len timesRepeat: [ids add: nil] ]
|
|
|
|
mappingPos [^ids size]
|
|
map: diskMap
|
|
[ | id toPut len |
|
|
ids := OrderedCollection new.
|
|
id := 0. toPut := id.
|
|
diskMap do: [
|
|
:c | len := c digitValue.
|
|
toPut
|
|
ifNil: [self addFreeLength: len.
|
|
id := id + 1.
|
|
toPut := id]
|
|
ifNotNil: [self addBlock: id length: len.
|
|
toPut := nil] ].
|
|
ids := ids asArray ]
|
|
|
|
checksum
|
|
[ ^((1 to: self diskSize) collect:
|
|
[ :i | (self idAt: i) ifNil: [0] ifNotNil: [:x | (i - 1) * x] ])
|
|
sum ]
|
|
|
|
compress
|
|
[ | end | end := ids size.
|
|
[ self isEmpty: end ] whileTrue: [ end := end - 1 ].
|
|
ids := (1 to: end) collect: [ :i | ids at: i ].
|
|
^ids ]
|
|
|
|
printOn: st
|
|
[ (1 to: self diskSize) do:
|
|
[ :i | (self idAt: i) ifNil: [st << '. '] ifNotNil: [:id | st << id << ' '] ] ]
|
|
]
|
|
|
|
DriveBase subclass: DriveP1 [
|
|
compact
|
|
[ | start end |
|
|
start := 1. end := ids size.
|
|
[ true ] whileTrue: [
|
|
[ self isFull: start ] whileTrue: [ start := start + 1 ].
|
|
[ self isEmpty: end ] whileTrue: [ end := end - 1 ].
|
|
end > start ifFalse: [^self compress].
|
|
ids at: start put: (ids at: end).
|
|
ids at: end put: nil ]]
|
|
]
|
|
|
|
Link subclass: Block [
|
|
| pos len |
|
|
pos: p [pos:=p]
|
|
pos [^pos]
|
|
len: n [len:=n]
|
|
len [^len]
|
|
|
|
fillFree: by [pos := pos + by. len := len - by]
|
|
isEmpty [^len = 0]
|
|
|
|
printOn: st [st << '#[' << len << ']@' << pos]
|
|
]
|
|
|
|
DriveBase subclass: DriveP2 [
|
|
| freeList idMap lastId |
|
|
map: m
|
|
[ freeList := LinkedList new.
|
|
idMap := Dictionary new.
|
|
super map: m ]
|
|
|
|
newBlock: len [^Block new pos: self mappingPos; len: len]
|
|
|
|
addBlock: id length: len
|
|
[ | block | block := self newBlock: len.
|
|
idMap at: id put: block.
|
|
lastId := id.
|
|
super addBlock: id length: len ]
|
|
addFreeLength: len
|
|
[ freeList add: (self newBlock: len).
|
|
super addFreeLength: len ]
|
|
|
|
compact
|
|
[ (lastId to: 0 by: -1) do: [:id | self compactBlock: id].
|
|
self compress ]
|
|
|
|
moveBlock: block to: free
|
|
[ (1 to: block len) do:
|
|
[ :off |
|
|
ids at: free pos + off put: (ids at: block pos + off).
|
|
ids at: block pos + off put: nil.
|
|
"Do not create a free space where the block was moved from
|
|
-- all of the blocks that will be moved are on the left"
|
|
].
|
|
free fillFree: block len.
|
|
free isEmpty ifTrue: [freeList remove: free] ]
|
|
|
|
compactBlock: id
|
|
[ | block | block := idMap at: id.
|
|
freeList do: [
|
|
:free |
|
|
free pos > block pos ifTrue: [^self].
|
|
free len >= block len ifTrue: [
|
|
self moveBlock: block to: free.
|
|
^self
|
|
]]]
|
|
]
|
|
|
|
AOC input: [ stdin nextLine ];
|
|
part1: DriveP1;
|
|
part2: DriveP2;
|
|
result: [ :map :Drive | Drive new map: map; compact; checksum ];
|
|
finish.
|