Rename shared packages by day instead of vaguely by purpose.

This commit is contained in:
2018-12-10 01:06:47 -05:00
parent 42cb05ba7f
commit 31f60eca0e
22 changed files with 49 additions and 50 deletions

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

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

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

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

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