summaryrefslogtreecommitdiff
path: root/day9/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'day9/main.go')
-rw-r--r--day9/main.go215
1 files changed, 215 insertions, 0 deletions
diff --git a/day9/main.go b/day9/main.go
new file mode 100644
index 0000000..1ecb78a
--- /dev/null
+++ b/day9/main.go
@@ -0,0 +1,215 @@
+package main
+
+import (
+ "bufio"
+ "fmt"
+ "os"
+ "strings"
+)
+
+const SENTINEL int = -2
+const FREE_BLOCK int = -1
+
+type Block struct {
+ Prev, Next *Block
+ Size int
+ FileId int
+}
+
+func (before *Block) InsertAfter(after *Block) {
+ before.Next.Prev = after
+ after.Next, before.Next = before.Next, after
+ after.Prev = before
+}
+
+func (block *Block) Detach() {
+ block.Prev.Next, block.Next.Prev = block.Next, block.Prev
+ block.Prev, block.Next = nil, nil
+}
+
+func (block *Block) TryMergeFreeWithNext() {
+ if block.FileId == FREE_BLOCK && block.Next.FileId == FREE_BLOCK {
+ block.Size += block.Next.Size
+ block.Next.Detach()
+ }
+}
+
+func (block *Block) TryMergeFree() *Block {
+ block.TryMergeFreeWithNext()
+ if block.Prev.FileId == FREE_BLOCK {
+ block = block.Prev
+ block.TryMergeFreeWithNext()
+ }
+ return block
+}
+
+func (block *Block) Split(Size int) {
+ if Size >= block.Size {
+ panic("please")
+ }
+
+ if Size == block.Size {
+ return
+ }
+
+ newBlock := &Block{nil, nil, block.Size - Size, block.FileId}
+ block.Size = Size
+ block.InsertAfter(newBlock)
+}
+
+type Disk struct {
+ Sentinel *Block
+}
+
+func readInt(b byte) int {
+ return int(b - '0')
+}
+
+func MakeDisk(description string) (disk Disk) {
+ disk.Sentinel = new(Block)
+ disk.Sentinel.Size = 0
+ disk.Sentinel.FileId = SENTINEL
+ disk.Sentinel.Next = disk.Sentinel
+ disk.Sentinel.Prev = disk.Sentinel
+
+ nextFileId := 0
+ nextIsFree := false
+
+ head := disk.Sentinel
+
+ for _, b := range []byte(description) {
+ block := &Block{
+ Prev: nil, Next: nil, Size: readInt(b),
+ }
+
+ if block.Size == 0 {
+ nextIsFree = !nextIsFree
+ continue
+ }
+
+ if nextIsFree {
+ block.FileId = FREE_BLOCK
+ nextIsFree = false
+ } else {
+ block.FileId = nextFileId
+ nextFileId++
+ nextIsFree = true
+ }
+
+ head.InsertAfter(block)
+ head = block
+ }
+
+ return
+}
+
+func (disk *Disk) Print() {
+ fmt.Println("===============")
+ for b := disk.Sentinel.Next; b != disk.Sentinel; b = b.Next {
+ fmt.Println(b)
+ }
+ fmt.Println("===============")
+ bufio.NewReader(os.Stdin).ReadBytes('\n')
+}
+
+func (disk *Disk) Compactify() {
+ head := disk.Sentinel.Next
+ tail := disk.Sentinel.Prev
+
+ for head != tail {
+ if head.FileId >= 0 {
+ head = head.Next
+ continue
+ }
+
+ if tail.FileId == FREE_BLOCK {
+ tail = tail.Prev
+ continue
+ }
+
+ if tail.Size > head.Size {
+ tail.Split(tail.Size - head.Size)
+ tail = tail.Next
+ }
+
+ if head.Size > tail.Size {
+ head.Split(tail.Size)
+ }
+
+ head.FileId, tail.FileId = tail.FileId, head.FileId
+ tail = tail.TryMergeFree()
+ }
+}
+
+func (disk *Disk) Defragment() {
+ tail := disk.Sentinel.Prev
+
+ for tail != disk.Sentinel {
+ if tail.FileId == FREE_BLOCK {
+ tail = tail.Prev
+ continue
+ }
+
+ var freeBlock *Block = nil
+ for head := disk.Sentinel.Next; head != tail; head = head.Next {
+ if head.FileId == FREE_BLOCK && head.Size >= tail.Size {
+ freeBlock = head
+ break
+ }
+ }
+
+ if freeBlock == nil {
+ tail = tail.Prev
+ continue
+ }
+
+ if freeBlock.Size > tail.Size {
+ freeBlock.Split(tail.Size)
+ }
+
+ freeBlock.FileId, tail.FileId = tail.FileId, freeBlock.FileId
+ tail = tail.TryMergeFree()
+ }
+}
+
+func (disk *Disk) ComputeChecksum() (sum int64) {
+ pos := 0
+ for b := disk.Sentinel.Next; b != disk.Sentinel; b = b.Next {
+ if b.FileId >= 0 {
+ for i := 0; i < b.Size; i++ {
+ sum += int64(pos * b.FileId)
+ pos++
+ }
+ } else {
+ pos += b.Size
+ }
+ }
+ return
+}
+
+func main() {
+ disk := MakeDisk(readData("example.txt"))
+ disk.Compactify()
+ fmt.Println(disk.ComputeChecksum(), 1928)
+
+ disk = MakeDisk(readData("data.txt"))
+ disk.Compactify()
+ fmt.Println(disk.ComputeChecksum(), 6415184586041)
+
+ disk = MakeDisk(readData("example.txt"))
+ disk.Defragment()
+ fmt.Println(disk.ComputeChecksum(), 2858)
+
+ disk = MakeDisk(readData("data.txt"))
+ disk.Defragment()
+ fmt.Println(disk.ComputeChecksum())
+}
+
+func readData(fileName string) string {
+ resultBytes, err := os.ReadFile(fileName)
+ if err != nil {
+ panic(err)
+ }
+
+ return strings.TrimSpace(string(resultBytes))
+}