Rename shared packages by day instead of vaguely by purpose.
This commit is contained in:
44
2018/internal/day07/breadthfirst.go
Normal file
44
2018/internal/day07/breadthfirst.go
Normal file
@ -0,0 +1,44 @@
|
||||
// BreadthFirstOrder and its companion functions ended up not giving the
|
||||
// right solution, but I'm leaving them here as a useful generic tree
|
||||
// algorithm.
|
||||
package day07
|
||||
|
||||
func BreadthFirstOrder(root *Node) []*Node {
|
||||
seen := make(map[rune]bool)
|
||||
return breadthFirstOrderR(root, seen)
|
||||
}
|
||||
|
||||
func findTreeRoot(depMap map[rune]*Node) *Node {
|
||||
for _, node := range depMap {
|
||||
if len(node.Parents) == 0 {
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func breadthFirstOrderR(node *Node, seen map[rune]bool) []*Node {
|
||||
order := []*Node{}
|
||||
|
||||
// handle root
|
||||
if len(node.Parents) == 0 {
|
||||
order = append(order, node)
|
||||
seen[node.Name] = true
|
||||
}
|
||||
|
||||
// add children we haven't seen before
|
||||
for _, child := range node.Children {
|
||||
if _, ok := seen[child.Name]; !ok {
|
||||
order = append(order, child)
|
||||
seen[child.Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
// recurse
|
||||
for _, child := range node.Children {
|
||||
order = append(order, breadthFirstOrderR(child, seen)...)
|
||||
}
|
||||
|
||||
return order
|
||||
}
|
54
2018/internal/day07/construction.go
Normal file
54
2018/internal/day07/construction.go
Normal file
@ -0,0 +1,54 @@
|
||||
package day07
|
||||
|
||||
func SimulateConstruction(root *Node) int {
|
||||
working := make(map[rune]*Node)
|
||||
done := map[rune]bool{
|
||||
0: true,
|
||||
}
|
||||
elapsed := 0
|
||||
|
||||
for {
|
||||
// returns true when no work is left to do.
|
||||
if tick(root, working, done) {
|
||||
return elapsed
|
||||
}
|
||||
|
||||
elapsed++
|
||||
}
|
||||
}
|
||||
|
||||
func tick(root *Node, working map[rune]*Node, done map[rune]bool) bool {
|
||||
complete := true
|
||||
|
||||
// Find candidates, if we need new ones.
|
||||
if len(working) < 5 {
|
||||
candidates := findCandidates(root, done)
|
||||
for _, candidate := range candidates {
|
||||
if len(working) >= 5 {
|
||||
break
|
||||
}
|
||||
|
||||
// if we see a candidate that we can work on, put it in the map.
|
||||
if working[candidate.Name] == nil {
|
||||
working[candidate.Name] = candidate
|
||||
complete = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// work on each in-progress node.
|
||||
for _, node := range working {
|
||||
complete = false
|
||||
node.Worked++
|
||||
|
||||
// if we're done, add it to done and delete from worker queue
|
||||
// `-4` is exploiting the fact that the rune capital A is value
|
||||
// 65 as an integer (and so on)
|
||||
if node.Worked >= int(node.Name)-4 {
|
||||
done[node.Name] = true
|
||||
delete(working, node.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return complete
|
||||
}
|
33
2018/internal/day07/debug.go
Normal file
33
2018/internal/day07/debug.go
Normal file
@ -0,0 +1,33 @@
|
||||
package day07
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func DebugPrintTree(root *Node) {
|
||||
debugPrintTreeR(root, 0)
|
||||
}
|
||||
|
||||
func debugPrintTreeR(node *Node, level int) {
|
||||
for i := 0; i < level; i++ {
|
||||
fmt.Printf(" ")
|
||||
}
|
||||
|
||||
DebugPrintNode(node)
|
||||
|
||||
for _, child := range node.Children {
|
||||
debugPrintTreeR(child, level+1)
|
||||
}
|
||||
}
|
||||
|
||||
func DebugPrintNode(node *Node) {
|
||||
fmt.Printf("%c @ %p. Children: ", node.Name, node)
|
||||
for _, child := range node.Children {
|
||||
fmt.Printf("%c ", child.Name)
|
||||
}
|
||||
fmt.Printf("Parents: ")
|
||||
for _, parent := range node.Parents {
|
||||
fmt.Printf("%c ", parent.Name)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
46
2018/internal/day07/findorder.go
Normal file
46
2018/internal/day07/findorder.go
Normal file
@ -0,0 +1,46 @@
|
||||
package day07
|
||||
|
||||
// FindOrder determines the correct order according to these rules:
|
||||
// 1. A node cannot come before all of its parents have been included.
|
||||
// 2. At any given time, the lowest-lettered available step must be executed.
|
||||
func FindOrder(root *Node) []*Node {
|
||||
seen := map[rune]bool{
|
||||
0: true,
|
||||
}
|
||||
order := []*Node{}
|
||||
|
||||
for {
|
||||
// continuously loop over the tree, retrieving the next usable node.
|
||||
candidates := findCandidates(root, seen)
|
||||
|
||||
if len(candidates) == 0 {
|
||||
return order
|
||||
}
|
||||
sortNodes(candidates)
|
||||
order = append(order, candidates[0])
|
||||
seen[candidates[0].Name] = true
|
||||
}
|
||||
}
|
||||
|
||||
func findCandidates(node *Node, seen map[rune]bool) []*Node {
|
||||
candidates := []*Node{}
|
||||
|
||||
// if we've been seen before, we're clear to recurse
|
||||
if seen[node.Name] == true {
|
||||
for _, child := range node.Children {
|
||||
candidates = append(candidates, findCandidates(child, seen)...)
|
||||
}
|
||||
return candidates
|
||||
}
|
||||
|
||||
// add the node, but only if all its parents have been seen
|
||||
for _, parent := range node.Parents {
|
||||
if seen[parent.Name] == false {
|
||||
return candidates
|
||||
}
|
||||
}
|
||||
|
||||
// We haven't seen this one before, and its parents are seen.
|
||||
// Return it as a candidate.
|
||||
return append(candidates, node)
|
||||
}
|
67
2018/internal/day07/tree.go
Normal file
67
2018/internal/day07/tree.go
Normal file
@ -0,0 +1,67 @@
|
||||
package day07
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Name rune
|
||||
Parents []*Node
|
||||
Children []*Node
|
||||
Worked int // the amount of time spent working on this node.
|
||||
}
|
||||
|
||||
// BuildDependencyTree parses the input and returns the root node.
|
||||
func BuildDependencyTree(data []string) *Node {
|
||||
depMap := make(map[rune]*Node)
|
||||
parentRegex := regexp.MustCompile("Step ([A-Z]) must")
|
||||
childRegex := regexp.MustCompile("step ([A-Z]) can begin")
|
||||
|
||||
for _, line := range data {
|
||||
parent := rune(parentRegex.FindSubmatch([]byte(line))[1][0])
|
||||
child := rune(childRegex.FindSubmatch([]byte(line))[1][0])
|
||||
|
||||
// Create the nodes if they don't exist
|
||||
if depMap[parent] == nil {
|
||||
depMap[parent] = &Node{Name: parent}
|
||||
}
|
||||
if depMap[child] == nil {
|
||||
depMap[child] = &Node{Name: child}
|
||||
}
|
||||
|
||||
// Get each node and add the dependency info.
|
||||
pNode := depMap[parent]
|
||||
cNode := depMap[child]
|
||||
|
||||
pNode.Children = append(pNode.Children, cNode)
|
||||
cNode.Parents = append(cNode.Parents, pNode)
|
||||
}
|
||||
|
||||
// sort the children in each node
|
||||
for _, node := range depMap {
|
||||
sortNodes(node.Children)
|
||||
}
|
||||
|
||||
return BuildTreeRoot(depMap)
|
||||
}
|
||||
|
||||
func sortNodes(nodes []*Node) {
|
||||
sort.Slice(nodes[:], func(i, j int) bool {
|
||||
return nodes[i].Name < nodes[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
func BuildTreeRoot(depMap map[rune]*Node) *Node {
|
||||
root := &Node{Name: 0}
|
||||
|
||||
for _, node := range depMap {
|
||||
if len(node.Parents) == 0 {
|
||||
root.Children = append(root.Children, node)
|
||||
node.Parents = append(node.Parents, root)
|
||||
}
|
||||
}
|
||||
|
||||
sortNodes(root.Children)
|
||||
return root
|
||||
}
|
Reference in New Issue
Block a user