mirror of
https://github.com/cupcakearmy/advent-of-code.git
synced 2025-09-02 13:20:40 +00:00
move to 2020 fodler
This commit is contained in:
129
2020/.gitignore
vendored
Normal file
129
2020/.gitignore
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
9
2020/README.md
Normal file
9
2020/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Advent Of Code Solutions
|
||||
|
||||
Here are my solutions for the advent of code 2020 🎄🎅
|
||||
|
||||
Note that the exact solutions are different for some people.
|
||||
|
||||
The main ones are solved in python. 1-4 I also did in Go, but I quickly [did not like it](./learning/Go.md).
|
||||
|
||||
`/solutions/:day/*`
|
15
2020/learning/Go.md
Normal file
15
2020/learning/Go.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Go Learning
|
||||
|
||||
So this will be my first time at writing some go.
|
||||
Please excuse me for the ranting 😅
|
||||
|
||||
## WTFs
|
||||
|
||||
- Why is there no bitwise OR for bools?! Please what? XOR == `a != b`, but still...
|
||||
- `math.Max()` only works with floats. Of course.
|
||||
- so apparently we don't have optional parameters and defaults. Lol
|
||||
- go has maps, but no native way of getting keys or values without a loop
|
||||
|
||||
## Syntax
|
||||
|
||||
- we constantly need to write `:=` which is annoying as hell
|
12
2020/solutions/1/README.md
Normal file
12
2020/solutions/1/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 1
|
||||
|
||||
This is quite simple, just iterate and find.
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>1224 and 796 -> 974304</li>
|
||||
<li>332, 858 and 830 -> 236430480</li>
|
||||
</ol>
|
||||
</details>
|
55
2020/solutions/1/go/main.go
Normal file
55
2020/solutions/1/go/main.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const target uint64 = 2020
|
||||
|
||||
func findTwo(list []uint64) {
|
||||
for _, a := range list {
|
||||
for _, b := range list {
|
||||
if a+b == target {
|
||||
fmt.Printf("The numbers: %v and %v.\tSolution: %v\n", a, b, a*b)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func findThree(list []uint64) {
|
||||
for _, a := range list {
|
||||
for _, b := range list {
|
||||
for _, c := range list {
|
||||
if a+b+c == target {
|
||||
fmt.Printf("The numbers: %v, %v and %v.\tSolution: %v\n", a, b, c, a*b*c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, err := ioutil.ReadFile("./solutions/1/data.txt")
|
||||
if err != nil {
|
||||
fmt.Println("File reading error", err)
|
||||
return
|
||||
}
|
||||
|
||||
lines := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
intLines := []uint64{}
|
||||
for _, i := range lines {
|
||||
num, _ := strconv.ParseUint(i, 10, 64)
|
||||
intLines = append(intLines, num)
|
||||
}
|
||||
|
||||
// fmt.Println("Result: ", findTwo(intLines))
|
||||
// fmt.Println("Result: ", findThree(intLines))
|
||||
findTwo(intLines)
|
||||
findThree(intLines)
|
||||
}
|
17
2020/solutions/1/python/main.py
Normal file
17
2020/solutions/1/python/main.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from typing import List
|
||||
from itertools import product
|
||||
from os.path import join, dirname
|
||||
|
||||
target = 2020
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
numbers: List[int] = list(map(int, f.readlines()))
|
||||
for a, b in product(numbers, numbers):
|
||||
if a + b == target:
|
||||
print(f'The numbers: {a} and {b}.\tSolution: {a*b}')
|
||||
break
|
||||
|
||||
for a, b, c in product(numbers, numbers, numbers):
|
||||
if a + b + c == target:
|
||||
print(f'The numbers: {a}, {b} and {c}.\tSolution: {a*b*c}')
|
||||
break
|
43
2020/solutions/10/README.md
Normal file
43
2020/solutions/10/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# 10
|
||||
|
||||
# First
|
||||
|
||||
The first one is really easy. Just sort, make the diff and count.
|
||||
First I take the list and sort it:
|
||||
|
||||
```python
|
||||
[16, 10, 15, 5, 1, 11, 7, 19, 6, 12, 4]
|
||||
[0, 1, 4, 5, 6, 7, 10, 11, 12, 15, 16, 19, 22] # Sorted and added the wall plug (0) and the phone (biggest + 3)
|
||||
[1, 3, 1, 1, 1, 3, 1, 1, 3, 1, 3, 3] # The size of each step
|
||||
```
|
||||
|
||||
Now we can simply count how many `1` and `3` there are with `l.count(1)`.
|
||||
|
||||
## Second
|
||||
|
||||
This is where it gets tricky.
|
||||
|
||||
First lets find all the consecutive `1`s ad only they can be removed. If we have more than 1 consecutive `1` we can remove one of it. However we need to be careful not to remove to many or the step will be higher than `3` and the chain breaks.
|
||||
|
||||
```python
|
||||
[1, 1, 1, 1] # We can transform this example by adding 2 numbers together and "joining" them.
|
||||
|
||||
[1, 2, 1] # Valid
|
||||
[1, 1, 2] # Valid
|
||||
[1, 3] # Valid
|
||||
[4] # Invalid because we can jump a max of 3 steps at a time.
|
||||
```
|
||||
|
||||
Now we could iterate but I wanted to find a formula. Not sure this is correct but here we go.
|
||||
|
||||
Basically we take the length of the consecutive `1` and compute `2**(l-1)` to get all possible combinations.
|
||||
Now we need to subtract the possible `4` which can only be achieved if we have at least 4 numbers -> `floor(l/4)`
|
||||
For a grand total of `2**(l-1) - floor(l/4)`
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>2475</li>
|
||||
<li>442136281481216</li>
|
||||
</ol>
|
||||
</details>
|
54
2020/solutions/10/python/main.py
Normal file
54
2020/solutions/10/python/main.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Optional, Set, Tuple
|
||||
from itertools import combinations, count
|
||||
from math import floor, prod
|
||||
|
||||
|
||||
def parse(s: str) -> List[int]:
|
||||
numbers: List[int] = sorted(map(int, s.strip().split('\n')))
|
||||
numbers.insert(0, 0) # The wall
|
||||
numbers.append(numbers[-1] + 3) # Phone itself
|
||||
return numbers
|
||||
|
||||
|
||||
def diff(l: List[int]) -> List[int]:
|
||||
return [
|
||||
l[x] - l[x-1]
|
||||
for x in range(1, len(l))
|
||||
]
|
||||
|
||||
|
||||
def calc(d: List[int]) -> int:
|
||||
one = d.count(1)
|
||||
three = d.count(3)
|
||||
return one * three
|
||||
|
||||
|
||||
def find_valid_permutations(d: List[int]) -> int:
|
||||
i = 0
|
||||
l = len(d)
|
||||
slices: List[int] = []
|
||||
while i < l:
|
||||
if d[i] != 3:
|
||||
try:
|
||||
n = d.index(3, i + 1) # Find the next three
|
||||
diff = n - i
|
||||
if diff > 1:
|
||||
slices.append(diff)
|
||||
i = n
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
i += 1
|
||||
return prod([
|
||||
2**(s-1) - floor(s/4)
|
||||
for s in slices
|
||||
])
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
numbers: List[int] = parse(f.read())
|
||||
d = diff(numbers)
|
||||
print(calc(d))
|
||||
print(find_valid_permutations(d))
|
12
2020/solutions/11/README.md
Normal file
12
2020/solutions/11/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 11
|
||||
|
||||
Today: Game of life, but a bit different :)
|
||||
I lost so much time because I missed that in the second part we now require 5 instead of 4 occupied seats 😩
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>2296</li>
|
||||
<li>2089</li>
|
||||
</ol>
|
||||
</details>
|
99
2020/solutions/11/python/main.py
Normal file
99
2020/solutions/11/python/main.py
Normal file
@@ -0,0 +1,99 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Optional
|
||||
from itertools import product
|
||||
from copy import deepcopy
|
||||
|
||||
TSeats = List[List[Optional[bool]]]
|
||||
|
||||
mapping = {
|
||||
'.': None,
|
||||
'#': True,
|
||||
'L': False
|
||||
}
|
||||
inv = {v: k for k, v in mapping.items()}
|
||||
|
||||
|
||||
class Seats:
|
||||
p = [-1, 0, 1]
|
||||
|
||||
def __init__(self, plan: str, alt: bool = False) -> None:
|
||||
self.seats: TSeats = [
|
||||
[
|
||||
mapping[seat]
|
||||
for seat in row
|
||||
]
|
||||
for row in plan.strip().split('\n')
|
||||
]
|
||||
self.max_x = len(self.seats[0])
|
||||
self.max_y = len(self.seats)
|
||||
self.alt = alt
|
||||
|
||||
def __str__(self) -> str:
|
||||
return '\n'.join([
|
||||
''.join([inv[seat] for seat in row])
|
||||
for row in self.seats
|
||||
])
|
||||
|
||||
def find_next_in_direction(self, y: int, x: int, dy: int, dx: int) -> Optional[bool]:
|
||||
y += dy
|
||||
x += dx
|
||||
while 0 <= x < self.max_x and 0 <= y < self.max_y:
|
||||
cur = self.seats[y][x]
|
||||
if cur is not None:
|
||||
return cur
|
||||
y += dy
|
||||
x += dx
|
||||
return None
|
||||
|
||||
def get_occupied(self, y: int, x: int,) -> int:
|
||||
occupied = 0
|
||||
for dx, dy in product(self.p, self.p):
|
||||
if dx == 0 and dy == 0:
|
||||
continue
|
||||
if self.alt and self.find_next_in_direction(y, x, dy, dx) == True:
|
||||
occupied += 1
|
||||
else:
|
||||
dx += x
|
||||
dy += y
|
||||
if 0 <= dx < self.max_x and 0 <= dy < self.max_y and self.seats[dy][dx]:
|
||||
occupied += 1
|
||||
return occupied
|
||||
|
||||
def iteration(self) -> int:
|
||||
changed = 0
|
||||
future: TSeats = deepcopy(self.seats)
|
||||
required_to_leave = 4 if self.alt else 3
|
||||
for y, x in product(range(self.max_y), range(self.max_x)):
|
||||
current = self.seats[y][x]
|
||||
if current == None:
|
||||
continue
|
||||
occupied = self.get_occupied(y, x)
|
||||
if (current == True and occupied > required_to_leave) or (current == False and occupied == 0):
|
||||
future[y][x] = not current
|
||||
changed += 1
|
||||
self.seats = future
|
||||
return changed
|
||||
|
||||
def count_occupied(self) -> int:
|
||||
return sum([
|
||||
sum([
|
||||
1 if seat == True else 0
|
||||
for seat in row
|
||||
])
|
||||
for row in self.seats
|
||||
])
|
||||
|
||||
def find_equilibrium(self) -> int:
|
||||
while self.iteration() > 0:
|
||||
pass
|
||||
return self.count_occupied()
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
txt = f.read()
|
||||
seats = Seats(txt)
|
||||
print(seats.find_equilibrium())
|
||||
|
||||
seats = Seats(txt, True)
|
||||
print(seats.find_equilibrium())
|
13
2020/solutions/12/README.md
Normal file
13
2020/solutions/12/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 12
|
||||
|
||||
Let's navigate!
|
||||
|
||||
Reminded me that I had to refresh trigonometry a bit xD
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>796</li>
|
||||
<li>39446</li>
|
||||
</ol>
|
||||
</details>
|
83
2020/solutions/12/python/main.py
Normal file
83
2020/solutions/12/python/main.py
Normal file
@@ -0,0 +1,83 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Optional
|
||||
from math import cos, sin, radians, atan2, degrees, sqrt
|
||||
|
||||
directions = {
|
||||
'N': 0,
|
||||
'E': 90,
|
||||
'S': 180,
|
||||
'W': 270,
|
||||
}
|
||||
|
||||
|
||||
class Ship:
|
||||
|
||||
def __init__(self, waypoint=False) -> None:
|
||||
self.x: float = 0
|
||||
self.y: float = 0
|
||||
self.d: int = directions['E']
|
||||
|
||||
self.waypoint = waypoint
|
||||
self.wx: float = 10
|
||||
self.wy: float = 1
|
||||
|
||||
@property
|
||||
def distance(self) -> int:
|
||||
return round(abs(self.x) + abs(self.y))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'⛴ X={round(self.x)} Y={round(self.y)} 𒎓={round(self.d)}\t🏴☠️ X={round(self.wx)} Y={round(self.wy)}'
|
||||
|
||||
def navigate(self, amount: int, degree: Optional[int] = None) -> None:
|
||||
if degree == None:
|
||||
if self.waypoint:
|
||||
self.x += self.wx * amount
|
||||
self.y += self.wy * amount
|
||||
return
|
||||
degree = self.d
|
||||
dx = amount * sin(radians(degree))
|
||||
dy = amount * cos(radians(degree))
|
||||
if self.waypoint:
|
||||
self.wx += dx
|
||||
self.wy += dy
|
||||
else:
|
||||
self.x += dx
|
||||
self.y += dy
|
||||
|
||||
def move(self, instruction: str) -> None:
|
||||
cmd: str = instruction[0]
|
||||
amount: int = int(instruction[1:])
|
||||
if cmd in directions:
|
||||
self.navigate(amount, degree=directions[cmd])
|
||||
elif cmd == 'F':
|
||||
self.navigate(amount)
|
||||
else:
|
||||
diff = amount if cmd == 'R' else -amount
|
||||
if self.waypoint:
|
||||
size = sqrt(self.wx**2 + self.wy**2)
|
||||
d = degrees(atan2(self.wy, self.wx))
|
||||
d -= diff
|
||||
self.wx = size * cos(radians(d))
|
||||
self.wy = size * sin(radians(d))
|
||||
else:
|
||||
self.d = (self.d + diff) % 360
|
||||
|
||||
def follow(self, file: str) -> None:
|
||||
instructions = file.strip().split('\n')
|
||||
for instruction in instructions:
|
||||
self.move(instruction)
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
txt = f.read()
|
||||
|
||||
ship = Ship()
|
||||
ship.follow(txt)
|
||||
print(ship)
|
||||
print(ship.distance)
|
||||
|
||||
ship = Ship(waypoint=True)
|
||||
ship.follow(txt)
|
||||
print(ship)
|
||||
print(ship.distance)
|
12
2020/solutions/13/README.md
Normal file
12
2020/solutions/13/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# 13
|
||||
|
||||
The second part one was really interesting. While my solution is correct, it takes way to much time in the data set.
|
||||
I started by checking what is the biggest ID number and check each location.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>156</li>
|
||||
<li>404517869995362</li>
|
||||
</ol>
|
||||
</details>
|
76
2020/solutions/13/python/main.py
Normal file
76
2020/solutions/13/python/main.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Tuple
|
||||
from math import ceil, floor
|
||||
|
||||
|
||||
class Station:
|
||||
|
||||
def __init__(self, txt: str) -> None:
|
||||
arrival, busses = txt.strip().split('\n')
|
||||
self.arrival = int(arrival)
|
||||
self.busses = [
|
||||
int(bus)
|
||||
for bus in busses.replace('x', '0').split(',')
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def get_next_for_id(id: int, arrival: int) -> int:
|
||||
return id * ceil(arrival / id)
|
||||
|
||||
def find_next(self) -> Tuple[int, int]:
|
||||
arrivals: List[Tuple[int, int]] = []
|
||||
for bus in self.busses:
|
||||
if bus == 0:
|
||||
continue
|
||||
arrivals.append((bus, self.get_next_for_id(bus, self.arrival)))
|
||||
return min(arrivals, key=lambda x: x[1])
|
||||
|
||||
def get_flag(self) -> int:
|
||||
id, arrives = self.find_next()
|
||||
return id * (arrives - self.arrival)
|
||||
|
||||
def contest(self, offset: int = 1) -> int:
|
||||
# Prepare
|
||||
highest = max(self.busses)
|
||||
highest_i = self.busses.index(highest)
|
||||
others = [
|
||||
(bus, i - highest_i)
|
||||
for i, bus in enumerate(self.busses)
|
||||
if bus != 0 and i != highest_i
|
||||
]
|
||||
others.sort(key=lambda x: x[0], reverse=True)
|
||||
|
||||
# Compute
|
||||
i: int = max(1, floor(offset / highest))
|
||||
while True:
|
||||
x = highest * i
|
||||
error = False
|
||||
for bus, diff in others:
|
||||
dx = x + diff
|
||||
if dx != self.get_next_for_id(bus, dx):
|
||||
error = True
|
||||
break
|
||||
if not error:
|
||||
return x - highest_i
|
||||
i += 1
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
|
||||
# Some "testing"
|
||||
all = {
|
||||
'17,x,13,19': 3417,
|
||||
'67,7,59,61': 754018,
|
||||
'67,x,7,59,61': 779210,
|
||||
'67,7,x,59,61': 1261476,
|
||||
'1789,37,47,1889': 1202161486,
|
||||
}
|
||||
for busses, expected in all.items():
|
||||
station = Station('69\n' + busses)
|
||||
print(expected, expected == station.contest())
|
||||
|
||||
txt = f.read()
|
||||
station = Station(txt)
|
||||
print(station.get_flag())
|
||||
print(station.contest(offset=10**14))
|
14
2020/solutions/2/README.md
Normal file
14
2020/solutions/2/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 2
|
||||
|
||||
For the first we can simply count the occurrences and see if they are between the accepted values.
|
||||
Just some simple parsing.
|
||||
|
||||
The second one is similar, but we can be more efficient if we XOR the first and second position.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>548</li>
|
||||
<li>502</li>
|
||||
</ol>
|
||||
</details>
|
77
2020/solutions/2/go/main.go
Normal file
77
2020/solutions/2/go/main.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type sRow struct {
|
||||
min, max int
|
||||
char, password string
|
||||
}
|
||||
|
||||
func parse(data []byte) []sRow {
|
||||
parsed := []sRow{}
|
||||
for _, row := range strings.Split(strings.TrimSpace(string(data)), "\n") {
|
||||
s0 := strings.Split(row, ":")
|
||||
rule := strings.TrimSpace(s0[0])
|
||||
password := strings.TrimSpace(s0[1])
|
||||
s1 := strings.Split(rule, " ")
|
||||
minMax := strings.TrimSpace(s1[0])
|
||||
char := strings.TrimSpace(s1[1])
|
||||
s2 := strings.Split(minMax, "-")
|
||||
min, _ := strconv.Atoi(strings.TrimSpace(s2[0]))
|
||||
max, _ := strconv.Atoi(strings.TrimSpace(s2[1]))
|
||||
|
||||
r := sRow{
|
||||
min: min,
|
||||
max: max,
|
||||
char: char,
|
||||
password: password,
|
||||
}
|
||||
parsed = append(parsed, r)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
func validSimple(rows []sRow) int {
|
||||
valid := 0
|
||||
for _, row := range rows {
|
||||
count := strings.Count(row.password, row.char)
|
||||
if row.min <= count && count <= row.max {
|
||||
valid++
|
||||
}
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
func validComplex(rows []sRow) int {
|
||||
valid := 0
|
||||
for _, row := range rows {
|
||||
l := len(row.password)
|
||||
min := row.min - 1
|
||||
max := row.max - 1
|
||||
if min >= l || max >= l {
|
||||
continue
|
||||
}
|
||||
r := []rune(row.password)
|
||||
a := string(r[min]) == row.char
|
||||
b := string(r[max]) == row.char
|
||||
if a != b {
|
||||
valid++
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, _ := ioutil.ReadFile("./solutions/2/data.txt")
|
||||
rows := parse(data)
|
||||
simple := validSimple(rows)
|
||||
fmt.Println(simple)
|
||||
complex := validComplex(rows)
|
||||
fmt.Println(complex)
|
||||
}
|
27
2020/solutions/2/python/main.py
Normal file
27
2020/solutions/2/python/main.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from os.path import join, dirname
|
||||
|
||||
|
||||
def checkRow(row: str, alternative=False) -> bool:
|
||||
rule, password = map(lambda s: s.strip(), row.split(':'))
|
||||
amount, char = rule.split(' ')
|
||||
minimum, maximum = map(int, amount.split('-'))
|
||||
if alternative:
|
||||
return (password[minimum - 1] == char) ^ (password[maximum - 1] == char)
|
||||
else:
|
||||
occurrences = password.count(char)
|
||||
return minimum <= occurrences <= maximum
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
valid = 0
|
||||
valid_alt = 0
|
||||
rows = list(f.read().strip().split('\n'))
|
||||
for row in rows:
|
||||
if(checkRow(row)):
|
||||
valid += 1
|
||||
if(checkRow(row, alternative=True)):
|
||||
valid_alt += 1
|
||||
print(f'Found {valid} valid passwords.')
|
||||
print('Policy changed...')
|
||||
print(f'Found {valid_alt} valid passwords.')
|
16
2020/solutions/3/README.md
Normal file
16
2020/solutions/3/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 3
|
||||
|
||||
We can simply parse the forest as an array of strings.
|
||||
The trees repeat infinitely to the right, this screams for a good mod.
|
||||
This means: `char = row[x % len(row)]`. No complex logic needed
|
||||
|
||||
For the second one we simply automate the process and sum up the total.
|
||||
We can simply encode the coordinates as a function of the index we are current at.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>252</li>
|
||||
<li>57 * 252 * 64 * 66 * 43 = 2608962048</li>
|
||||
</ol>
|
||||
</details>
|
66
2020/solutions/3/go/main.go
Normal file
66
2020/solutions/3/go/main.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const high = rune('#')
|
||||
|
||||
type sForest struct {
|
||||
data [][]bool
|
||||
height int
|
||||
width int
|
||||
}
|
||||
|
||||
func (f sForest) at(y, x int) bool {
|
||||
if y >= f.height {
|
||||
return false
|
||||
}
|
||||
return f.data[y][x%f.width]
|
||||
}
|
||||
|
||||
func (f sForest) traverse(y, x int) int {
|
||||
trees := 0
|
||||
for dy := 0; dy <= f.height; dy++ {
|
||||
tree := f.at(dy*y, dy*x)
|
||||
if tree {
|
||||
trees++
|
||||
}
|
||||
}
|
||||
return trees
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, _ := ioutil.ReadFile("./solutions/3/data.txt")
|
||||
|
||||
rows := strings.Split(strings.TrimSpace(string(data)), "\n")
|
||||
height, width := len(rows), len(rows[0])
|
||||
d := make([][]bool, height)
|
||||
|
||||
for y, row := range rows {
|
||||
d[y] = make([]bool, width)
|
||||
for x, char := range []rune(row) {
|
||||
d[y][x] = char == high
|
||||
}
|
||||
}
|
||||
|
||||
forest := sForest{
|
||||
data: d,
|
||||
height: height,
|
||||
width: width,
|
||||
}
|
||||
|
||||
fmt.Println("Simple: ", forest.traverse(1, 3))
|
||||
|
||||
trees11 := forest.traverse(1, 1)
|
||||
trees13 := forest.traverse(1, 3)
|
||||
trees15 := forest.traverse(1, 5)
|
||||
trees17 := forest.traverse(1, 7)
|
||||
trees21 := forest.traverse(2, 1)
|
||||
|
||||
fmt.Println(trees11, trees13, trees15, trees17, trees21)
|
||||
fmt.Println(trees11 * trees13 * trees15 * trees17 * trees21)
|
||||
|
||||
}
|
50
2020/solutions/3/python/main.py
Normal file
50
2020/solutions/3/python/main.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from typing import Dict
|
||||
from os.path import join, dirname
|
||||
from functools import reduce
|
||||
|
||||
|
||||
class Forest():
|
||||
|
||||
def __init__(self, text: str) -> None:
|
||||
self.text = text.strip().split('\n')
|
||||
|
||||
@property
|
||||
def height(self) -> int:
|
||||
return len(self.text)
|
||||
|
||||
def is_tree_at(self, y: int, x: int) -> bool:
|
||||
if y > self.height:
|
||||
return False
|
||||
row = self.text[y]
|
||||
return row[x % len(row)] == '#'
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
forest = Forest(f.read())
|
||||
|
||||
# 1
|
||||
trees: int = 0
|
||||
for y in range(forest.height):
|
||||
is_tree: bool = forest.is_tree_at(y, y*3)
|
||||
if is_tree:
|
||||
trees += 1
|
||||
print(f'Result Simple: {trees}')
|
||||
|
||||
# 2
|
||||
all: Dict[str, int] = {
|
||||
'11': 0,
|
||||
'13': 0,
|
||||
'15': 0,
|
||||
'17': 0,
|
||||
'21': 0,
|
||||
}
|
||||
for i in range(forest.height):
|
||||
for key, value in all.items():
|
||||
dy, dx = map(int, list(key))
|
||||
y = i * dy
|
||||
x = i * dx
|
||||
if forest.is_tree_at(y, x):
|
||||
all[key] += 1
|
||||
total = reduce((lambda x, y: x * y), all.values())
|
||||
print(f'Result Combined: {list(all.values())} = {total}')
|
14
2020/solutions/4/README.md
Normal file
14
2020/solutions/4/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 4
|
||||
|
||||
This one was a lot of parsing, but nothing regexp can't do.
|
||||
The first is quite straight forward, just check that all but `cid` are present.
|
||||
|
||||
The second was a bit of validation for each field, but again some simple regexp and number checking and the job is done 🙂
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>206</li>
|
||||
<li>123</li>
|
||||
</ol>
|
||||
</details>
|
103
2020/solutions/4/go/main.go
Normal file
103
2020/solutions/4/go/main.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
k "github.com/wesovilabs/koazee"
|
||||
)
|
||||
|
||||
type tPassport = map[string]string
|
||||
|
||||
func stringBetween(s string, min, max int) bool {
|
||||
num, _ := strconv.Atoi(s)
|
||||
return min <= num && num <= max
|
||||
}
|
||||
|
||||
func verifyPassport(passport tPassport) (bool, bool) {
|
||||
requiredKeys := k.StreamOf([]string{"byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"})
|
||||
eyeColors := k.StreamOf([]string{"amb", "blu", "brn", "gry", "grn", "hzl", "oth"})
|
||||
|
||||
// Simple
|
||||
counted := 0
|
||||
for k := range passport {
|
||||
if k == "cid" {
|
||||
continue
|
||||
}
|
||||
included, _ := requiredKeys.Contains(k)
|
||||
if !included {
|
||||
return false, false
|
||||
}
|
||||
counted++
|
||||
}
|
||||
if counted < 7 {
|
||||
return false, false
|
||||
}
|
||||
|
||||
// Complex
|
||||
if !stringBetween(passport["byr"], 1920, 2002) || !stringBetween(passport["iyr"], 2010, 2020) || !stringBetween(passport["eyr"], 2020, 2030) {
|
||||
return true, false
|
||||
}
|
||||
|
||||
tmp := []rune(passport["hgt"])
|
||||
hgtLen := len(tmp)
|
||||
hgt, _ := strconv.Atoi(string(tmp[0 : hgtLen-2]))
|
||||
unit := string(tmp[hgtLen-2:])
|
||||
if unit != "cm" && unit != "in" {
|
||||
return true, false
|
||||
}
|
||||
if unit == "cm" && (hgt < 150 || hgt > 193) {
|
||||
return true, false
|
||||
}
|
||||
if unit == "in" && (hgt < 59 || hgt > 76) {
|
||||
return true, false
|
||||
}
|
||||
|
||||
if !regexp.MustCompile(`^#[\dabdcdef]{6}$`).MatchString(passport["hcl"]) {
|
||||
return true, false
|
||||
}
|
||||
if !regexp.MustCompile(`^\d{9}$`).MatchString(passport["pid"]) {
|
||||
return true, false
|
||||
}
|
||||
|
||||
ecl, _ := eyeColors.Contains(passport["ecl"])
|
||||
if !ecl {
|
||||
return true, false
|
||||
}
|
||||
|
||||
return true, true
|
||||
}
|
||||
|
||||
func main() {
|
||||
data, _ := ioutil.ReadFile("./solutions/4/data.txt")
|
||||
passportsRaw := strings.Split(strings.TrimSpace(string(data)), "\n\n")
|
||||
passports := []tPassport{}
|
||||
|
||||
re := regexp.MustCompile(`\n|\s`)
|
||||
for _, passportRaw := range passportsRaw {
|
||||
passport := tPassport{}
|
||||
entries := re.Split(passportRaw, -1)
|
||||
for _, entry := range entries {
|
||||
split := strings.Split(entry, ":")
|
||||
passport[split[0]] = split[1]
|
||||
}
|
||||
passports = append(passports, passport)
|
||||
}
|
||||
|
||||
validSimple := 0
|
||||
validComplex := 0
|
||||
for _, passport := range passports {
|
||||
simple, complex := verifyPassport(passport)
|
||||
if simple {
|
||||
validSimple++
|
||||
}
|
||||
if complex {
|
||||
validComplex++
|
||||
}
|
||||
}
|
||||
fmt.Println("Simple Validation:\t", validSimple)
|
||||
fmt.Println("Extended Validation:\t", validComplex)
|
||||
}
|
52
2020/solutions/4/python/main.py
Normal file
52
2020/solutions/4/python/main.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from os.path import join, dirname
|
||||
import re
|
||||
|
||||
|
||||
def validate_chunk(chunk, extended=False):
|
||||
parts = re.split(' |\n', chunk.strip())
|
||||
password = dict(map(lambda p: p.split(":"), parts))
|
||||
|
||||
required = ['byr', 'iyr', 'eyr', 'hgt', 'hcl', 'ecl', 'pid']
|
||||
if not all(item in password.keys() for item in required):
|
||||
return False
|
||||
|
||||
if not extended:
|
||||
return True
|
||||
|
||||
if not 1920 <= int(password['byr']) <= 2002:
|
||||
return False
|
||||
if not 2010 <= int(password['iyr']) <= 2020:
|
||||
return False
|
||||
if not 2020 <= int(password['eyr']) <= 2030:
|
||||
return False
|
||||
|
||||
tmp = password['hgt']
|
||||
hgt = int(tmp[:-2])
|
||||
unit = tmp[-2:]
|
||||
if not unit in ['cm', 'in']:
|
||||
return False
|
||||
if unit == 'cm' and not 150 <= hgt <= 193:
|
||||
return False
|
||||
if unit == 'in' and not 59 <= hgt <= 76:
|
||||
return False
|
||||
|
||||
if not re.match(r'^#[\dabcdef]{6}$', password['hcl']):
|
||||
return False
|
||||
if not re.match(r'^\d{9}$', password['pid']):
|
||||
return False
|
||||
|
||||
if password['ecl'] not in ['amb', 'blu', 'brn', 'gry', 'grn', 'hzl', 'oth']:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
chunks = re.split('\n\n+', f.read().strip())
|
||||
total_simple = 0
|
||||
total_extended = 0
|
||||
for chunk in chunks:
|
||||
total_simple += int(validate_chunk(chunk))
|
||||
total_extended += int(validate_chunk(chunk, extended=True))
|
||||
print(f'Simple Validation:\t{total_simple}')
|
||||
print(f'Extended Validation:\t{total_extended}')
|
13
2020/solutions/5/README.md
Normal file
13
2020/solutions/5/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 5
|
||||
|
||||
For the first one we treat the codes as 2 binary codes. One 7 and the other 3 long.
|
||||
|
||||
The second tripped me up as I was returning the binary encoded ticket number but AOC was expecting the seat ID. My bad, took me long to get it. Thanks to @tcq1
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>998</li>
|
||||
<li>84, 4 -> 676</li>
|
||||
</ol>
|
||||
</details>
|
51
2020/solutions/5/python/main.py
Normal file
51
2020/solutions/5/python/main.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from os.path import join, dirname
|
||||
from itertools import product
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
def from_binary(code: str, high: str) -> int:
|
||||
return int(''.join([
|
||||
'1' if char == high else '0'
|
||||
for char in code
|
||||
]), 2)
|
||||
|
||||
|
||||
def read_seat(seat) -> Tuple[int, int]:
|
||||
row_raw = seat[:-3]
|
||||
column_raw = seat[-3:]
|
||||
row = from_binary(row_raw, 'B')
|
||||
column = from_binary(column_raw, 'R')
|
||||
return row, column
|
||||
|
||||
|
||||
def seat_code(row: int, column: int) -> int:
|
||||
return row * 8 + column
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
seats = f.read().strip().split('\n')
|
||||
|
||||
free = list(product(range(4, 126), range(8)))
|
||||
maximum = 0
|
||||
for seat in seats:
|
||||
row, column = read_seat(seat)
|
||||
m = row * 8 + column
|
||||
maximum = max(maximum, m)
|
||||
free.remove((row, column))
|
||||
|
||||
print(f"Highers ID:\t{maximum}")
|
||||
|
||||
# Find the remaining seat
|
||||
row_max = 0
|
||||
row_min = 128
|
||||
for row, _ in free:
|
||||
row_max = max(row_max, row)
|
||||
row_min = min(row_min, row)
|
||||
remaining = [
|
||||
(row, column)
|
||||
for row, column in free
|
||||
if row >= row_min + 1 and row <= row_max - 2
|
||||
][0]
|
||||
my_ticket = seat_code(*remaining)
|
||||
print(f"My Ticket:\t{my_ticket}")
|
13
2020/solutions/6/README.md
Normal file
13
2020/solutions/6/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# 6
|
||||
|
||||
Basically I own this one to the built in `set()` of python.
|
||||
|
||||
The first is an union, the second an intersection. Did not know they existed
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>6809</li>
|
||||
<li>3394</li>
|
||||
</ol>
|
||||
</details>
|
28
2020/solutions/6/python/main.py
Normal file
28
2020/solutions/6/python/main.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from os.path import join, dirname
|
||||
from itertools import product
|
||||
from typing import List, Set, Tuple
|
||||
from functools import reduce
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
groups = f.read().strip().split('\n\n')
|
||||
|
||||
at_least_one: List[int] = []
|
||||
everyone: List[int] = []
|
||||
for group in groups:
|
||||
answers: Set[str] = set()
|
||||
combined = None
|
||||
for answer in group.split('\n'):
|
||||
answer = answer.strip()
|
||||
as_set = set(list(answer))
|
||||
answers = answers.union(as_set)
|
||||
combined = as_set if combined == None else combined.intersection(
|
||||
as_set)
|
||||
at_least_one.append(len(answers))
|
||||
everyone.append(len(combined))
|
||||
# print(single)
|
||||
# print(reduce(lambda a, b: a.intersection(b), single))
|
||||
|
||||
print(f'At least one person: {sum(at_least_one)}')
|
||||
print(f'Everyone: {sum(everyone)}')
|
15
2020/solutions/7/README.md
Normal file
15
2020/solutions/7/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 7
|
||||
|
||||
This one was maybe the coolest yet! Fixed-point iteration and some recursion. Amazing :)
|
||||
|
||||
For the first part we iterate as long as we don't find any enclosing bags anymore. This can build long chains.
|
||||
|
||||
The second we recurse down the bag chain und sum it up recursively.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>164</li>
|
||||
<li>7872</li>
|
||||
</ol>
|
||||
</details>
|
70
2020/solutions/7/python/main.py
Normal file
70
2020/solutions/7/python/main.py
Normal file
@@ -0,0 +1,70 @@
|
||||
from os.path import join, dirname
|
||||
from typing import Dict, List, Set, Tuple
|
||||
import re
|
||||
|
||||
TRules = Dict[str, Dict[str, int]]
|
||||
|
||||
|
||||
def split_trim(input: str, split: str) -> List[str]:
|
||||
return list(map(lambda s: s.strip(), input.split(split)))
|
||||
|
||||
|
||||
def extract_inner(input: str) -> Tuple[int, str]:
|
||||
parts = input.split(' ')
|
||||
amount = int(parts[0])
|
||||
color = ' '.join(parts[1:-1])
|
||||
return amount, color
|
||||
|
||||
|
||||
def parse_rules(rules: str) -> TRules:
|
||||
d: TRules = {}
|
||||
for rule in rules.strip().split('\n'):
|
||||
outer, inner = split_trim(rule, 'contain')
|
||||
outer = re.sub(r'bags?', '', outer).strip()
|
||||
d[outer] = {
|
||||
color: amount
|
||||
for amount, color in [
|
||||
extract_inner(i)
|
||||
for i in split_trim(inner, ',')
|
||||
if 'no other bag' not in i # Also matches "bags"
|
||||
]
|
||||
}
|
||||
return d
|
||||
|
||||
|
||||
def find_enclosing_bags(rules: TRules, color: str) -> Set[str]:
|
||||
colors: Set[str] = set()
|
||||
stack: Set[str] = set([color])
|
||||
|
||||
while len(stack):
|
||||
for item in list(stack):
|
||||
stack.remove(item)
|
||||
for contains, enclosing in rules.items():
|
||||
if item in enclosing:
|
||||
if contains not in colors:
|
||||
stack.add(contains)
|
||||
colors.add(contains)
|
||||
return colors
|
||||
|
||||
|
||||
def count_containing(rules: TRules, color: str) -> int:
|
||||
children = rules[color]
|
||||
if not children:
|
||||
return 0
|
||||
|
||||
return sum([
|
||||
amount + amount * count_containing(rules, clr)
|
||||
for clr, amount in children.items()
|
||||
])
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
rules = parse_rules(f.read())
|
||||
|
||||
color = 'shiny gold'
|
||||
first = len(find_enclosing_bags(rules, color))
|
||||
print(f'We can pack the {color} into {first} bags')
|
||||
|
||||
second = count_containing(rules, color)
|
||||
print(f'We need to put {second} bags into {color}')
|
15
2020/solutions/8/README.md
Normal file
15
2020/solutions/8/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 8
|
||||
|
||||
Keep getting better! Today we write a little state machine, love it.
|
||||
|
||||
So firstly we remember what `oc` (Operation Counter) we have already visited and if so we simply stop and return the accumulator.
|
||||
|
||||
For I basically run the code many times, inverting `nop` to `jmp` and vice versa until i found a working instruction set.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>1087</li>
|
||||
<li>780</li>
|
||||
</ol>
|
||||
</details>
|
63
2020/solutions/8/python/main.py
Normal file
63
2020/solutions/8/python/main.py
Normal file
@@ -0,0 +1,63 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Optional, Set, Tuple
|
||||
|
||||
Instructions = List[Tuple[str, int]]
|
||||
|
||||
|
||||
class VM:
|
||||
def __init__(self, code: str) -> None:
|
||||
instructionsRaw = code.strip().split('\n')
|
||||
self.acc = 0
|
||||
self.oc = 0
|
||||
self.invert: Optional[int] = None
|
||||
self.instructions: Instructions = []
|
||||
for instruction in instructionsRaw:
|
||||
op, value = instruction.split(' ')
|
||||
self.instructions.append((op, int(value)))
|
||||
|
||||
def reset(self):
|
||||
self.acc = 0
|
||||
self.oc = 0
|
||||
|
||||
def exec(self):
|
||||
op, value = self.instructions[self.oc]
|
||||
if self.oc == self.invert:
|
||||
op = 'jmp' if op == 'nop' else 'nop'
|
||||
if op == 'nop':
|
||||
self.oc += 1
|
||||
elif op == 'acc':
|
||||
self.acc += value
|
||||
self.oc += 1
|
||||
elif op == 'jmp':
|
||||
self.oc += value
|
||||
|
||||
def run(self) -> Tuple[int, bool]:
|
||||
self.reset()
|
||||
already_visited: Set[int] = set()
|
||||
m = len(self.instructions)
|
||||
while True:
|
||||
if self.oc in already_visited:
|
||||
return (self.acc, True)
|
||||
if not self.oc < m:
|
||||
return (self.acc, False)
|
||||
already_visited.add(self.oc)
|
||||
self.exec()
|
||||
|
||||
def fix(self):
|
||||
for i, instruction in enumerate(self.instructions):
|
||||
op, _ = instruction
|
||||
if op == 'nop' or op == 'jmp':
|
||||
self.invert = i
|
||||
acc, error = self.run()
|
||||
if not error:
|
||||
return acc
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
vm = VM(f.read())
|
||||
|
||||
acc, err = vm.run()
|
||||
print(acc)
|
||||
|
||||
print(vm.fix())
|
11
2020/solutions/9/README.md
Normal file
11
2020/solutions/9/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 9
|
||||
|
||||
Again the python std lib saved me some lines of code. `itertools` to the rescue and basically we are done.
|
||||
|
||||
<details>
|
||||
<summary>Solutions</summary>
|
||||
<ol>
|
||||
<li>23278925</li>
|
||||
<li>4011064</li>
|
||||
</ol>
|
||||
</details>
|
50
2020/solutions/9/python/main.py
Normal file
50
2020/solutions/9/python/main.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from os.path import join, dirname
|
||||
from typing import List, Optional, Set, Tuple
|
||||
from itertools import combinations
|
||||
|
||||
|
||||
class XMAS:
|
||||
|
||||
def __init__(self, data: str, size: int) -> None:
|
||||
self.size: int = size
|
||||
self.position: int = size
|
||||
self.buffer: List[int] = [
|
||||
int(x)
|
||||
for x in data.strip().split('\n')
|
||||
]
|
||||
|
||||
def check_next(self) -> bool:
|
||||
possible = [
|
||||
a + b
|
||||
for a, b in combinations(self.buffer[self.position - self.size: self.position], 2)
|
||||
]
|
||||
return self.buffer[self.position] in possible
|
||||
|
||||
def find_first_invalid(self) -> int:
|
||||
l = len(self.buffer)
|
||||
while self.position < l:
|
||||
valid = self.check_next()
|
||||
if not valid:
|
||||
return self.buffer[self.position]
|
||||
self.position += 1
|
||||
raise Exception
|
||||
|
||||
def find_slice(self, target: int):
|
||||
l = len(self.buffer)
|
||||
for n in range(l):
|
||||
for m in range(n, l):
|
||||
slice = self.buffer[n: m]
|
||||
if sum(slice) == target:
|
||||
return min(slice) + max(slice)
|
||||
raise Exception
|
||||
|
||||
|
||||
data = join(dirname(__file__), '../data.txt')
|
||||
with open(data) as f:
|
||||
xmas = XMAS(f.read(), 25)
|
||||
|
||||
first_invalid = xmas.find_first_invalid()
|
||||
print(first_invalid)
|
||||
|
||||
solution = xmas.find_slice(first_invalid)
|
||||
print(solution)
|
Reference in New Issue
Block a user