2022-19 attempt 1

Signed-off-by: AKP <tom@tdpain.net>

2022-19 attempt 2

Signed-off-by: AKP <tom@tdpain.net>

I think this is semi-functional

Signed-off-by: AKP <tom@tdpain.net>

2022-19

Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
akp 2022-12-19 17:58:41 +00:00
parent 7846d9675f
commit 8572888192
No known key found for this signature in database
GPG key ID: AA5726202C8879B7
8 changed files with 235 additions and 3 deletions

View file

@ -0,0 +1,3 @@
# [Day 19: Not Enough Minerals](https://adventofcode.com/2022/day/19)
My part 2 took 48 minutes to run. ***48!!*** Even though part 1 only took 17! aaaaaaaaaaa

View file

@ -0,0 +1,15 @@
{
"day": 19,
"dir": "challenges/2022/19-notEnoughMinerals",
"implementations": {
"Python": {
"part.1.avg": 17.7188,
"part.1.max": 17.7188,
"part.1.min": 17.7188,
"part.2.avg": 2903.2192,
"part.2.max": 2903.2192,
"part.2.min": 2903.2192
}
},
"numRuns": 1
}

View file

@ -0,0 +1,17 @@
{
"inputFile": "input.txt",
"testCases": {
"one": [
{
"input": "Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.\nBlueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.\n",
"expected": "33"
}
],
"two": [
{
"input": "Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.\nBlueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.\n",
"expected": "3472"
}
]
}
}

View file

@ -0,0 +1,143 @@
from typing import *
from aocpy import BaseChallenge, foldl
from dataclasses import dataclass
import re
from enum import Enum
class Material(Enum):
ORE = 0
CLAY = 1
OBSIDIAN = 2
GEODE = 3
MaterialTracker = Tuple[int, int, int, int]
@dataclass(init=False)
class Blueprint:
number: int
robots: Dict[Material, MaterialTracker]
def __init__(self, number: int, ore_robot_cost: int, clay_robot_cost: int, obsidian_robot_cost_ore: int, obsidian_robot_cost_clay: int, geode_robot_cost_ore: int, geode_robot_cost_obsidian: int):
self.number = number
self.robots = {
Material.ORE: (ore_robot_cost, 0, 0, 0),
Material.CLAY: (clay_robot_cost, 0, 0, 0),
Material.OBSIDIAN: (obsidian_robot_cost_ore, obsidian_robot_cost_clay, 0, 0),
Material.GEODE: (geode_robot_cost_ore, 0, geode_robot_cost_obsidian, 0),
}
def iter_robots(self) -> Generator[Tuple[Material, MaterialTracker], None, None]:
for key in self.robots:
yield (key, self.robots[key])
def __hash__(self) -> int:
return hash(self.number)
parse_re = re.compile(r"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\.")
def parse(instr: str) -> List[Blueprint]:
res: List[Blueprint] = []
for line in instr.strip().splitlines():
res.append(
Blueprint(*map(int, parse_re.match(line).groups())),
)
return res
def calc_max_geodes(blueprint: Blueprint, max_time: int, materials: MaterialTracker, robots: MaterialTracker, robot_quota: MaterialTracker, minute: int, cannot_build: int) -> int:
if minute == max_time + 1:
return materials[Material.GEODE.value]
try_build: int = 0
for (robot_type, robot_materials) in blueprint.iter_robots():
rtv = robot_type.value
if cannot_build & (1 << rtv) != 0:
continue
if robot_type != Material.GEODE and robots[rtv] == robot_quota[rtv]:
continue
has_enough_materials = True
for (required, available) in zip(robot_materials, materials):
if required > available:
has_enough_materials = False
break
if has_enough_materials:
try_build = try_build | (1 << robot_type.value)
materials = (
materials[0] + robots[0],
materials[1] + robots[1],
materials[2] + robots[2],
materials[3] + robots[3],
)
max_score = 0
for i in range(5):
if i == 4 and cannot_build | try_build != 0b1111:
# always try not building anything
sc = calc_max_geodes(blueprint, max_time, materials, robots, robot_quota, minute + 1, cannot_build | try_build)
else:
if try_build & (1 << i) == 0:
continue
robot_type = Material(i)
robot_materials = blueprint.robots[robot_type]
# subtract materials required to build this robot
mc = (
materials[0] - robot_materials[0],
materials[1] - robot_materials[1],
materials[2] - robot_materials[2],
materials[3],
)
# update robot counts
rc = tuple((robots[j] + (1 if i == j else 0) for j in range(4)))
# recurse
sc = calc_max_geodes(blueprint, max_time, mc, rc, robot_quota, minute + 1, 0)
if sc is not None and sc > max_score:
max_score = sc
return max_score
class Challenge(BaseChallenge):
@staticmethod
def one(instr: str) -> int:
inp = parse(instr)
quals: List[int] = []
for bp in inp:
robot_quota: Tuple[int] = tuple(max(x) for x in zip(*bp.robots.values()))
quals.append(
calc_max_geodes(bp, 24, (0,0,0,0), (1,0,0,0), robot_quota, 1, 0) * bp.number,
)
return sum(quals)
@staticmethod
def two(instr: str) -> int:
inp = parse(instr)
nums: List[int] = []
for bp in inp[:min(3, len(inp))]:
print(f"{bp.number=}", flush=True)
robot_quota: Tuple[int] = tuple(max(x) for x in zip(*bp.robots.values()))
nums.append(
calc_max_geodes(bp, 32, (0,0,0,0), (1,0,0,0), robot_quota, 1, 0)
)
print(nums[-1], flush=True)
return foldl(lambda x, y: x * y, nums, 1)

