package main import ( "bufio" "fmt" "os" "strings" ) type Step struct { ID byte Requires []byte RequiredBy []byte } type Puzzle struct { Steps []Step } func findStep(steps []Step, ID byte) int { for t := range steps { if steps[t].ID == ID { return t } } return -1 } func findFirst(steps []Step) int { for t := range steps { if len(steps[t].Requires) == 0 { return t } } return -1 } func findLast(steps []Step) int { for t := range steps { if len(steps[t].RequiredBy) == 0 { return t } } return -1 } // a is a superset of b func contains(a []byte, b []byte) bool { if len(b) == 0 { return true } for _, bc := range b { found := false for _, ac := range a { if ac == bc { found = true break } } if !found { return false } } return true } func (p *Puzzle) Add(ID byte, requires byte) { t := findStep(p.Steps, ID) if t == -1 { p.Steps = append(p.Steps, Step{ ID: ID, Requires: make([]byte, 0, 10), RequiredBy: make([]byte, 0, 10), }) t = len(p.Steps) - 1 } p.Steps[t].RequiredBy = append(p.Steps[t].RequiredBy, requires) t = findStep(p.Steps, requires) if t == -1 { p.Steps = append(p.Steps, Step{ ID: requires, Requires: make([]byte, 0, 10), }) t = len(p.Steps) - 1 } p.Steps[t].Requires = append(p.Steps[t].Requires, ID) } func (p *Puzzle) Solution1() string { avail := make([]byte, 0, 10) visited := make([]byte, 0, 10) for _, step := range p.Steps { if contains(visited, step.Requires) { fmt.Println(string(step.ID), "->", string(step.Requires), "/", string(step.RequiredBy)) avail = append(avail, step.ID) } } for { if len(avail) == 0 { break } next := byte('[') for _, c := range avail { if c < next { next = c } } alreadyVisited := false for _, c := range visited { if c == next { alreadyVisited = true break } } if alreadyVisited { break } visited = append(visited, next) curr := findStep(p.Steps, next) for _, c := range p.Steps[curr].RequiredBy { step := findStep(p.Steps, c) if contains(visited, p.Steps[step].Requires) && !contains(avail, []byte{c}) { avail = append(avail, c) } } fmt.Println("\ncurr", string(p.Steps[curr].ID)) fmt.Println("avail", string(avail)) fmt.Println("visited", string(visited)) for t, c := range avail { if next == c { avail = append(avail[:t], avail[t+1:]...) break } } } return string(visited) } type Worker struct { Current byte Left int } func (w *Worker) Assign(c byte) { w.Current = c w.Left = int(c-'A'+1) + 60 } func (w *Worker) Tick() { w.Left-- } func (w *Worker) Done() bool { return (w.Left == 0) } func (p *Puzzle) Solution2() int { workers := make([]Worker, 5) avail := make([]byte, 0, 10) visited := make([]byte, 0, 10) done := make([]byte, 0, 10) for _, step := range p.Steps { if contains(visited, step.Requires) { fmt.Println(string(step.ID), "->", string(step.Requires), "/", string(step.RequiredBy)) avail = append(avail, step.ID) } } count := 0 fmt.Printf("Second") for t := range workers { workers[t].Current = '.' fmt.Printf("\tWorker%d", t) } fmt.Printf("\tAvail") fmt.Println("\tDone") for { for i := range workers { if workers[i].Done() { if workers[i].Current != 0 && workers[i].Current != '.' { if !contains(done, []byte{workers[i].Current}) { done = append(done, workers[i].Current) } for _, step := range p.Steps { if contains(done, step.Requires) && !contains(visited, []byte{step.ID}) && !contains(avail, []byte{step.ID}) && !contains(done, []byte{step.ID}) { avail = append(avail, step.ID) } } workers[i].Current = '.' } ni := -1 next := byte('[') for t, c := range avail { if c < next { next = c ni = t } } if next == '[' { continue } avail = append(avail[:ni], avail[ni+1:]...) if !contains(visited, []byte{next}) { visited = append(visited, next) } workers[i].Assign(next) } workers[i].Tick() } fmt.Printf("%4d", count) for _, worker := range workers { fmt.Printf("\t%4c", worker.Current) } fmt.Printf("\t%s", avail) fmt.Printf("\t%s\n", done) if len(done) == len(p.Steps) { break } count++ } return count } func main() { input, err := os.Open("input.txt") if err != nil { panic(err.Error()) } defer input.Close() scanner := bufio.NewScanner(input) p := Puzzle{ Steps: make([]Step, 0, 10), } for scanner.Scan() { words := strings.Split(scanner.Text(), " ") p.Add(words[1][0], words[7][0]) } if err := scanner.Err(); err != nil { panic(err.Error()) } fmt.Println(p.Solution1()) fmt.Println("-->", p.Solution2(), "<--") }