283 lines
5.4 KiB
Go
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)
|
|
}
|