🌅
This commit is contained in:
282
solutions/day5/day5.go
Normal file
282
solutions/day5/day5.go
Normal file
@@ -0,0 +1,282 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user