Code formatting

Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
akp 2022-12-20 17:51:35 +00:00
parent 320b990b05
commit 6f282e5761
No known key found for this signature in database
GPG key ID: AA5726202C8879B7
9 changed files with 125 additions and 101 deletions

View file

@ -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])):

View file

@ -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()

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -9,4 +9,4 @@ print("union(){")
for line in inp.strip().splitlines():
print(f"\ttranslate([{line}]) cube(1);")
print("};")
print("};")

View file

@ -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:

View file

@ -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)

View file

@ -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)