2022 Day 19 Complete
This commit is contained in:
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user