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) }