145 lines
4.1 KiB
Smalltalk
145 lines
4.1 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]) notNil]
|
|
|
|
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]
|
|
]
|
|
|
|
LinkedList subclass: FreeList [
|
|
| searchCache |
|
|
|
|
initialize [ searchCache := nil ]
|
|
|
|
searchCache
|
|
[ searchCache ifNil: [
|
|
searchCache := Array new: 9 withAll: self first].
|
|
^searchCache ]
|
|
|
|
cachedLinkForSize: sz [ ^self searchCache at: sz ]
|
|
cachedLinkForSize: sz put: block [ searchCache at: sz put: block ]
|
|
|
|
remove: block
|
|
[ searchCache keysAndValuesDo: [
|
|
:i :elt | elt == block ifTrue: [
|
|
searchCache at: i put: elt nextLink ]].
|
|
super remove: block ]
|
|
|
|
freeForBlock: block
|
|
[ | link len |
|
|
len := block len.
|
|
link := self cachedLinkForSize: len.
|
|
[ link isNil ] whileFalse: [
|
|
link pos > block pos ifTrue: [
|
|
self cachedLinkForSize: len put: link. ^nil ].
|
|
link len >= len ifTrue: [
|
|
self cachedLinkForSize: len put: link. ^link ].
|
|
link nextLink
|
|
ifNil: [ self cachedLinkForSize: len put: link. ^nil ]
|
|
ifNotNil: [:it | link := it]].
|
|
^nil ]
|
|
]
|
|
|
|
DriveBase subclass: DriveP2 [
|
|
| freeList idMap lastId |
|
|
map: m
|
|
[ freeList := FreeList 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 freeForBlock: block) ifNotNil: [
|
|
:free | self moveBlock: block to: free ]]
|
|
]
|
|
|
|
AOC input: [ stdin nextLine ];
|
|
part1: DriveP1;
|
|
part2: DriveP2;
|
|
result: [ :map :Drive | Drive new map: map; compact; checksum ];
|
|
finish.
|