aoc24/day9/main.go
mstar c22cfbe185 Task 9 completed
No bruteforce necessary, lets go
2024-12-09 16:15:57 +01:00

183 lines
4.5 KiB
Go

package main
import (
"fmt"
"slices"
"strconv"
"unicode"
"git.mstar.dev/mstar/aoc24/util"
"git.mstar.dev/mstar/goutils/other"
"git.mstar.dev/mstar/goutils/sliceutils"
)
const EmptyBlockId = -1
func findFirstEmptyIndex(list []int) (int, bool) {
for i, v := range list {
if v == EmptyBlockId {
return i, true
}
}
return -1, false
}
func findLastUsedIndex(list []int) (int, bool) {
for i := len(list) - 1; i >= 0; i-- {
if list[i] != EmptyBlockId {
return i, true
}
}
return -1, false
}
func indicesToBlocks(indices []int) []int {
blocks := []int{}
// indices are len of block, len of empty, len of block, len of empty
isFile := true
// block id increments each time a block with a file completes
i := 0
for _, size := range indices {
// Write the full block
for range size {
if isFile {
blocks = append(blocks, i)
} else {
blocks = append(blocks, EmptyBlockId)
}
}
// If current block is a file, increment id counter
if isFile {
i++
}
// Swap state
isFile = !isFile
}
return blocks
}
func compactBlocks1(blocks []int) (compacted []int) {
compacted = slices.Clone(blocks)
firstEmpty, _ := findFirstEmptyIndex(compacted)
lastUsed, _ := findLastUsedIndex(compacted)
for firstEmpty < lastUsed {
util.SliceSwap(compacted, firstEmpty, lastUsed)
firstEmpty, _ = findFirstEmptyIndex(compacted)
lastUsed, _ = findLastUsedIndex(compacted)
}
return
}
func checksumBlocks(blocks []int) uint64 {
var total uint64 = 0
for i, v := range blocks {
if v == EmptyBlockId {
continue
}
total += uint64(i * v)
}
return total
}
func findFirstEmptyBlockOfLength(list []int, length int) (int, bool) {
lenCounter := 0
for i, v := range list {
if v == EmptyBlockId {
lenCounter++
} else {
lenCounter = 0
}
if lenCounter == length {
// Start of empty block with (at least) target length is at i - (lenCounter-1), since lenCounter starts at 0
return i - lenCounter + 1, true
}
}
return -1, false
}
func findBlockById(list []int, blockId int) (start, length int, found bool) {
length = 0
for i := len(list) - 1; i >= 0; i-- {
if list[i] == blockId {
length++
} else {
if length != 0 {
return i + 1, length, true
}
}
}
return -1, -1, false
}
func compactBlocks2(list []int) (compacted []int) {
compacted = slices.Clone(list)
lastBlockIndex, _ := findLastUsedIndex(compacted)
lastBlockId := compacted[lastBlockIndex]
// fmt.Printf("i1 Checking block id %d\n", lastBlockId)
lastUsedStart, length, _ := findBlockById(compacted, lastBlockId)
firstEmpty, found := findFirstEmptyBlockOfLength(compacted, length)
for !found {
lastBlockId--
if lastBlockId < 0 {
return
}
// fmt.Printf("i2 Checking block id %d\n", lastBlockId)
lastUsedStart, length, _ = findBlockById(compacted, lastBlockId)
firstEmpty, found = findFirstEmptyBlockOfLength(compacted, length)
}
for {
// fmt.Printf(
// "Moving block %d starting at %d with len %d to %d\n",
// lastBlockId,
// lastUsedStart,
// length,
// firstEmpty,
// )
for i := range length {
util.SliceSwap(compacted, lastUsedStart+i, firstEmpty+i)
}
lastBlockId--
if lastBlockId < 0 {
return
}
// fmt.Printf("l1 Checking block id %d\n", lastBlockId)
lastUsedStart, length, _ = findBlockById(compacted, lastBlockId)
firstEmpty, found = findFirstEmptyBlockOfLength(compacted, length)
// startLastEmpty := findStartLastEmptyBlock(compacted)
// fmt.Printf("First empty found: %d, start last empty: %d\n", firstEmpty, startLastEmpty)
for !found || firstEmpty > lastUsedStart {
// fmt.Printf("No space for id %d found\n", lastBlockId)
lastBlockId--
if lastBlockId < 0 {
return
}
// fmt.Printf("l2 Checking block id %d\n", lastBlockId)
lastUsedStart, length, _ = findBlockById(compacted, lastBlockId)
firstEmpty, found = findFirstEmptyBlockOfLength(compacted, length)
}
}
}
func main() {
rawInput := []rune(string(util.LoadFileFromArgs()))
allNums := sliceutils.Map(
sliceutils.Filter(rawInput, func(t rune) bool { return unicode.IsNumber(t) }),
func(t rune) int { return other.Must(strconv.Atoi(string(t))) },
)
blocks := indicesToBlocks(allNums)
// fmt.Printf("Parsed blocks: %v\n", blocks)
compacted := compactBlocks1(blocks)
// fmt.Printf("Compacted blocks: %v\n", compacted)
onlySetBlocks := sliceutils.Filter(compacted, func(t int) bool { return t != EmptyBlockId })
checksum1 := checksumBlocks(onlySetBlocks)
fmt.Printf("Task 1: %d\n", checksum1)
compacted2 := compactBlocks2(blocks)
fmt.Printf(
"Task 2: %d\n",
checksumBlocks(
compacted2,
),
)
}