added day14 solutions, weighted graph BF traversals

This commit is contained in:
Alex Chao
2020-08-04 16:15:19 -04:00
parent 9c85f180ad
commit 1b84060b80
3 changed files with 218 additions and 0 deletions
+1
View File
@@ -19,3 +19,4 @@ Day | Name | Type of Algo & Notes
11 | Space Police | - More Intcode stuff... <br> - __2D Array/Slice manipulation__ and a bit of maths/graphing <br> - Implemented a __RotateGrid__ algo
12 | The N-Body Problem | I like to call this a _(harmonic) frequency_ algo. Finding the harmonic frequency of multiple bodies/items and then finding the Least Common Multiple of those frequencies will tell you when all the bodies have returned to their initial state. <br> - I've used this approach for a leetcode problem about prisoners in jail cells too
13 | Care Package | Intcode again! It's basically every other day... <br> - part1: 2D array manipulation again <br> - part2: holy algo, some logic to basically play Bricks game. <br> - This is more of a design question for how you manage state
14 | Space Stoichiometry | __Weighted Graph and Breadth First Traversals__ <br> - Because not all of the products have a quantity of 1, it complicates the graph's data model. I ended up mapping the product/chemical name to a map of its stoichiometry where the product's number is positive & reactants were negative. <br> - part2: not just a simple division because of "extra byproducts" of generating each piece of fuel. I just let this brute force thing run for ~30 seconds...
+102
View File
@@ -0,0 +1,102 @@
package main
import (
"adventofcode/util"
"fmt"
"strconv"
"strings"
)
func main() {
input := util.ReadFile("../input.txt")
stoichiometryGraph := makeDependencyGraph(strings.Split(input, "\n"))
neededChemicals := map[string]int{"FUEL": 1}
// fmt.Println(stoichiometryGraph, neededChemicals)
// while the neededChemicals map has positive values for keys besides "ORE"
for !isOnlyOres(neededChemicals) {
// iterate through neededChemicals
for neededChemical, quantityNeeded := range neededChemicals {
// if a positive value is found for a neededChemical besides "ORE"
if quantityNeeded > 0 && neededChemical != "ORE" {
// find its reaction in the stoichiometryGraph
reactionStoichiometry := stoichiometryGraph[neededChemical]
// determine the number of times the reactionStoichiometry must be run
timesToRun := quantityNeeded / reactionStoichiometry[neededChemical]
if quantityNeeded%reactionStoichiometry[neededChemical] > 0 {
timesToRun++
}
// decrement all of the values in neededChemicals map with this reaction's details (* timesToRun)
for reactionChemical, chemicalStoich := range reactionStoichiometry {
neededChemicals[reactionChemical] -= chemicalStoich * timesToRun
}
}
}
}
// Print final output
fmt.Println("ORE needed", neededChemicals["ORE"])
}
// Creates a graph that maps the products name to its full reaction stoichiometry
func makeDependencyGraph(reactions []string) map[string]map[string]int {
graph := make(map[string]map[string]int)
for _, reaction := range reactions {
product, reactionStoichiometry := parseReaction(reaction)
graph[product] = reactionStoichiometry
}
return graph
}
// parseReaction takes in a line of the input i.e. a reaction in a string
// parses all its details and returns the generated product as a string
// and a map of all products to the quantity used/produced by the reaction
// for produced chemicals, map value will be > 0, inputs will be < 0
func parseReaction(reaction string) (string, map[string]int) {
reactionStoichiometry := make(map[string]int)
splitByArrow := strings.Split(reaction, " => ")
productStr := splitByArrow[1]
reactantsStr := splitByArrow[0]
// handle product
productQty, productName := parseQtyAndName(productStr)
reactionStoichiometry[productName] = productQty
// split reactants via comma first
reactantsSli := strings.Split(reactantsStr, ", ")
for _, str := range reactantsSli {
reactantQuantity, reactantName := parseQtyAndName(str)
reactionStoichiometry[reactantName] = -1 * reactantQuantity
}
return productName, reactionStoichiometry
}
// parse an inputted string of the for "<number> <string>" and return the int & string
func parseQtyAndName(input string) (int, string) {
split := strings.Split(input, " ")
quantity, _ := strconv.Atoi(split[0])
name := split[1]
return quantity, name
}
// helper function to determine if the neededChemicals graph is "complete"
// it is complete if there only positive value is for the chemical "ORE"
func isOnlyOres(neededChemicals map[string]int) bool {
for key, val := range neededChemicals {
if key != "ORE" && val > 0 {
return false
}
}
return true
}
+115
View File
@@ -0,0 +1,115 @@
package main
import (
"adventofcode/util"
"fmt"
"strconv"
"strings"
)
// const will be comparable to any int type?
const trillion int = 1000000000000
func main() {
input := util.ReadFile("../input.txt")
stoichiometryGraph := makeDependencyGraph(strings.Split(input, "\n"))
// give the brute force algo a big headstart? 1 million fuels to start?
var fuelMade int = 1000000
neededChemicals := map[string]int{"FUEL": fuelMade}
// brute force this while the ore count is below 1 trillion
for neededChemicals["ORE"] <= trillion {
neededChemicals["FUEL"]++
// while the neededChemicals map has positive values for keys besides "ORE"
for !isOnlyOres(neededChemicals) {
// iterate through neededChemicals
for neededChemical, quantityNeeded := range neededChemicals {
// if a positive value is found for a neededChemical besides "ORE"
if quantityNeeded > 0 && neededChemical != "ORE" {
// find its reaction in the stoichiometryGraph
reactionStoichiometry := stoichiometryGraph[neededChemical]
// determine the number of times the reactionStoichiometry must be run
timesToRun := quantityNeeded / reactionStoichiometry[neededChemical]
if quantityNeeded%reactionStoichiometry[neededChemical] > 0 {
timesToRun++
}
// decrement all of the values in neededChemicals map with this reaction's details (* timesToRun)
for reactionChemical, chemicalStoich := range reactionStoichiometry {
neededChemicals[reactionChemical] -= chemicalStoich * timesToRun
}
}
}
}
// if you don't exceed the 1 trillion ORE, increment fuelMade
if neededChemicals["ORE"] < trillion {
fuelMade++
}
fmt.Println("Making FUEL... Remaining ORE:", trillion-neededChemicals["ORE"])
}
// Print final output
fmt.Println("\nFinal amount of FUEL", fuelMade)
}
// Creates a graph that maps the products name to its full reaction stoichiometry
func makeDependencyGraph(reactions []string) map[string]map[string]int {
graph := make(map[string]map[string]int)
for _, reaction := range reactions {
product, reactionStoichiometry := parseReaction(reaction)
graph[product] = reactionStoichiometry
}
return graph
}
// parseReaction takes in a line of the input i.e. a reaction in a string
// parses all its details and returns the generated product as a string
// and a map of all products to the quantity used/produced by the reaction
// for produced chemicals, map value will be > 0, inputs will be < 0
func parseReaction(reaction string) (string, map[string]int) {
reactionStoichiometry := make(map[string]int)
splitByArrow := strings.Split(reaction, " => ")
productStr := splitByArrow[1]
reactantsStr := splitByArrow[0]
// handle product
productQty, productName := parseQtyAndName(productStr)
reactionStoichiometry[productName] = productQty
// split reactants via comma first
reactantsSli := strings.Split(reactantsStr, ", ")
for _, str := range reactantsSli {
reactantQuantity, reactantName := parseQtyAndName(str)
reactionStoichiometry[reactantName] = -1 * reactantQuantity
}
return productName, reactionStoichiometry
}
// parse an inputted string of the for "<number> <string>" and return the int & string
func parseQtyAndName(input string) (int, string) {
split := strings.Split(input, " ")
quantity, _ := strconv.Atoi(split[0])
name := split[1]
return quantity, name
}
// helper function to determine if the neededChemicals graph is "complete"
// it is complete if there only positive value is for the chemical "ORE"
func isOnlyOres(neededChemicals map[string]int) bool {
for key, val := range neededChemicals {
if key != "ORE" && val > 0 {
return false
}
}
return true
}