From 2f165369cc4e77dfc34d32f18219ede0f27f6a45 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 9 Dec 2024 08:47:11 -0600 Subject: [PATCH 1/5] =?UTF-8?q?=F0=9F=93=9D=20Add=20instructions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index 914ae7b..f2df47a 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,66 @@ # Day 9: Disk Defragmenter [Link](https://adventofcode.com/2024/day/9) + +## Day 9: Disk Fragmenter + +Another push of the button leaves you in the familiar hallways of some friendly [amphipods](https://adventofcode.com/2021/day/23)! Good thing you each somehow got your own personal mini submarine. The Historians jet away in search of the Chief, mostly by driving directly into walls. + +While The Historians quickly figure out how to pilot these things, you notice an amphipod in the corner struggling with his computer. He's trying to make more contiguous free space by compacting all of the files, but his program isn't working; you offer to help. + +He shows you the **disk map** (your puzzle input) he's already generated. For example: + +``` +2333133121414131402 +``` + +The disk map uses a dense format to represent the layout of **files** and **free space** on the disk. The digits alternate between indicating the length of a file and the length of free space. + +So, a disk map like `12345` would represent a one-block file, two blocks of free space, a three-block file, four blocks of free space, and then a five-block file. A disk map like `90909` would represent three nine-block files in a row (with no free space between them). + +Each file on disk also has an **ID number** based on the order of the files as they appear **before** they are rearranged, starting with ID 0. So, the disk map 12345 has three files: a one-block file with ID 0, a three-block file with ID 1, and a five-block file with ID 2. Using one character for each block where digits are the file ID and . is free space, the disk map 12345 represents these individual blocks: + +``` +0..111....22222 +``` + +The first example above, `2333133121414131402`, represents these individual blocks: + +``` +00...111...2...333.44.5555.6666.777.888899 +``` + +The amphipod would like to **move file blocks one at a time** from the end of the disk to the leftmost free space block (until there are no gaps remaining between file blocks). For the disk map 12345, the process looks like this: + +``` +0..111....22222 +02.111....2222. +022111....222.. +0221112...22... +02211122..2.... +022111222...... +``` + +The first example requires a few more steps: + +``` +00...111...2...333.44.5555.6666.777.888899 +009..111...2...333.44.5555.6666.777.88889. +0099.111...2...333.44.5555.6666.777.8888.. +00998111...2...333.44.5555.6666.777.888... +009981118..2...333.44.5555.6666.777.88.... +0099811188.2...333.44.5555.6666.777.8..... +009981118882...333.44.5555.6666.777....... +0099811188827..333.44.5555.6666.77........ +00998111888277.333.44.5555.6666.7......... +009981118882777333.44.5555.6666........... +009981118882777333644.5555.666............ +00998111888277733364465555.66............. +0099811188827773336446555566.............. +``` + +The final step of this file-compacting process is to update the **filesystem checksum**. To calculate the checksum, add up the result of multiplying each of these blocks' position with the file ID number it contains. The leftmost block is in position 0. If a block contains free space, skip it instead. + +Continuing the first example, the first few blocks' position multiplied by its file ID number are `0 * 0 = 0`, `1 * 0 = 0`, `2 * 9 = 18`, `3 * 9 = 27`, `4 * 8 = 32`, and so on. In this example, the checksum is the sum of these, **1928**. + +Compact the amphipod's hard drive using the process he requested. What is the resulting filesystem checksum? From 932e8e2f116e19fd54ca4537461259f18d4d05ec Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 9 Dec 2024 12:30:41 -0600 Subject: [PATCH 2/5] =?UTF-8?q?=F0=9F=9A=A7=20Add=20Defrag=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blocks.go | 5 +++++ blocks_test.go | 31 +++++++++++++++++++++++++++++++ cmd/defrag/main.go | 36 ++++++++++++++++++++++++++++++++++++ defrag.go | 26 ++++++++++++++++++++++++++ defrag_test.go | 19 +++++++++++++++++++ go.mod | 10 +++++++++- go.sum | 10 ++++++++++ 7 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 blocks.go create mode 100644 blocks_test.go create mode 100644 cmd/defrag/main.go create mode 100644 defrag.go create mode 100644 defrag_test.go create mode 100644 go.sum diff --git a/blocks.go b/blocks.go new file mode 100644 index 0000000..8b3ba09 --- /dev/null +++ b/blocks.go @@ -0,0 +1,5 @@ +package defrag + +func Blocks(diskMap []byte) ([]byte, error) { + return nil, nil +} diff --git a/blocks_test.go b/blocks_test.go new file mode 100644 index 0000000..1e1831c --- /dev/null +++ b/blocks_test.go @@ -0,0 +1,31 @@ +package defrag + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +type tt struct { + name string + diskMap []byte + blocks []byte + defragged []byte +} + +func sampleTests() []tt { + return []tt{ + {"short", []byte("12345"), []byte("0..111....22222"), []byte("022111222......")}, + {"longer", []byte("2333133121414131402"), []byte("00...111...2...333.44.5555.6666.777.888899"), []byte("0099811188827773336446555566..............")}, + } +} + +func TestBlocks(t *testing.T) { + for _, testcase := range sampleTests() { + t.Run(testcase.name, func(sub *testing.T) { + blocks, err := Blocks(testcase.diskMap) + require.NoError(sub, err) + require.Equal(sub, testcase.blocks, blocks) + }) + } +} diff --git a/cmd/defrag/main.go b/cmd/defrag/main.go new file mode 100644 index 0000000..29ac7ed --- /dev/null +++ b/cmd/defrag/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "io" + "os" + "bytes" + + "codeberg.org/danjones000/advent-of-code/2024-09/defrag" +) + +func handleErr(err error) { + if err == nil { + return + } + + fmt.Fprintln(os.Stderr, err) + os.Exit(1) +} + +func main() { + name := os.Args[0] + + input, err := io.ReadAll(os.Stdin) + handleErr(err) + + fmt.Fprintln(os.Stderr, "Welcome to ", name) + input = bytes.TrimSpace(input) + + blocks, err := defrag.Blocks(input) + handleErr(err) + + fmt.Println("@TODO") + fmt.Println("Got this") + fmt.Printf("%s\n", blocks) +} diff --git a/defrag.go b/defrag.go new file mode 100644 index 0000000..82204d2 --- /dev/null +++ b/defrag.go @@ -0,0 +1,26 @@ +package defrag + +// Defrag performs an in-place sort of blocks. +// If an error occurs during processing, it will be returned. The slice may have been partially sorted. +func Defrag(blocks []byte) error { + lastPulled := len(blocks) + lastPushed := -1 + for block := 0; block < len(blocks); block++ { + if blocks[block] != '.' { + continue + } + if lastPulled <= block { + break + } + for swap := lastPulled - 1; swap > lastPushed; swap-- { + if blocks[swap] == '.' { + continue + } + blocks[block], blocks[swap] = blocks[swap], blocks[block] + lastPulled = swap + lastPushed = block + break + } + } + return nil +} diff --git a/defrag_test.go b/defrag_test.go new file mode 100644 index 0000000..62f625b --- /dev/null +++ b/defrag_test.go @@ -0,0 +1,19 @@ +package defrag + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestDefrag(t *testing.T) { + for _, testcase := range sampleTests() { + t.Run(testcase.name, func(sub *testing.T) { + blocks := make([]byte, len(testcase.blocks)) + copy(blocks, testcase.blocks) + err := Defrag(blocks) + require.NoError(sub, err) + require.Equal(sub, testcase.defragged, blocks) + }) + } +} diff --git a/go.mod b/go.mod index 8879fe6..610e26e 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,11 @@ -module codeberg.org/danjones000/advent-of-code-2024-09 +module codeberg.org/danjones000/advent-of-code/2024-09/defrag go 1.23.1 + +require github.com/stretchr/testify v1.10.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..713a0b4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From ac16a3f167e983b8d5dba87d1e790ba95ebd6620 Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 9 Dec 2024 12:35:54 -0600 Subject: [PATCH 3/5] =?UTF-8?q?=F0=9F=9A=B8=20Fill=20out=20cmd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/defrag/main.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cmd/defrag/main.go b/cmd/defrag/main.go index 29ac7ed..e620e91 100644 --- a/cmd/defrag/main.go +++ b/cmd/defrag/main.go @@ -29,8 +29,12 @@ func main() { blocks, err := defrag.Blocks(input) handleErr(err) + err = defrag.Defrag(blocks) + handleErr(err) + + fmt.Println("From this") + fmt.Printf("%s\n", input) - fmt.Println("@TODO") fmt.Println("Got this") fmt.Printf("%s\n", blocks) } From a495a2ef1a57364daefcc4e7bbefd81b2655b16d Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 9 Dec 2024 12:49:56 -0600 Subject: [PATCH 4/5] =?UTF-8?q?=E2=9C=A8=20Implement=20Blocks=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blocks.go | 27 ++++++++++++++++++++++++++- cmd/defrag/main.go | 2 +- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/blocks.go b/blocks.go index 8b3ba09..2c90f7a 100644 --- a/blocks.go +++ b/blocks.go @@ -1,5 +1,30 @@ package defrag +import ( + "bytes" + "strconv" + "strings" +) + func Blocks(diskMap []byte) ([]byte, error) { - return nil, nil + current := 0 + onFile := false + buff := bytes.Buffer{} + for _, by := range diskMap { + count, err := strconv.Atoi(string(by)) + if err != nil { + return nil, err + } + onFile = !onFile + if count == 0 { + continue + } + append := "." + if onFile { + append = strconv.Itoa(current) + current++ + } + buff.WriteString(strings.Repeat(append, count)) + } + return buff.Bytes(), nil } diff --git a/cmd/defrag/main.go b/cmd/defrag/main.go index e620e91..99bdecf 100644 --- a/cmd/defrag/main.go +++ b/cmd/defrag/main.go @@ -1,10 +1,10 @@ package main import ( + "bytes" "fmt" "io" "os" - "bytes" "codeberg.org/danjones000/advent-of-code/2024-09/defrag" ) From 495d50ba109499cab7f7306773d2cb0b058d3d3c Mon Sep 17 00:00:00 2001 From: Dan Jones Date: Mon, 9 Dec 2024 14:34:01 -0600 Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=90=9B=20Account=20for=20file=20ids?= =?UTF-8?q?=20>=209?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + blocks.go | 17 +++++++++-------- blocks_test.go | 11 +++++------ checksum.go | 14 ++++++++++++++ cmd/defrag/main.go | 9 ++++++++- defrag.go | 6 +++--- defrag_test.go | 19 ------------------- 7 files changed, 40 insertions(+), 37 deletions(-) create mode 100644 checksum.go delete mode 100644 defrag_test.go diff --git a/.gitignore b/.gitignore index 6f72f89..a6782c6 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ go.work.sum # env file .env +defrag diff --git a/blocks.go b/blocks.go index 2c90f7a..20f6051 100644 --- a/blocks.go +++ b/blocks.go @@ -1,30 +1,31 @@ package defrag import ( - "bytes" "strconv" - "strings" ) -func Blocks(diskMap []byte) ([]byte, error) { +func Blocks(diskMap []byte) ([]int, error) { current := 0 onFile := false - buff := bytes.Buffer{} + out := []int{} for _, by := range diskMap { count, err := strconv.Atoi(string(by)) if err != nil { return nil, err } onFile = !onFile + if count == 0 { continue } - append := "." + val := -1 if onFile { - append = strconv.Itoa(current) + val = current current++ } - buff.WriteString(strings.Repeat(append, count)) + for idx := 0; idx < count; idx++ { + out = append(out, val) + } } - return buff.Bytes(), nil + return out, nil } diff --git a/blocks_test.go b/blocks_test.go index 1e1831c..b50bd33 100644 --- a/blocks_test.go +++ b/blocks_test.go @@ -7,16 +7,15 @@ import ( ) type tt struct { - name string - diskMap []byte - blocks []byte - defragged []byte + name string + diskMap []byte + blocks []int } func sampleTests() []tt { return []tt{ - {"short", []byte("12345"), []byte("0..111....22222"), []byte("022111222......")}, - {"longer", []byte("2333133121414131402"), []byte("00...111...2...333.44.5555.6666.777.888899"), []byte("0099811188827773336446555566..............")}, + {"short", []byte("12345"), []int{0, -1, -1, 1, 1, 1, -1, -1, -1, -1, 2, 2, 2, 2, 2}}, + {"longer", []byte("2333133121414131402"), []int{0, 0, -1, -1, -1, 1, 1, 1, -1, -1, -1, 2, -1, -1, -1, 3, 3, 3, -1, 4, 4, -1, 5, 5, 5, 5, -1, 6, 6, 6, 6, -1, 7, 7, 7, -1, 8, 8, 8, 8, 9, 9}}, } } diff --git a/checksum.go b/checksum.go new file mode 100644 index 0000000..0b934b0 --- /dev/null +++ b/checksum.go @@ -0,0 +1,14 @@ +package defrag + +// import "strconv" + +func Checksum(blocks []int) (sum int, err error) { + for idx, fileByte := range blocks { + if fileByte == -1 { + break + } + + sum += idx * fileByte + } + return +} diff --git a/cmd/defrag/main.go b/cmd/defrag/main.go index 99bdecf..e5e7fd7 100644 --- a/cmd/defrag/main.go +++ b/cmd/defrag/main.go @@ -29,6 +29,7 @@ func main() { blocks, err := defrag.Blocks(input) handleErr(err) + err = defrag.Defrag(blocks) handleErr(err) @@ -36,5 +37,11 @@ func main() { fmt.Printf("%s\n", input) fmt.Println("Got this") - fmt.Printf("%s\n", blocks) + fmt.Printf("%v\n", blocks) + + sum, err := defrag.Checksum(blocks) + handleErr(err) + + fmt.Println("Checksum:") + fmt.Printf("%d\n", sum) } diff --git a/defrag.go b/defrag.go index 82204d2..8623c4f 100644 --- a/defrag.go +++ b/defrag.go @@ -2,18 +2,18 @@ package defrag // Defrag performs an in-place sort of blocks. // If an error occurs during processing, it will be returned. The slice may have been partially sorted. -func Defrag(blocks []byte) error { +func Defrag(blocks []int) error { lastPulled := len(blocks) lastPushed := -1 for block := 0; block < len(blocks); block++ { - if blocks[block] != '.' { + if blocks[block] != -1 { continue } if lastPulled <= block { break } for swap := lastPulled - 1; swap > lastPushed; swap-- { - if blocks[swap] == '.' { + if blocks[swap] == -1 { continue } blocks[block], blocks[swap] = blocks[swap], blocks[block] diff --git a/defrag_test.go b/defrag_test.go deleted file mode 100644 index 62f625b..0000000 --- a/defrag_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package defrag - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestDefrag(t *testing.T) { - for _, testcase := range sampleTests() { - t.Run(testcase.name, func(sub *testing.T) { - blocks := make([]byte, len(testcase.blocks)) - copy(blocks, testcase.blocks) - err := Defrag(blocks) - require.NoError(sub, err) - require.Equal(sub, testcase.defragged, blocks) - }) - } -}