Advent of Code 2022 Day 5: Supply Stacks
By Andreas Røssland
https://adventofcode.com/2022/day/05 https://github.com/roessland/advent-of-code
Today’s problem was to move items between stacks. Time spent: 00:29:00 for part 1, and 00:33:12 for both. I could have shaved 15 minutes off my time if I just copy/pasted the input directly into a slice literal, but oh well.
Data structures and reading input
package main
import (
"bufio"
"embed"
_ "embed"
"fmt"
"github.com/roessland/advent-of-code/2022/aocutil"
"github.com/roessland/gopkg/sliceutil"
"github.com/roessland/gopkg/stackqueue"
"log"
"unicode"
)
//go:embed input.txt input-ex.txt
var inputFiles embed.FS
func main() {
part1()
part2()
}
type Move struct {
No, From, To int
}
func Print(stacks []stackqueue.Stack[byte]) {
for _, s := range stacks {
fmt.Println(string(s))
}
}
func ReadInput() (stacks []stackqueue.Stack[byte], moves []Move) {
f, err := inputFiles.Open("input.txt")
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
N := (len(line) + 1) / 4
if stacks == nil {
stacks = make([]stackqueue.Stack[byte], N)
}
if line == "" {
break
}
if unicode.IsNumber(rune(line[1])) {
continue
}
for i := 0; i < N; i++ {
c := line[i*4+1]
if unicode.IsLetter(rune(c)) {
stacks[i] = append(stacks[i], c)
}
}
}
for i := range stacks {
sliceutil.Reverse(stacks[i])
}
for scanner.Scan() {
line := scanner.Text()
p := aocutil.GetIntsInString(line)
moves = append(moves, Move{p[0], p[1] - 1, p[2] - 1})
}
return stacks, moves
}
Part 1
func part1() {
stacks, moves := ReadInput()
Print(stacks)
for _, move := range moves {
for move.No > 0 {
crate := stacks[move.From].Pop()
stacks[move.To].Push(crate)
move.No--
}
}
msg := ""
for _, stack := range stacks {
msg = fmt.Sprintf("%s%c", msg, stack.Peek())
}
fmt.Println("Part 1:", msg)
Print(stacks)
}
Part 2
One simple way to simulate moving multiple items at the same time is to just reverse the items in the destination stack after moving them. An append
and slice might be prettier but this is what I came up with at first.
func part2() {
stacks, moves := ReadInput()
Print(stacks)
for _, move := range moves {
remaining := move.No
for remaining > 0 {
crate := stacks[move.From].Pop()
stacks[move.To].Push(crate)
remaining--
}
sliceutil.Reverse(stacks[move.To][len(stacks[move.To])-move.No:])
}
msg := ""
for _, stack := range stacks {
msg = fmt.Sprintf("%s%c", msg, stack.Peek())
}
fmt.Println("Part 1:", msg)
Print(stacks)
}
Appendix: Stackqueue
https://github.com/roessland/gopkg/blob/master/stackqueue/stack.go v1.6.0
package stackqueue
// Stack is LIFO queue data structure.
type Stack[T any] []T
// New creates a new stack.
func New[T any]() Stack[T] {
s := make([]T, 0)
return s
}
// Push adds an item to the stack.
func (stack *Stack[T]) Push(item T) {
(*stack) = append(*stack, item)
}
// Pop remove the item from the top of the stack and returns it.
func (stack *Stack[T]) Pop() T {
item := (*stack)[len(*stack)-1]
*stack = (*stack)[:len(*stack)-1]
return item
}
// Peek returns the item at the top of the stack.
func (stack *Stack[T]) Peek() T {
return (*stack)[len(*stack)-1]
}
// Len returns the number of items in the stack.
func (stack *Stack[T]) Len() int {
return len(*stack)
}
Appendix: sliceutil
https://github.com/roessland/gopkg/blob/master/sliceutil/reverse.go v1.6.0
package sliceutil
func Reverse[T any](s []T) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}