2022 Day 19 Complete

This commit is contained in:
2022-12-27 16:32:19 -06:00
parent 15631b3564
commit d940cade58
2 changed files with 343 additions and 212 deletions

View File

@@ -2,175 +2,122 @@ package main
import (
"fmt"
"strings"
"time"
h "git.bullercodeworks.com/brian/adventofcode/helpers"
)
type blueprint struct {
id int
oreBot oreBot
clayBot clayBot
obsidianBot obsidianBot
geodeBot geodeBot
}
type oreBot struct{ oreCost int }
type clayBot struct{ oreCost int }
type obsidianBot struct{ oreCost, clayCost int }
type geodeBot struct{ oreCost, obsidianCost int }
var globalBest = 0
func main() {
blueprints := parseInput(h.StdinToStringSlice())
part1(blueprints)
part2(blueprints)
}
func part1(blueprints []blueprint) {
result := 0
for _, bp := range blueprints {
result += bp.id * search(bp, 0, 0, 0, 24, 1, 0, 0, 0, 0)
globalBest = 0
}
fmt.Println("# Part 1")
fmt.Println(result)
fmt.Println()
inp := h.StdinToStringSlice()
part1(inp)
}
func part1(inp []string) {
blueprints := ParseBlueprints(inp)
robots := []Robot{{tp: ResOre}}
var ticks int
resources := make(map[Resource]int)
var done bool
printState(robots, resources)
useBP := blueprints[0]
_ = useBP
// TODO: Get Ratios of needed resources
for !done {
// First check if we can build some bots
if useBP.CanBuild(ResGeode, resources) {
resources = useBP.Build(resGeode, resources)
}
if useBP.CanBuild(ResObsidian, resources) {
resources = useBP.Build(resObsidian, resources)
}
if useBP.CanBuild(ResClay, resources) {
resources = useBP.Build(resClay, resources)
}
// Every tick, each robot gathers one of it's resource
for i := range robots {
resources[robots[i].tp]++
}
ticks++
fmt.Println(h.CLEAR_SCREEN)
printState(robots, resources)
time.Sleep(time.Second / 10)
func part2(blueprints []blueprint) {
if len(blueprints) < 3 {
fmt.Println("# Part 2")
fmt.Println("Error: Not enough Blueprints")
return
}
}
func printState(robots []Robot, resources map[Resource]int) {
var oreBots, clayBots, obsBots, geodeBots int
for i := range robots {
switch robots[i].tp {
case ResOre:
oreBots++
case ResClay:
clayBots++
case ResObsidian:
obsBots++
case ResGeode:
geodeBots++
}
result := 1
for i := 0; i < 3; i++ {
result *= search(blueprints[i], 0, 0, 0, 32, 1, 0, 0, 0, 0)
globalBest = 0
}
fmt.Printf(
"Bots: %2d Ore, %2d Clay, %2d Obsidian, %2d Geode\n",
oreBots, clayBots, obsBots, geodeBots)
fmt.Printf("[ Ore : %2d ]\n", resources[ResOre])
fmt.Printf("[ Clay : %2d ]\n", resources[ResClay])
fmt.Printf("[ Obsidian: %2d ]\n", resources[ResObsidian])
fmt.Printf("[ Geode : %2d ]\n", resources[ResGeode])
fmt.Println("# Part 2")
fmt.Println(result)
}
type Resource int
const (
ResOre = iota
ResClay
ResObsidian
ResGeode
ResError
)
type Robot struct {
tp Resource
}
type Cost struct {
res Resource
count int
}
type Blueprint struct {
costs map[Resource][]Cost
}
func (b Blueprint) CanBuild(tp Resource, resources map[Resource]int) bool {
cost := b.costs[tp]
for _, c := range cost {
if resources[c.res] < c.count {
return false
}
func search(bp blueprint, ore, clay, obs, time, oreBots, clayBots, obsidianBots, geodeBots, geodes int) int {
if time == 0 || globalBest >= geodes+rangeSum(geodeBots, geodeBots+time-1) {
return 0
}
return true
}
func (b Blueprint) String() string {
ret := fmt.Sprintf("Blueprint contains %d Recipes.\n", len(b.costs))
for _, tp := range []Resource{ResOre, ResClay, ResObsidian, ResGeode} {
if v, ok := b.costs[tp]; ok {
ret = ret + fmt.Sprintf("Each %s robot costs %d %s", tp.String(), v[0].count, v[0].res.String())
if len(v) > 1 {
ret = ret + fmt.Sprintf(" and %d %s", v[1].count, v[1].res.String())
}
ret = ret + ". "
}
if oreBots >= bp.geodeBot.oreCost && obsidianBots >= bp.geodeBot.obsidianCost {
return rangeSum(geodeBots, geodeBots+time-1)
}
return ret
oreLimitHit := oreBots >= h.Max(bp.geodeBot.oreCost, h.Max(bp.clayBot.oreCost, bp.obsidianBot.oreCost))
clayLimitHit := clayBots >= bp.obsidianBot.clayCost
obsLimitHit := obsidianBots >= bp.geodeBot.obsidianCost
best := 0
if !oreLimitHit {
best = h.Max(
best,
geodeBots+search(
bp, ore+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.oreBot.oreCost && !oreLimitHit {
best = h.Max(
best,
geodeBots+search(
bp, ore-bp.oreBot.oreCost+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots+1, clayBots, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.clayBot.oreCost && !clayLimitHit {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.clayBot.oreCost+oreBots, clay+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots+1, obsidianBots, geodeBots, geodes+geodeBots))
}
if ore >= bp.obsidianBot.oreCost && clay >= bp.obsidianBot.clayCost && !obsLimitHit {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.obsidianBot.oreCost+oreBots, clay-bp.obsidianBot.clayCost+clayBots, obs+obsidianBots,
time-1, oreBots, clayBots, obsidianBots+1, geodeBots, geodes+geodeBots))
}
if ore >= bp.geodeBot.oreCost && obs >= bp.geodeBot.obsidianCost {
best = h.Max(
best, geodeBots+search(
bp, ore-bp.geodeBot.oreCost+oreBots, clay+clayBots, obs-bp.geodeBot.obsidianCost+obsidianBots,
time-1, oreBots, clayBots, obsidianBots, geodeBots+1, geodes+geodeBots))
}
globalBest = h.Max(best, globalBest)
return best
}
func ParseBlueprints(inp []string) []Blueprint {
var ret []Blueprint
func rangeSum(first, last int) int {
return last*(last+1)/2 - ((first - 1) * first / 2)
}
func parseInput(inp []string) []blueprint {
var ret []blueprint
var id int
oreBot := oreBot{}
clayBot := clayBot{}
obsidianBot := obsidianBot{}
geodeBot := geodeBot{}
for i := range inp {
costs := make(map[Resource][]Cost)
wrk := strings.Split(inp[i], ": ")[1]
botReqs := strings.Split(wrk, ".")
for j := range botReqs {
if len(botReqs[j]) > 0 {
fields := strings.Fields(botReqs[j])
bot := ResourceFromString(fields[1])
var req []Cost
req = append(req, Cost{
res: ResourceFromString(fields[5]),
count: h.Atoi(fields[4]),
})
if len(fields) > 6 {
req = append(req, Cost{
res: ResourceFromString(fields[8]),
count: h.Atoi(fields[7]),
})
}
costs[bot] = req
}
}
ret = append(ret, Blueprint{costs: costs})
fmt.Sscanf(inp[i], "Blueprint %d: Each ore robot costs %d ore. Each clay robot costs %d ore. Each obsidian robot costs %d ore and %d clay. Each geode robot costs %d ore and %d obsidian.", &id, &oreBot.oreCost, &clayBot.oreCost, &obsidianBot.oreCost, &obsidianBot.clayCost, &geodeBot.oreCost, &geodeBot.obsidianCost)
ret = append(ret, blueprint{id, oreBot, clayBot, obsidianBot, geodeBot})
}
return ret
}
func ResourceFromString(s string) Resource {
switch s {
case "ore":
return ResOre
case "clay":
return ResClay
case "obsidian":
return ResObsidian
case "geode":
return ResGeode
}
return ResError
}
func (r Resource) String() string {
switch r {
case ResOre:
return "ore"
case ResClay:
return "clay"
case ResObsidian:
return "obsidian"
case ResGeode:
return "geode"
}
return "unknown"
}