Day 14 (Python)
This commit is contained in:
parent
f83f5d34e5
commit
ba24d16664
7 changed files with 237 additions and 1 deletions
2
.github/README.md
vendored
2
.github/README.md
vendored
|
@ -29,7 +29,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have
|
||||||
| [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) |
|
| [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) |
|
||||||
| [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) |
|
| [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) |
|
||||||
| [13](/13-shuttleSearch) | ![Partially complete][partial] | [Link](/13-shuttleSearch/python) | |
|
| [13](/13-shuttleSearch) | ![Partially complete][partial] | [Link](/13-shuttleSearch/python) | |
|
||||||
| 14 | ![Not yet attempted][pending] | | |
|
| [14](/14-dockingData) | ![Partially complete][partial] | [Link](/14-dockingData/python) | |
|
||||||
| 15 | | | |
|
| 15 | | | |
|
||||||
| 16 | | | |
|
| 16 | | | |
|
||||||
| 17 | | | |
|
| 17 | | | |
|
||||||
|
|
19
14-dockingData/README.md
Normal file
19
14-dockingData/README.md
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# [Day 14: Docking Data](https://adventofcode.com/2020/day/14)
|
||||||
|
|
||||||
|
<details><summary>Script output</summary>
|
||||||
|
|
||||||
|
```
|
||||||
|
❯ python .\python\
|
||||||
|
AoC 2020: day 14 - Docking Data
|
||||||
|
Python 3.8.5
|
||||||
|
|
||||||
|
Test cases
|
||||||
|
1.1 pass
|
||||||
|
2.1 pass
|
||||||
|
|
||||||
|
Answers
|
||||||
|
Part 1: 8570568288597
|
||||||
|
Part 2: 3289441921203
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
19
14-dockingData/info.json
Normal file
19
14-dockingData/info.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"year": "2020",
|
||||||
|
"day": "14",
|
||||||
|
"title": "Docking Data",
|
||||||
|
"testCases": {
|
||||||
|
"one": [
|
||||||
|
{
|
||||||
|
"input": "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X\nmem[8] = 11\nmem[7] = 101\nmem[8] = 0\n",
|
||||||
|
"expected": 165
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"two": [
|
||||||
|
{
|
||||||
|
"input": "mask = 000000000000000000000000000000X1001X\nmem[42] = 100\nmask = 00000000000000000000000000000000X0XX\nmem[26] = 1\n",
|
||||||
|
"expected": 208
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
77
14-dockingData/python/__main__.py
Normal file
77
14-dockingData/python/__main__.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import json
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from rich import print
|
||||||
|
|
||||||
|
from partOne import partOne
|
||||||
|
from partTwo import partTwo
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests(test_cases):
|
||||||
|
do_tests = True
|
||||||
|
if len(test_cases) == 0:
|
||||||
|
do_tests = False
|
||||||
|
elif len(test_cases["one"]) == 0 and len(test_cases["two"]) == 0:
|
||||||
|
do_tests = False
|
||||||
|
|
||||||
|
if not do_tests:
|
||||||
|
print("Info: no test cases specified. Skipping tests\n")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Test cases")
|
||||||
|
|
||||||
|
def rt(tcs, f, n):
|
||||||
|
for i, tc in enumerate(tcs):
|
||||||
|
print(f"{n}.{i+1} ", end="")
|
||||||
|
expectedInt = tc["expected"]
|
||||||
|
result = f(str(tc["input"]))
|
||||||
|
if result == expectedInt:
|
||||||
|
print("[green]pass[/green]")
|
||||||
|
else:
|
||||||
|
print(f"[red]fail[/red] (got {result}, expected {expectedInt})")
|
||||||
|
|
||||||
|
rt(test_cases["one"], partOne, 1)
|
||||||
|
rt(test_cases["two"], partTwo, 2)
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
info = open("info.json").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: could not open info.json")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
info = json.loads(info)
|
||||||
|
|
||||||
|
year = info["year"]
|
||||||
|
day = info["day"]
|
||||||
|
title = info["title"]
|
||||||
|
|
||||||
|
print(f"[yellow]AoC {year}[/yellow]: day {day} - {title}")
|
||||||
|
print(f"Python {platform.python_version()}\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
challenge_input = open("input.txt").read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
print("Error: could not open input.txt")
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
if "vis" in sys.argv:
|
||||||
|
import visualise
|
||||||
|
|
||||||
|
print("[green]Running visualisation....[/green]")
|
||||||
|
|
||||||
|
visualise.visualise(challenge_input)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
run_tests(info["testCases"])
|
||||||
|
|
||||||
|
if "debug" in sys.argv:
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
print("Answers")
|
||||||
|
print("Part 1:", partOne(challenge_input))
|
||||||
|
print("Part 2:", partTwo(challenge_input))
|
42
14-dockingData/python/common.py
Normal file
42
14-dockingData/python/common.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from typing import List, Tuple
|
||||||
|
import re
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
|
class Instruction:
|
||||||
|
address: int
|
||||||
|
value: int
|
||||||
|
mask: str
|
||||||
|
|
||||||
|
def __init__(self, instruction: str, mask: str):
|
||||||
|
if mask is None:
|
||||||
|
raise ValueError(f"bad mask")
|
||||||
|
m = re.match(r"mem\[(\d+)\] ?= ?(\d+)", instruction)
|
||||||
|
if m is None:
|
||||||
|
raise ValueError(f"unable to parse input instruction '{instruction}'")
|
||||||
|
self.mask = mask
|
||||||
|
self.address = int(m.group(1))
|
||||||
|
self.value = int(m.group(2))
|
||||||
|
|
||||||
|
|
||||||
|
def parse(instr: str) -> List[Instruction]:
|
||||||
|
retval = []
|
||||||
|
|
||||||
|
current_mask = None
|
||||||
|
for line in instr.strip().split("\n"):
|
||||||
|
if "mask" in line:
|
||||||
|
current_mask = line.split("=")[-1].strip()
|
||||||
|
else:
|
||||||
|
retval.append(Instruction(line, current_mask))
|
||||||
|
|
||||||
|
return retval
|
||||||
|
|
||||||
|
|
||||||
|
def number_to_base(n: int, b: int) -> str:
|
||||||
|
if n == 0:
|
||||||
|
return "0"
|
||||||
|
digits = []
|
||||||
|
while n:
|
||||||
|
digits.append(int(n % b))
|
||||||
|
n //= b
|
||||||
|
return "".join([str(x) for x in digits[::-1]])
|
28
14-dockingData/python/partOne.py
Normal file
28
14-dockingData/python/partOne.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
from common import *
|
||||||
|
|
||||||
|
|
||||||
|
def apply_mask(number: int, mask: str) -> int:
|
||||||
|
# There has to be a better way to do this... but oh well
|
||||||
|
# it works, after all
|
||||||
|
value = number_to_base(number, 2).zfill(len(mask))
|
||||||
|
combi = ""
|
||||||
|
for (v, m) in zip(value, mask.lower()):
|
||||||
|
if m == "x":
|
||||||
|
combi += v
|
||||||
|
else:
|
||||||
|
combi += m
|
||||||
|
return int(combi, 2)
|
||||||
|
|
||||||
|
|
||||||
|
def partOne(instr: str) -> int:
|
||||||
|
input_instructions = parse(instr)
|
||||||
|
memory = {}
|
||||||
|
|
||||||
|
for instruction in input_instructions:
|
||||||
|
memory[instruction.address] = apply_mask(instruction.value, instruction.mask)
|
||||||
|
|
||||||
|
sigma = 0
|
||||||
|
for key in memory:
|
||||||
|
sigma += memory[key]
|
||||||
|
|
||||||
|
return sigma
|
51
14-dockingData/python/partTwo.py
Normal file
51
14-dockingData/python/partTwo.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
from common import *
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
def get_memory_addresses(value: int, mask: str) -> List[int]:
|
||||||
|
|
||||||
|
value = number_to_base(value, 2).zfill(len(mask))
|
||||||
|
combi = []
|
||||||
|
for (v, m) in zip(value, mask.lower()):
|
||||||
|
if m == "0":
|
||||||
|
combi.append(v)
|
||||||
|
elif m == "1":
|
||||||
|
combi.append("1")
|
||||||
|
else:
|
||||||
|
combi.append(m)
|
||||||
|
|
||||||
|
mask = combi
|
||||||
|
|
||||||
|
n = mask.count("x")
|
||||||
|
# generates sets of ones and zeroes in unique orders for n times
|
||||||
|
values = list(itertools.product(*[[0, 1]] * n))
|
||||||
|
|
||||||
|
xns = []
|
||||||
|
|
||||||
|
# Find all floating point iterations
|
||||||
|
for val_combo in values:
|
||||||
|
msk = copy.copy(mask)
|
||||||
|
val_counter = 0
|
||||||
|
for i, char in enumerate(msk):
|
||||||
|
if char == "x":
|
||||||
|
msk[i] = str(val_combo[val_counter])
|
||||||
|
val_counter += 1
|
||||||
|
xns.append(int("".join(msk), 2))
|
||||||
|
|
||||||
|
return xns
|
||||||
|
|
||||||
|
|
||||||
|
def partTwo(instr: str) -> int:
|
||||||
|
input_instructions = parse(instr)
|
||||||
|
memory = {}
|
||||||
|
|
||||||
|
for instruction in input_instructions:
|
||||||
|
addresses = get_memory_addresses(instruction.address, instruction.mask)
|
||||||
|
for address in addresses:
|
||||||
|
memory[address] = instruction.value
|
||||||
|
|
||||||
|
sigma = 0
|
||||||
|
for key in memory:
|
||||||
|
sigma += memory[key]
|
||||||
|
|
||||||
|
return sigma
|
Loading…
Add table
Add a link
Reference in a new issue