Code formatting
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
320b990b05
commit
6f282e5761
9 changed files with 125 additions and 101 deletions
|
@ -22,7 +22,9 @@ def parse(instr: str) -> Tuple[List[Stack], List[MoveInstruction]]:
|
|||
state_lines = raw_initial_state.splitlines()
|
||||
state_lines = state_lines[:-1] # the last line only contains digits that
|
||||
# we don't need to look at
|
||||
state_lines = [[line[i : i + 3] for i in range(0, len(line), 4)] for line in state_lines]
|
||||
state_lines = [
|
||||
[line[i : i + 3] for i in range(0, len(line), 4)] for line in state_lines
|
||||
]
|
||||
|
||||
state: List[Stack] = []
|
||||
for _ in range(len(state_lines[0])):
|
||||
|
|
|
@ -3,6 +3,7 @@ from aocpy import BaseChallenge
|
|||
|
||||
Instruction = Tuple[str, Optional[int]]
|
||||
|
||||
|
||||
def parse(instr: str) -> List[Instruction]:
|
||||
res: List[Instruction] = []
|
||||
for line in instr.strip().splitlines():
|
||||
|
@ -66,7 +67,7 @@ class MachineTwo(MachineOne):
|
|||
self.output += "█"
|
||||
else:
|
||||
self.output += " "
|
||||
|
||||
|
||||
def execute(self, ins: Instruction):
|
||||
(opcode, operand) = ins
|
||||
if opcode == "noop":
|
||||
|
@ -81,7 +82,6 @@ class MachineTwo(MachineOne):
|
|||
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
inp = parse(instr)
|
||||
|
@ -89,7 +89,7 @@ class Challenge(BaseChallenge):
|
|||
machine = MachineOne()
|
||||
for ins in inp:
|
||||
machine.execute(ins)
|
||||
|
||||
|
||||
return sum(machine.vals)
|
||||
|
||||
@staticmethod
|
||||
|
@ -99,5 +99,5 @@ class Challenge(BaseChallenge):
|
|||
machine = MachineTwo()
|
||||
for ins in inp:
|
||||
machine.execute(ins)
|
||||
|
||||
|
||||
return "\n" + machine.output.strip()
|
||||
|
|
|
@ -44,11 +44,13 @@ class Monkey:
|
|||
|
||||
inspections: int
|
||||
|
||||
def __init__(self, inp: str):
|
||||
def __init__(self, inp: str):
|
||||
self.inspections = 0
|
||||
|
||||
parts = inp.splitlines()[1:]
|
||||
self.items = list(map(int, starting_items_regex.match(parts[0]).group(1).split(", ")))
|
||||
self.items = list(
|
||||
map(int, starting_items_regex.match(parts[0]).group(1).split(", "))
|
||||
)
|
||||
self.divisor = int(test_regex.match(parts[2]).group(1))
|
||||
self.if_true = int(true_regex.match(parts[3]).group(1))
|
||||
self.if_false = int(false_regex.match(parts[4]).group(1))
|
||||
|
@ -80,7 +82,7 @@ def run(inp: List[Monkey], n: int, mod: Callable[[int], int]) -> int:
|
|||
item = monkey.inspect_first()
|
||||
item = mod(item)
|
||||
next_monkey = monkey.calculate_throw(item)
|
||||
|
||||
|
||||
inp[next_monkey].items.append(item)
|
||||
|
||||
a, b = list(sorted(inp, reverse=True, key=lambda x: x.inspections))[:2]
|
||||
|
@ -88,12 +90,11 @@ def run(inp: List[Monkey], n: int, mod: Callable[[int], int]) -> int:
|
|||
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
inp = parse(instr)
|
||||
return run(inp, 20, lambda x: x // 3)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def two(instr: str) -> int:
|
||||
inp = parse(instr)
|
||||
|
|
|
@ -14,7 +14,12 @@ class Node:
|
|||
value: int
|
||||
edges: List[Node]
|
||||
|
||||
def __init__(self, pos: Vector, value: int = 0, edges: Optional[List[Tuple[Node, int]]] = None):
|
||||
def __init__(
|
||||
self,
|
||||
pos: Vector,
|
||||
value: int = 0,
|
||||
edges: Optional[List[Tuple[Node, int]]] = None,
|
||||
):
|
||||
self.pos = pos
|
||||
self.value = value
|
||||
self.edges = [] if edges is None else edges
|
||||
|
@ -46,7 +51,6 @@ def parse(instr: str) -> Tuple[Dict[Vector, Node], Vector, Vector]:
|
|||
elif char == "E":
|
||||
end = n.pos
|
||||
|
||||
|
||||
for coord in nodes:
|
||||
n = nodes[coord]
|
||||
|
||||
|
@ -62,7 +66,9 @@ def parse(instr: str) -> Tuple[Dict[Vector, Node], Vector, Vector]:
|
|||
|
||||
|
||||
def shortest_path(nodes: Dict[Vector, Node], begin: Vector, end: Vector) -> int:
|
||||
priorities: Dict[Vector, Tuple[Union[int, float], Optional[Vector]]] = {node: (inf, None) for node in nodes}
|
||||
priorities: Dict[Vector, Tuple[Union[int, float], Optional[Vector]]] = {
|
||||
node: (inf, None) for node in nodes
|
||||
}
|
||||
visited: Dict[Vector, None] = {begin: None}
|
||||
|
||||
cursor = begin
|
||||
|
@ -99,8 +105,8 @@ def shortest_path(nodes: Dict[Vector, Node], begin: Vector, end: Vector) -> int:
|
|||
|
||||
return len(route)
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
nodes, start, end = parse(instr)
|
||||
|
@ -109,7 +115,7 @@ class Challenge(BaseChallenge):
|
|||
@staticmethod
|
||||
def two(instr: str) -> int:
|
||||
nodes, _, end = parse(instr)
|
||||
|
||||
|
||||
# possible starting positions are ones with value=0 bordering one with value=1
|
||||
|
||||
starting_positions: List[Vector] = []
|
||||
|
@ -130,4 +136,3 @@ class Challenge(BaseChallenge):
|
|||
shortest = x
|
||||
|
||||
return shortest
|
||||
|
||||
|
|
|
@ -8,20 +8,25 @@ from math import inf
|
|||
import itertools
|
||||
import copy
|
||||
|
||||
|
||||
@dataclass
|
||||
class Node:
|
||||
name: str
|
||||
value: int
|
||||
edges: List[Node]
|
||||
|
||||
def __init__(self, name: str, value: int = 0, edges: Optional[List[Tuple[Node, int]]] = None):
|
||||
def __init__(
|
||||
self, name: str, value: int = 0, edges: Optional[List[Tuple[Node, int]]] = None
|
||||
):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.edges = [] if edges is None else edges
|
||||
|
||||
|
||||
|
||||
def shortest_path(nodes: Dict[str, Node], begin: str, end: str) -> List[str]:
|
||||
priorities: Dict[str, Tuple[Union[int, float], Optional[str]]] = {node: (inf, None) for node in nodes}
|
||||
priorities: Dict[str, Tuple[Union[int, float], Optional[str]]] = {
|
||||
node: (inf, None) for node in nodes
|
||||
}
|
||||
visited: Dict[str, None] = {begin: None}
|
||||
|
||||
cursor = begin
|
||||
|
@ -59,10 +64,14 @@ def shortest_path(nodes: Dict[str, Node], begin: str, end: str) -> List[str]:
|
|||
return route[:-1]
|
||||
|
||||
|
||||
parse_re = re.compile(r"Valve ([A-Z]+) has flow rate=(\d+); tunnels? leads? to valves? ((?:[A-Z]+,? ?)+)")
|
||||
parse_re = re.compile(
|
||||
r"Valve ([A-Z]+) has flow rate=(\d+); tunnels? leads? to valves? ((?:[A-Z]+,? ?)+)"
|
||||
)
|
||||
|
||||
|
||||
def parse(instr: str) -> Tuple[Dict[str, Node], List[str], Dict[Tuple[str, str], List[str]]]:
|
||||
def parse(
|
||||
instr: str,
|
||||
) -> Tuple[Dict[str, Node], List[str], Dict[Tuple[str, str], List[str]]]:
|
||||
sp = [parse_re.match(line).groups() for line in instr.strip().splitlines()]
|
||||
|
||||
nodes: Dict[str, Node] = {}
|
||||
|
@ -93,20 +102,34 @@ def parse(instr: str) -> Tuple[Dict[str, Node], List[str], Dict[Tuple[str, str],
|
|||
pl = len(path) + 1
|
||||
shortest_paths[(start_node, end_node)] = pl
|
||||
shortest_paths[(end_node, start_node)] = pl
|
||||
|
||||
|
||||
return nodes, unjammed_nodes, shortest_paths
|
||||
|
||||
|
||||
def permutations(current_node: str, nodes_remaining: List[str], shortest_paths: Dict[Tuple[str, str], List[str]], path: List[str], cost_remaining: int) -> Generator[List[str]]:
|
||||
def permutations(
|
||||
current_node: str,
|
||||
nodes_remaining: List[str],
|
||||
shortest_paths: Dict[Tuple[str, str], List[str]],
|
||||
path: List[str],
|
||||
cost_remaining: int,
|
||||
) -> Generator[List[str]]:
|
||||
for next_node in nodes_remaining:
|
||||
cost = shortest_paths[(current_node, next_node)]
|
||||
if cost < cost_remaining:
|
||||
nr = copy.copy(nodes_remaining)
|
||||
nr.remove(next_node)
|
||||
yield from permutations(next_node, nr, shortest_paths, path + [next_node], cost_remaining - cost)
|
||||
yield from permutations(
|
||||
next_node, nr, shortest_paths, path + [next_node], cost_remaining - cost
|
||||
)
|
||||
yield path
|
||||
|
||||
def calc_vented(nodes: Dict[str, Node], shortest_paths: Dict[Tuple[str, str], List[str]], visit_order: List[str], time_remaining: int) -> int:
|
||||
|
||||
def calc_vented(
|
||||
nodes: Dict[str, Node],
|
||||
shortest_paths: Dict[Tuple[str, str], List[str]],
|
||||
visit_order: List[str],
|
||||
time_remaining: int,
|
||||
) -> int:
|
||||
current = "AA"
|
||||
pressure = 0
|
||||
|
||||
|
@ -118,8 +141,8 @@ def calc_vented(nodes: Dict[str, Node], shortest_paths: Dict[Tuple[str, str], Li
|
|||
|
||||
return pressure
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
nodes, unjammed_nodes, shortest_paths = parse(instr)
|
||||
|
@ -137,13 +160,20 @@ class Challenge(BaseChallenge):
|
|||
def two(instr: str) -> int:
|
||||
nodes, unjammed_nodes, shortest_paths = parse(instr)
|
||||
|
||||
pressures = [(calc_vented(nodes, shortest_paths, visit_order, 26), visit_order) for visit_order in permutations("AA", unjammed_nodes, shortest_paths, [], 26)]
|
||||
pressures = [
|
||||
(calc_vented(nodes, shortest_paths, visit_order, 26), visit_order)
|
||||
for visit_order in permutations(
|
||||
"AA", unjammed_nodes, shortest_paths, [], 26
|
||||
)
|
||||
]
|
||||
|
||||
max_pressure = 0
|
||||
for i, (pressure_a, order_a) in enumerate(sorted(pressures, reverse=True, key=lambda x: x[0])):
|
||||
for i, (pressure_a, order_a) in enumerate(
|
||||
sorted(pressures, reverse=True, key=lambda x: x[0])
|
||||
):
|
||||
if pressure_a * 2 < max_pressure:
|
||||
break
|
||||
for (pressure_b, order_b) in pressures[i+1:]:
|
||||
for (pressure_b, order_b) in pressures[i + 1 :]:
|
||||
if len(set(order_a).intersection(order_b)) == 0:
|
||||
pressure = pressure_a + pressure_b
|
||||
if pressure > max_pressure:
|
||||
|
|
|
@ -9,4 +9,4 @@ print("union(){")
|
|||
for line in inp.strip().splitlines():
|
||||
print(f"\ttranslate([{line}]) cube(1);")
|
||||
|
||||
print("};")
|
||||
print("};")
|
||||
|
|
|
@ -6,9 +6,12 @@ CubeMap = Dict[Coordinate, bool]
|
|||
|
||||
|
||||
ADJACENT_LOCATIONS = (
|
||||
(1, 0, 0), (-1, 0, 0),
|
||||
(0, 1, 0), (0, -1, 0),
|
||||
(0, 0, 1), (0, 0, -1),
|
||||
(1, 0, 0),
|
||||
(-1, 0, 0),
|
||||
(0, 1, 0),
|
||||
(0, -1, 0),
|
||||
(0, 0, 1),
|
||||
(0, 0, -1),
|
||||
)
|
||||
|
||||
|
||||
|
@ -20,7 +23,7 @@ def parse(instr: str) -> CubeMap:
|
|||
res: CubeMap = {}
|
||||
for line in instr.strip().splitlines():
|
||||
res[tuple(map(int, line.split(",")))] = False
|
||||
|
||||
|
||||
for coord in res:
|
||||
is_completely_surrounded = True
|
||||
for modifier in ADJACENT_LOCATIONS:
|
||||
|
@ -64,7 +67,6 @@ def is_coord_in_range(c: Coordinate, min_c: Coordinate, max_c: Coordinate) -> bo
|
|||
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
inp = parse(instr)
|
||||
|
@ -73,7 +75,7 @@ class Challenge(BaseChallenge):
|
|||
@staticmethod
|
||||
def two(instr: str) -> int:
|
||||
inp = parse(instr)
|
||||
|
||||
|
||||
min_x, max_x = min_max(c[0] for c in inp)
|
||||
min_y, max_y = min_max(c[1] for c in inp)
|
||||
min_z, max_z = min_max(c[2] for c in inp)
|
||||
|
@ -91,9 +93,9 @@ class Challenge(BaseChallenge):
|
|||
|
||||
for modifier in ADJACENT_LOCATIONS:
|
||||
combined = sum_coordinates(current, modifier)
|
||||
if (not is_coord_in_range(combined, min_coord, max_coord)):
|
||||
if not is_coord_in_range(combined, min_coord, max_coord):
|
||||
continue
|
||||
|
||||
|
||||
if combined in inp:
|
||||
touchable_faces += 1
|
||||
else:
|
||||
|
|
|
@ -21,13 +21,27 @@ class Blueprint:
|
|||
|
||||
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):
|
||||
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.OBSIDIAN: (
|
||||
obsidian_robot_cost_ore,
|
||||
obsidian_robot_cost_clay,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
Material.GEODE: (geode_robot_cost_ore, 0, geode_robot_cost_obsidian, 0),
|
||||
}
|
||||
|
||||
|
@ -39,7 +53,9 @@ class Blueprint:
|
|||
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\.")
|
||||
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]:
|
||||
|
@ -51,7 +67,15 @@ def parse(instr: str) -> List[Blueprint]:
|
|||
return res
|
||||
|
||||
|
||||
def calc_max_geodes(blueprint: Blueprint, max_time: int, materials: MaterialTracker, robots: MaterialTracker, robot_quota: MaterialTracker, minute: int, cannot_build: int) -> int:
|
||||
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]
|
||||
|
||||
|
@ -84,7 +108,15 @@ def calc_max_geodes(blueprint: Blueprint, max_time: int, materials: MaterialTrac
|
|||
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)
|
||||
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
|
||||
|
@ -104,16 +136,17 @@ def calc_max_geodes(blueprint: Blueprint, max_time: int, materials: MaterialTrac
|
|||
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)
|
||||
|
||||
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)
|
||||
|
@ -122,7 +155,8 @@ class Challenge(BaseChallenge):
|
|||
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,
|
||||
calc_max_geodes(bp, 24, (0, 0, 0, 0), (1, 0, 0, 0), robot_quota, 1, 0)
|
||||
* bp.number,
|
||||
)
|
||||
|
||||
return sum(quals)
|
||||
|
@ -132,11 +166,11 @@ class Challenge(BaseChallenge):
|
|||
inp = parse(instr)
|
||||
|
||||
nums: List[int] = []
|
||||
for bp in inp[:min(3, len(inp))]:
|
||||
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)
|
||||
calc_max_geodes(bp, 32, (0, 0, 0, 0), (1, 0, 0, 0), robot_quota, 1, 0)
|
||||
)
|
||||
print(nums[-1], flush=True)
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
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)
|
Loading…
Add table
Add a link
Reference in a new issue