Files
advent2022/solutions/day5/day5.go
2022-12-23 19:45:56 -08:00

283 lines
5.4 KiB
Go

package day5
import (
"bufio"
"errors"
"fmt"
"os"
"strconv"
"strings"
"unicode"
)
type Stack struct {
identity rune
then *Stack
}
func newStack() *Stack {
return nil
}
func push(box rune, stack *Stack) *Stack {
var retval *Stack = new(Stack)
retval.identity = box
retval.then = stack
return retval
}
func pushToEnd(box rune, stack *Stack) *Stack {
if stack == nil {
retval := new(Stack)
retval.identity = box
retval.then = nil
return retval
} else {
stack.then = pushToEnd(box, stack.then)
return stack
}
}
func pushSet(front *Stack, stack *Stack) *Stack {
var prev *Stack = nil
current := front
for current != nil {
prev = current
current = current.then
}
if prev == nil {
return stack
} else {
prev.then = stack
}
return front
}
func pop(stack *Stack) (newStack *Stack, value rune, err error) {
if stack == nil {
newStack = nil
value = 'X'
err = errors.New("attempt to pop from empty stack")
return
}
value = stack.identity
newStack = stack.then
return
}
func popSet(amt int, stack *Stack) (newStack *Stack, set *Stack, err error) {
var prev *Stack = nil
var current *Stack = stack
for i := 0; i < amt; i++ {
if current == nil {
err = errors.New("tried to take more items than existed in stack")
return
}
prev = current
current = current.then
}
if prev != nil {
set = stack
prev.then = nil
} else {
set = nil
}
newStack = current
return
}
func stackLength(stack *Stack) int {
if stack == nil {
return 0
} else {
return 1 + stackLength(stack.then)
}
}
type Workspace []*Stack
func addLine(line string, input *Workspace) (err error) {
currentColumn := 0
fmt.Println("Workspace line's length is", len(line))
for len(line) >= 3 {
fmt.Println(" line length now", len(line))
// make sure we've got enough space in the workspace
for currentColumn+1 >= len(*input) {
*input = append(*input, newStack())
}
// see if there's actually something there
if line[0] == '[' && unicode.IsLetter(rune(line[1])) {
fmt.Println("Adding", rune(line[1]), "to column", currentColumn)
(*input)[currentColumn] = pushToEnd(rune(line[1]), (*input)[currentColumn])
} else if line[0] != ' ' {
err = errors.New("doesn't start with square or blank")
return
}
currentColumn += 1
if len(line) == 3 {
return
}
if len(line) > 3 {
line = line[4:]
}
}
return
}
func runMove(problem int, move OperatorMove, workspace *Workspace) error {
switch problem {
case 1:
for i := 0; i < move.count; i++ {
newFrom, value, err := pop((*workspace)[move.from])
if err != nil {
return err
}
(*workspace)[move.from] = newFrom
(*workspace)[move.to] = push(value, (*workspace)[move.to])
}
return nil
case 2:
newFrom, poppedSet, err := popSet(move.count, (*workspace)[move.from])
if err != nil {
return err
}
(*workspace)[move.from] = newFrom
(*workspace)[move.to] = pushSet(poppedSet, (*workspace)[move.to])
return nil
default:
return errors.New("weird problem number found in runMove")
}
}
func printWorkspace(input Workspace) {
maxLength := 0
for _, element := range input {
current := stackLength(element)
if current > maxLength {
maxLength = current
}
}
for _, element := range input {
current := stackLength(element)
blanks := maxLength - current
outputString := ""
for i := 0; i < blanks; i++ {
outputString = outputString + " "
}
node := element
for node != nil {
outputString = outputString + fmt.Sprintf("[%c] ", node.identity)
node = node.then
}
fmt.Println(outputString)
}
}
type OperatorMove struct {
count int
from int
to int
}
func parseMove(line string) (move OperatorMove, err error) {
words := strings.Split(line, " ")
if len(words) != 6 {
err = errors.New("wrong number of words in move line")
return
}
if words[0] != "move" || words[2] != "from" || words[4] != "to" {
err = fmt.Errorf("illegal words in move line: %s/%s/%s", words[0], words[2], words[4])
return
}
count, cerr := strconv.Atoi(words[1])
from, ferr := strconv.Atoi(words[3])
to, terr := strconv.Atoi(words[5])
if cerr != nil || ferr != nil || terr != nil {
err = fmt.Errorf("illegal number found: %s/%s/%s", words[1], words[3], words[5])
}
move = OperatorMove{count, from - 1, to - 1}
return
}
func Run(filename string, problemStr string) {
problem, err := strconv.Atoi(problemStr)
if err != nil || (problem < 1 || problem > 2) {
fmt.Println("Didn't understand problem number", os.Args[2], "should be 1 or 2")
}
file, err := os.Open(filename)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
workspace := new(Workspace)
moves := []OperatorMove{}
for scanner.Scan() {
line := scanner.Text()
if strings.Contains(line, "[") {
fmt.Println("Adding workspace line")
addLine(line, workspace)
} else {
move, err := parseMove(line)
if err == nil {
fmt.Println("Adding move", move)
moves = append(moves, move)
} else {
fmt.Println("Skipping dead line")
}
}
}
file.Close()
printWorkspace(*workspace)
for count, move := range moves {
fmt.Println("-----", count+1, "-----")
runMove(problem, move, workspace)
printWorkspace(*workspace)
}
resultString := ""
for _, stack := range *workspace {
if stack == nil {
resultString += "*"
} else {
resultString += fmt.Sprintf("%c", rune((*stack).identity))
}
}
fmt.Println("Final string is", resultString)
}