View file

@ -0,0 +1,50 @@
from py import Challenge
import time
import json
# TASKS_STR = input()
# TASKS = json.loads(TASKS_STR)
def send_result(task_id, ok, output, duration):
print(json.dumps({
"task_id": task_id,
"ok": ok,
"output": str(output) if output is not None else "",
"duration": float(duration),
}), flush=True)
while True:
task = json.loads(input())
taskPart = task["part"]
task_id = task["task_id"]
run = None
if taskPart == 1:
run = lambda: Challenge.one(task["input"])
elif taskPart == 2:
run = lambda: Challenge.two(task["input"])
elif taskPart == 3:
run = lambda: Challenge.vis(task["input"], task["output_dir"])
else:
send_result(task_id, False, "unknown task part", 0)
continue
start_time = time.time()
res = None
err = None
try:
res = run()
except Exception as e:
err = f"{type(e)}: {e}"
import traceback
err = f"{type(e)}: {e}\n{''.join(traceback.format_tb(e.__traceback__))}"
end_time = time.time()
running_time = end_time-start_time
if err is not None:
send_result(task_id, False, err, running_time)
else:
send_result(task_id, True, res, running_time)

View file

@ -33,4 +33,5 @@ The red dotted line denotes 15 seconds.
| 15 - Beacon Exclusion Zone | ★ ★ | [Python](15-beaconExclusionZone/py/__init__.py) | Searching through a 4000000^2 size grid for a literal single empty spot |
| 16 - Proboscidea Volcanium | ★ ★ | [Python](16-proboscideaVolcanium/py/__init__.py) | Nasty combinatorics |
| 17 - Pyroclastic Flow | ★ ★ | [Python](17-pyroclasticFlow/py/__init__.py) | Detecting cycles in a large amount of knock-off Tetris. |
| 18 - Boiling Boulders | ★ ★ | [Python](18-boilingBoulders/py/__init__.py) | Finding the surface area of a shape specified by a list of unit cubes. |
| 18 - Boiling Boulders | ★ ★ | [Python](18-boilingBoulders/py/__init__.py) | Finding the surface area of a shape specified by a list of unit cubes. |
| 19 - Not Enough Minerals | ★ ★ | [Python](19-notEnoughMinerals/py/__init__.py) | Finding the most effective sequence of operations to complete a specific task. |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Before After
Before After

View file

@ -26,6 +26,7 @@ var args struct {
Benchmark bool `arg:"-b,--benchmark" help:"benchmark a day's implementations'"`
BenchmarkN int `arg:"-n,--benchmark-n" help:"Number of iterations to run for benchmarking" default:"1000"`
TestOnly bool `arg:"-t,--test-only" help:"Only run test inputs"`
NoTest bool `arg:"-x,--no-test" help:"Do not run test inputs"`
Visualise bool `arg:"-g,--visualise" help:"Run visualisation generation"`
}
@ -123,8 +124,10 @@ func run() error {
fmt.Print("Running...\n\n")
if err := runTests(runner, challengeInfo); err != nil {
return err
if !args.NoTest {
if err := runTests(runner, challengeInfo); err != nil {
return err
}
}
if !args.TestOnly {