mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-26 21:30:31 +01:00
Compare commits
151 Commits
8a137deae4
...
2022/day-2
| Author | SHA1 | Date | |
|---|---|---|---|
| 561fd2f07c | |||
| 256d351f8e | |||
| 48594a75e6 | |||
| 85a51b13c1 | |||
| 2ae2d6baa8 | |||
| 4a55e53182 | |||
| af0897300d | |||
| cabae7b1fd | |||
| 0635141ac6 | |||
| d9d5947c3b | |||
| cc8b4ce353 | |||
| 0b91da04b3 | |||
| dba146b299 | |||
| 33111615be | |||
| 04e8a41d98 | |||
| 36d76018ba | |||
| 4172fd0463 | |||
| ad0b4a4659 | |||
| 2dab7342f8 | |||
| edd14a0e3d | |||
| 4d7188e1ff | |||
| 255edaca79 | |||
| 8ea716cba8 | |||
| 601de2c565 | |||
| 894524bc81 | |||
| f19bf28f34 | |||
| de3a24a87c | |||
| 09b590e927 | |||
| 9dacb4c1ae | |||
| 07e03c1630 | |||
| 3accf9845d | |||
| fd26f58e25 | |||
| b2f9898714 | |||
| d757c389f0 | |||
| fd561a3e9d | |||
| 2fcdc6b8d2 | |||
| 8a3f0f843c | |||
| 23b5c39838 | |||
| 452f6e5f14 | |||
| 61fb240622 | |||
| aee25057d6 | |||
| a98332894f | |||
| 09e012c082 | |||
| 944d3e644a | |||
| d56f4ae8f8 | |||
| 53ca8d0043 | |||
| 6506af879a | |||
| 101ebee505 | |||
| cc81a7012b | |||
| 9c299f140c | |||
| ba1b7b693e | |||
| 7d331f9131 | |||
| dfd8b2b985 | |||
| 0f6167e90f | |||
| 3e2a3f5206 | |||
| 8cc2245492 | |||
| fb167ef899 | |||
| 4240f8fc8c | |||
| 9a4ac427e0 | |||
| c3929a56d8 | |||
| 9e37026f30 | |||
| a926243f0d | |||
| a35ae82548 | |||
| 8a0b72f111 | |||
| b5866b2d8a | |||
| 554683bc64 | |||
| 1031a8cdbb | |||
| 5c764f6627 | |||
| ffe8d27469 | |||
| d471f170b3 | |||
| 07cbf6cf53 | |||
| 6e3252ce5a | |||
| 0897e2e907 | |||
| 4d1fdd9cc0 | |||
| 84c160cf54 | |||
| 440d454911 | |||
| 50cd6d8171 | |||
| dde9c0adbf | |||
| e0e1bc26e8 | |||
| 77ce31980b | |||
| 8c78106846 | |||
| 22f767e8df | |||
| 3434966ac2 | |||
| c5f66fcc09 | |||
| c02f1e11c5 | |||
| d099614217 | |||
| 766ee91719 | |||
| 2f3eb50a5b | |||
| b369f9d36a | |||
| 1433b0cdbe | |||
| 5e52da6e6b | |||
| e50b812aed | |||
| fdef10a78e | |||
| 392aefb32d | |||
| fb358be8f0 | |||
| 612d3ecb6b | |||
| d08a4e0e4e | |||
| ed844a997c | |||
| c9468ba139 | |||
| 938eda0d22 | |||
| f413b08da6 | |||
| 10531e3422 | |||
| 2e0a7ea81d | |||
| 2c64028978 | |||
| 89159137fe | |||
| 186d91d1b7 | |||
| c985ba8a1a | |||
| cece8439a7 | |||
| 5f98b62f21 | |||
| affaf6e96f | |||
| a6996e5234 | |||
| a64eff96a4 | |||
| 7718fc59c6 | |||
| 4e3c9c75c5 | |||
| 583b590a6c | |||
| ef302bbeb7 | |||
| 5791760412 | |||
| e66f3df949 | |||
| 71f54bac6b | |||
| dbf6b2eb17 | |||
| 05e2a4f3a7 | |||
| ca43475a44 | |||
| 3f8c9505c3 | |||
| befa2eefaa | |||
| 22091fc8c3 | |||
| abc742e2ad | |||
| a43f260e1b | |||
| beae045f55 | |||
| 2d561eab7d | |||
| 165159cba9 | |||
| eff22abf8a | |||
| 43c063cef9 | |||
| c3671ec177 | |||
| c8c616dffc | |||
| f94661d7a8 | |||
| 2c767d5996 | |||
| dd039e9276 | |||
| 2907726363 | |||
| 091c125f42 | |||
| b65c805891 | |||
| 4418292dc4 | |||
| 18351f93eb | |||
| fe639f14b3 | |||
| 07098ab691 | |||
| b18945c44d | |||
| ad3029759a | |||
| 7a292b026d | |||
| 930d86404d | |||
| 07e869c497 | |||
| 07db73aa3e | |||
| 69de955158 |
18
.github/workflows/2019.yml
vendored
Normal file
18
.github/workflows/2019.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
name: AOC 2019 test suite
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.9
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r 2019/requirements.txt
|
||||||
|
- name: Run Pytest
|
||||||
|
run: pytest 2019
|
||||||
52
.github/workflows/2022.yml
vendored
Normal file
52
.github/workflows/2022.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
on:
|
||||||
|
- push
|
||||||
|
|
||||||
|
name: Advent of Code 2022
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolchain:
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
experimental: [false]
|
||||||
|
include:
|
||||||
|
- toolchain: nightly
|
||||||
|
experimental: true
|
||||||
|
|
||||||
|
name: Continuous Integration
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: ${{ matrix.toolchain }}
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Set up caching
|
||||||
|
uses: Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: >
|
||||||
|
2022 -> target
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
working-directory: 2022
|
||||||
|
run: >
|
||||||
|
cargo build --all-targets
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: 2022
|
||||||
|
run: >
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
- name: Run clippy
|
||||||
|
working-directory: 2022
|
||||||
|
run: >
|
||||||
|
cargo clippy -- --deny warnings
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -56,3 +56,11 @@ docs/_build/
|
|||||||
# PyBuilder
|
# PyBuilder
|
||||||
target/
|
target/
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
# Rust lock
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
# Performance data
|
||||||
|
perf.data
|
||||||
|
perf.data.old
|
||||||
|
flamegraph.svg
|
||||||
|
|||||||
19
.travis.yml
19
.travis.yml
@@ -1,19 +0,0 @@
|
|||||||
dist: bionic
|
|
||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- stable
|
|
||||||
- beta
|
|
||||||
- nightly
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
allow_failures:
|
|
||||||
- rust: nightly
|
|
||||||
fast_finish: true
|
|
||||||
|
|
||||||
cache:
|
|
||||||
- cargo
|
|
||||||
- 2020/target
|
|
||||||
|
|
||||||
# Custom directory, for the correct year
|
|
||||||
before_script:
|
|
||||||
- cd 2020
|
|
||||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
|||||||
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "0.1"
|
regex = "1"
|
||||||
|
|||||||
@@ -73,10 +73,10 @@ fn main() {
|
|||||||
let door_label = line.unwrap();
|
let door_label = line.unwrap();
|
||||||
let caps = room_pattern.captures(&door_label).unwrap();
|
let caps = room_pattern.captures(&door_label).unwrap();
|
||||||
|
|
||||||
let name = caps.at(1).unwrap();
|
let name = caps.get(1).unwrap().as_str();
|
||||||
let checksum = caps.at(4).unwrap();
|
let checksum = caps.get(4).unwrap().as_str();
|
||||||
if is_valid(name, checksum) {
|
if is_valid(name, checksum) {
|
||||||
let sector_id = caps.at(3).unwrap().parse().unwrap();
|
let sector_id = caps.get(3).unwrap().as_str().parse().unwrap();
|
||||||
cur_sum += sector_id;
|
cur_sum += sector_id;
|
||||||
|
|
||||||
let decoded: String = name.chars()
|
let decoded: String = name.chars()
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ version = "0.1.0"
|
|||||||
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex = "^0.1"
|
regex = "1"
|
||||||
lazy_static = "^0.2"
|
lazy_static = "1"
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
|||||||
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
regex="^0.1"
|
regex="1"
|
||||||
|
|||||||
142
2019/.gitignore
vendored
142
2019/.gitignore
vendored
@@ -1 +1,141 @@
|
|||||||
cmake-build-*
|
# Virtual environment
|
||||||
|
venv
|
||||||
|
|
||||||
|
# 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/
|
||||||
|
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/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# 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
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .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/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.12)
|
|
||||||
|
|
||||||
project(aoc2019)
|
|
||||||
|
|
||||||
find_package(GTest REQUIRED)
|
|
||||||
|
|
||||||
option(ANIMATE_DAY13 "Animate the Arkanoid game in day 13." Off)
|
|
||||||
|
|
||||||
file(GLOB DAYS_FILES src/day*.cpp)
|
|
||||||
add_library(AoCSolutions src/implementations.cpp "${DAYS_FILES}" src/point.hpp src/utils.cpp src/utils.hpp)
|
|
||||||
target_compile_features(AoCSolutions PUBLIC cxx_std_17)
|
|
||||||
|
|
||||||
if (ANIMATE_DAY13)
|
|
||||||
target_compile_definitions(AoCSolutions ANIMATE_DAY13)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_executable(runner src/runner.cpp)
|
|
||||||
target_compile_features(runner PUBLIC cxx_std_17)
|
|
||||||
target_link_libraries(runner AoCSolutions)
|
|
||||||
|
|
||||||
add_executable(unit_tests tests/test_solutions.cpp tests/test_intcode.cpp)
|
|
||||||
target_compile_features(unit_tests PUBLIC cxx_std_17)
|
|
||||||
target_link_libraries(unit_tests AoCSolutions GTest::GTest GTest::Main)
|
|
||||||
target_compile_definitions(unit_tests PRIVATE "TEST_SAMPLES_DIR=\"${CMAKE_SOURCE_DIR}/tests/samples\"")
|
|
||||||
target_include_directories(unit_tests PRIVATE "${CMAKE_SOURCE_DIR}/src")
|
|
||||||
|
|
||||||
enable_testing()
|
|
||||||
gtest_discover_tests(unit_tests NO_PRETTY_VALUES)
|
|
||||||
@@ -1,31 +1,8 @@
|
|||||||
# Advent of Code 2019
|
# Advent of Code 2019
|
||||||
|
|
||||||
This project contains my implementations for Advent of Code 2019. The
|
This is a quick-and-dirty implementation of all 2019 problems implemented in
|
||||||
goal is to create reasonably fast C++ implementations in readable and
|
Python because I got fed up with C++ and really couldn't stand the missing
|
||||||
ergonomic C++. At the end of the contest, I will probably do a write-
|
stars on the [events page](https://adventofcode.com/2020/events).
|
||||||
up of some sorts.
|
|
||||||
|
|
||||||
|
I'll try to incorporate unit tests and all because it's just nice programming,
|
||||||
## How to compile
|
but this edition has decidedly less polish than my other attempts.
|
||||||
|
|
||||||
Install the dependencies:
|
|
||||||
|
|
||||||
- [GTest](https://github.com/google/googletest) **Note:** this project
|
|
||||||
by default tries to dynamically link GTest, and the Ubuntu packages
|
|
||||||
only provide a statically linked archive. You may need to compile it
|
|
||||||
for yourself.
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir build && cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
You can then use the generated executable `runner`.
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
Tests can be executed with `make test`. The `tests` folder contains a
|
|
||||||
`samples` folder. This folder contains pairs of `XX-Y-something.in` and
|
|
||||||
`XX-Y-something.out`, which will be taken as the expected input and
|
|
||||||
output of the implementations. You can add your own samples to this mix.
|
|
||||||
|
|||||||
0
2019/aoc2019/__init__.py
Normal file
0
2019/aoc2019/__init__.py
Normal file
28
2019/aoc2019/__main__.py
Normal file
28
2019/aoc2019/__main__.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import argparse
|
||||||
|
import importlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('day', type=int)
|
||||||
|
parser.add_argument('input', type=argparse.FileType('rt'), nargs='?', default=sys.stdin)
|
||||||
|
parser.add_argument('-2', '--part2', action='store_true')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
day = importlib.import_module(f'.day{args.day:02d}', __package__)
|
||||||
|
|
||||||
|
if args.part2:
|
||||||
|
function = day.part2 # type: ignore
|
||||||
|
else:
|
||||||
|
function = day.part1 # type: ignore
|
||||||
|
|
||||||
|
print(function(args.input))
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
sys.exit(f'Invalid day: {args.day}')
|
||||||
|
|
||||||
|
|
||||||
|
main()
|
||||||
23
2019/aoc2019/day01.py
Normal file
23
2019/aoc2019/day01.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
|
||||||
|
def fuel_required(weight: int) -> int:
|
||||||
|
return max(0, weight // 3 - 2)
|
||||||
|
|
||||||
|
|
||||||
|
def recursive_fuel_required(weight: int) -> int:
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
while weight > 0:
|
||||||
|
weight = fuel_required(weight)
|
||||||
|
total += weight
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return sum(fuel_required(int(line)) for line in data)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
return sum(recursive_fuel_required(int(line)) for line in data)
|
||||||
33
2019/aoc2019/day02.py
Normal file
33
2019/aoc2019/day02.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
program[1] = 12
|
||||||
|
program[2] = 2
|
||||||
|
|
||||||
|
computer = Computer(program)
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
return computer[0]
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
for verb in range(100):
|
||||||
|
for noun in range(100):
|
||||||
|
computer = Computer(program.copy())
|
||||||
|
|
||||||
|
computer[1] = noun
|
||||||
|
computer[2] = verb
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
if computer[0] == 19690720:
|
||||||
|
return 100 * noun + verb
|
||||||
|
|
||||||
|
raise ValueError('Did not find valid combination')
|
||||||
44
2019/aoc2019/day03.py
Normal file
44
2019/aoc2019/day03.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import itertools
|
||||||
|
from typing import Dict, TextIO
|
||||||
|
|
||||||
|
|
||||||
|
def compute_points(line: str) -> Dict[complex, int]:
|
||||||
|
points: Dict[complex, int] = {}
|
||||||
|
steps = itertools.count(1)
|
||||||
|
|
||||||
|
pos = complex(0)
|
||||||
|
|
||||||
|
directions = {
|
||||||
|
'U': 1j,
|
||||||
|
'R': 1,
|
||||||
|
'D': -1j,
|
||||||
|
'L': -1,
|
||||||
|
}
|
||||||
|
|
||||||
|
for move in line.strip().split(','):
|
||||||
|
direction = directions[move[0]]
|
||||||
|
|
||||||
|
for _ in range(int(move[1:])):
|
||||||
|
pos += direction
|
||||||
|
|
||||||
|
points.setdefault(pos, next(steps))
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
points_a = compute_points(next(data))
|
||||||
|
points_b = compute_points(next(data))
|
||||||
|
|
||||||
|
in_common = set(points_a.keys()) & set(points_b.keys())
|
||||||
|
|
||||||
|
return int(min(abs(c.imag) + abs(c.real) for c in in_common))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
points_a = compute_points(next(data))
|
||||||
|
points_b = compute_points(next(data))
|
||||||
|
|
||||||
|
in_common = set(points_a.keys()) & set(points_b.keys())
|
||||||
|
|
||||||
|
return min(points_a[pos] + points_b[pos] for pos in in_common)
|
||||||
38
2019/aoc2019/day04.py
Normal file
38
2019/aoc2019/day04.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import itertools
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
|
||||||
|
def read_range(data: TextIO) -> range:
|
||||||
|
a, b = next(data).split('-')
|
||||||
|
|
||||||
|
return range(int(a), int(b) + 1) # plus one because inclusive
|
||||||
|
|
||||||
|
|
||||||
|
def valid(number: int, strict: bool) -> bool:
|
||||||
|
s = str(number)
|
||||||
|
prev = '/' # is smaller than '0'
|
||||||
|
has_group = False
|
||||||
|
|
||||||
|
if len(s) != 6:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for k, g in itertools.groupby(s):
|
||||||
|
if k < prev:
|
||||||
|
return False
|
||||||
|
|
||||||
|
prev = k
|
||||||
|
|
||||||
|
amount = sum(1 for _ in g)
|
||||||
|
|
||||||
|
if amount == 2 or not strict and amount > 2:
|
||||||
|
has_group = True
|
||||||
|
|
||||||
|
return has_group
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return sum(1 for password in read_range(data) if valid(password, False))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
return sum(1 for password in read_range(data) if valid(password, True))
|
||||||
29
2019/aoc2019/day05.py
Normal file
29
2019/aoc2019/day05.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
computer = Computer(program)
|
||||||
|
|
||||||
|
# Enter the required starting code
|
||||||
|
computer.input.append(1)
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
return computer.output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
computer = Computer(program)
|
||||||
|
|
||||||
|
# Enter the required starting code
|
||||||
|
computer.input.append(5)
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
return computer.output.pop()
|
||||||
27
2019/aoc2019/day06.py
Normal file
27
2019/aoc2019/day06.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
import networkx # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def read_graph(data: TextIO) -> networkx.DiGraph:
|
||||||
|
graph = networkx.DiGraph()
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
a, b = line.strip().split(')')
|
||||||
|
graph.add_edge(a, b)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
graph = read_graph(data)
|
||||||
|
|
||||||
|
paths = networkx.single_source_shortest_path_length(graph, 'COM')
|
||||||
|
|
||||||
|
return sum(paths.values())
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
graph = read_graph(data).to_undirected()
|
||||||
|
|
||||||
|
return networkx.shortest_path_length(graph, 'YOU', 'SAN') - 2
|
||||||
69
2019/aoc2019/day07.py
Normal file
69
2019/aoc2019/day07.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import itertools
|
||||||
|
from typing import List, TextIO, Tuple
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def amplify(phases: Tuple[int, ...], program: List[int]) -> int:
|
||||||
|
amps: List[Computer] = []
|
||||||
|
|
||||||
|
for i, phase in enumerate(phases):
|
||||||
|
amp = Computer(program.copy())
|
||||||
|
|
||||||
|
if i > 0:
|
||||||
|
amp.input = amps[i - 1].output
|
||||||
|
|
||||||
|
amp.input.append(phase)
|
||||||
|
|
||||||
|
amps.append(amp)
|
||||||
|
|
||||||
|
amps[0].input.append(0)
|
||||||
|
|
||||||
|
for amp in amps:
|
||||||
|
amp.run()
|
||||||
|
|
||||||
|
return amps[-1].output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def reamplify(phases: Tuple[int, ...], program: List[int]) -> int:
|
||||||
|
amps: List[Computer] = []
|
||||||
|
|
||||||
|
for i, _ in enumerate(phases):
|
||||||
|
amp = Computer(program.copy())
|
||||||
|
|
||||||
|
if i > 0:
|
||||||
|
amp.input = amps[i - 1].output
|
||||||
|
|
||||||
|
amps.append(amp)
|
||||||
|
|
||||||
|
amps[0].input = amps[-1].output
|
||||||
|
|
||||||
|
for amp, phase in zip(amps, phases):
|
||||||
|
amp.input.append(phase)
|
||||||
|
|
||||||
|
amps[0].input.append(0)
|
||||||
|
|
||||||
|
changes = True
|
||||||
|
|
||||||
|
while changes:
|
||||||
|
changes = False
|
||||||
|
for amp in amps:
|
||||||
|
try:
|
||||||
|
amp.run()
|
||||||
|
except IndexError:
|
||||||
|
# Waiting for input
|
||||||
|
changes = True
|
||||||
|
|
||||||
|
return amps[-1].output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
return max(amplify(phase, program) for phase in itertools.permutations(range(0, 5)))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
return max(reamplify(phase, program) for phase in itertools.permutations(range(5, 10)))
|
||||||
34
2019/aoc2019/day08.py
Normal file
34
2019/aoc2019/day08.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from collections import Counter
|
||||||
|
from typing import Iterable, TextIO
|
||||||
|
|
||||||
|
import numpy # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def parse_layers(width: int, height: int, data: TextIO) -> Iterable[numpy.array]:
|
||||||
|
chunk_size = width * height
|
||||||
|
|
||||||
|
content = next(data).strip()
|
||||||
|
|
||||||
|
for pos in range(0, len(content), chunk_size):
|
||||||
|
yield numpy.array([int(c) for c in content[pos:pos + chunk_size]])
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
best_layer: Counter[int] = min((Counter(layer) for layer in parse_layers(25, 6, data)), key=lambda c: c[0])
|
||||||
|
|
||||||
|
return best_layer[1] * best_layer[2]
|
||||||
|
|
||||||
|
|
||||||
|
def format_row(row: Iterable[int]) -> str:
|
||||||
|
return ''.join('#' if p == 1 else ' ' for p in row)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> str:
|
||||||
|
background = numpy.zeros(25 * 6, numpy.int8)
|
||||||
|
background.fill(2)
|
||||||
|
|
||||||
|
for layer in parse_layers(25, 6, data):
|
||||||
|
mask = background == 2
|
||||||
|
background[mask] = layer[mask]
|
||||||
|
|
||||||
|
return '\n'.join(format_row(row) for row in background.reshape(6, 25))
|
||||||
25
2019/aoc2019/day09.py
Normal file
25
2019/aoc2019/day09.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import sys
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def run_machine(data: TextIO, initializer: int) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
computer = Computer(program)
|
||||||
|
computer.input.append(initializer)
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
if len(computer.output) > 1:
|
||||||
|
sys.exit(computer.output)
|
||||||
|
else:
|
||||||
|
return computer.output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return run_machine(data, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
return run_machine(data, 2)
|
||||||
93
2019/aoc2019/day10.py
Normal file
93
2019/aoc2019/day10.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import math
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import TextIO, Tuple
|
||||||
|
|
||||||
|
import numpy # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def read_asteroids(data: TextIO) -> Tuple[numpy.array, numpy.array]:
|
||||||
|
xs = []
|
||||||
|
ys = []
|
||||||
|
for y, line in enumerate(data):
|
||||||
|
for x, c in enumerate(line):
|
||||||
|
if c == '#':
|
||||||
|
xs.append(x)
|
||||||
|
ys.append(y)
|
||||||
|
|
||||||
|
return numpy.array(xs), -numpy.array(ys)
|
||||||
|
|
||||||
|
|
||||||
|
def asteroids_visible(x: int, y: int, xs: numpy.array, ys: numpy.array) -> int:
|
||||||
|
dx = xs - x
|
||||||
|
dy = ys - y
|
||||||
|
|
||||||
|
div = numpy.abs(numpy.gcd(dx, dy))
|
||||||
|
|
||||||
|
mask = div != 0
|
||||||
|
|
||||||
|
dx[mask] //= div[mask]
|
||||||
|
dy[mask] //= div[mask]
|
||||||
|
|
||||||
|
unique = set(zip(dx, dy))
|
||||||
|
|
||||||
|
return len(unique) - 1 # need to ignore the point itself
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
xs, ys = read_asteroids(data)
|
||||||
|
|
||||||
|
return max(asteroids_visible(x, y, xs, ys) for x, y in zip(xs, ys))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
xs, ys = read_asteroids(data)
|
||||||
|
|
||||||
|
cx, cy = max(zip(xs, ys), key=lambda c: asteroids_visible(c[0], c[1], xs, ys))
|
||||||
|
|
||||||
|
dx = xs - cx
|
||||||
|
dy = ys - cy
|
||||||
|
|
||||||
|
angles = numpy.arctan2(dy, dx)
|
||||||
|
distances = numpy.abs(dx) + numpy.abs(dy)
|
||||||
|
|
||||||
|
to_shoot = defaultdict(list)
|
||||||
|
|
||||||
|
for angle, distance, dx, dy in zip(angles, distances, dx, dy):
|
||||||
|
if distance == 0:
|
||||||
|
# The point itself
|
||||||
|
continue
|
||||||
|
|
||||||
|
to_shoot[angle].append((distance, dx, dy))
|
||||||
|
|
||||||
|
for distances in to_shoot.values():
|
||||||
|
distances.sort(reverse=True)
|
||||||
|
|
||||||
|
unique_angles = sorted(set(angles), reverse=True)
|
||||||
|
|
||||||
|
shot = 0
|
||||||
|
|
||||||
|
# First shoot from up clockwise
|
||||||
|
for angle in unique_angles:
|
||||||
|
if angle > math.pi / 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
shot += 1
|
||||||
|
|
||||||
|
_, dx, dy = to_shoot[angle].pop()
|
||||||
|
|
||||||
|
# Repeatedly shoot until you reach #200
|
||||||
|
while True:
|
||||||
|
for angle in unique_angles:
|
||||||
|
if not to_shoot[angle]:
|
||||||
|
# Nothing left to shoot
|
||||||
|
continue
|
||||||
|
|
||||||
|
shot += 1
|
||||||
|
|
||||||
|
_, dx, dy = to_shoot[angle].pop()
|
||||||
|
|
||||||
|
if shot == 200:
|
||||||
|
x = cx + dx
|
||||||
|
y = -(cy + dy)
|
||||||
|
return 100 * x + y
|
||||||
|
|
||||||
60
2019/aoc2019/day11.py
Normal file
60
2019/aoc2019/day11.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from typing import Dict, Optional, TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import Computer, read_program
|
||||||
|
|
||||||
|
|
||||||
|
def run_robot(data: TextIO, painted: Optional[Dict[complex, int]] = None) -> Dict[complex, int]:
|
||||||
|
if painted is None:
|
||||||
|
painted = {}
|
||||||
|
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
pos = 0j
|
||||||
|
direction = 1j
|
||||||
|
|
||||||
|
finished = False
|
||||||
|
|
||||||
|
while not finished:
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
finished = True
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
while len(computer.output) >= 2:
|
||||||
|
paint = computer.output.popleft()
|
||||||
|
turn = computer.output.popleft()
|
||||||
|
|
||||||
|
painted[pos] = paint
|
||||||
|
|
||||||
|
if turn:
|
||||||
|
direction *= -1j
|
||||||
|
else:
|
||||||
|
direction *= 1j
|
||||||
|
|
||||||
|
pos += direction
|
||||||
|
|
||||||
|
computer.input.append(painted.get(pos, 0))
|
||||||
|
|
||||||
|
return painted
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return len(run_robot(data))
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> str:
|
||||||
|
painted = run_robot(data, {0j: 1})
|
||||||
|
|
||||||
|
xmin = int(min(pos.real for pos in painted.keys()))
|
||||||
|
xmax = int(max(pos.real for pos in painted.keys()))
|
||||||
|
ymin = int(min(pos.imag for pos in painted.keys()))
|
||||||
|
ymax = int(max(pos.imag for pos in painted.keys()))
|
||||||
|
|
||||||
|
image = ''
|
||||||
|
|
||||||
|
for y in reversed(range(ymin, ymax + 1)):
|
||||||
|
line = ''.join('#' if painted.get(x + y * 1j) == 1 else ' ' for x in range(xmin, xmax + 1))
|
||||||
|
image += f'{line}\n'
|
||||||
|
|
||||||
|
return image[:-1]
|
||||||
69
2019/aoc2019/day12.py
Normal file
69
2019/aoc2019/day12.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import itertools
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
import numpy # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def read_moons(data: TextIO) -> numpy.array:
|
||||||
|
moons = []
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
moon = [int(x) for x in re.split(r"[^-0-9]+", line) if x]
|
||||||
|
moons.append(moon)
|
||||||
|
|
||||||
|
return numpy.array(moons)
|
||||||
|
|
||||||
|
|
||||||
|
def advance(pos: numpy.array, vel: numpy.array) -> None:
|
||||||
|
""" update pos and vel in place """
|
||||||
|
pos_prime = numpy.repeat(numpy.reshape(pos, (1, len(pos))), len(pos), axis=0).transpose() - pos
|
||||||
|
pos_prime = -numpy.sign(pos_prime)
|
||||||
|
vel += numpy.sum(pos_prime, axis=1)
|
||||||
|
pos += vel
|
||||||
|
|
||||||
|
|
||||||
|
def simulate_moons(moons: numpy.array, iterations: int) -> int:
|
||||||
|
moons = numpy.transpose(moons) # Transpose so we have rows of x, y, z
|
||||||
|
velocity = numpy.zeros_like(moons)
|
||||||
|
|
||||||
|
for _ in range(iterations):
|
||||||
|
for pos, vel in zip(moons, velocity):
|
||||||
|
advance(pos, vel)
|
||||||
|
|
||||||
|
potential = numpy.sum(numpy.abs(moons), axis=0)
|
||||||
|
kinetic = numpy.sum(numpy.abs(velocity), axis=0)
|
||||||
|
|
||||||
|
return int(numpy.sum(kinetic * potential))
|
||||||
|
|
||||||
|
|
||||||
|
def find_repetition(moons: numpy.array) -> int:
|
||||||
|
moons = numpy.transpose(moons)
|
||||||
|
velocity = numpy.zeros_like(moons)
|
||||||
|
|
||||||
|
needed = 1
|
||||||
|
|
||||||
|
for pos, vel in zip(moons, velocity):
|
||||||
|
pos_prime = numpy.copy(pos)
|
||||||
|
|
||||||
|
for i in itertools.count(1):
|
||||||
|
advance(pos, vel)
|
||||||
|
|
||||||
|
if (pos == pos_prime).all() and (vel == 0).all():
|
||||||
|
needed *= i // math.gcd(needed, i)
|
||||||
|
break
|
||||||
|
|
||||||
|
return needed
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
moons = read_moons(data)
|
||||||
|
|
||||||
|
return simulate_moons(moons, 1000)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
moons = read_moons(data)
|
||||||
|
|
||||||
|
return find_repetition(moons)
|
||||||
57
2019/aoc2019/day13.py
Normal file
57
2019/aoc2019/day13.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import statistics
|
||||||
|
from typing import TextIO, Tuple, Dict
|
||||||
|
|
||||||
|
from aoc2019.intcode import Computer, read_program
|
||||||
|
|
||||||
|
|
||||||
|
def render_screen(computer: Computer, screen: Dict[Tuple[int, int], int]):
|
||||||
|
while computer.output:
|
||||||
|
x = computer.output.popleft()
|
||||||
|
y = computer.output.popleft()
|
||||||
|
val = computer.output.popleft()
|
||||||
|
|
||||||
|
screen[x, y] = val
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
screen: Dict[Tuple[int, int], int] = {}
|
||||||
|
|
||||||
|
render_screen(computer, screen)
|
||||||
|
|
||||||
|
return sum(1 for val in screen.values() if val == 2)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
computer.program[0] = 2
|
||||||
|
|
||||||
|
screen: Dict[Tuple[int, int], int] = {}
|
||||||
|
|
||||||
|
finished = False
|
||||||
|
|
||||||
|
while not finished:
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
finished = True
|
||||||
|
except IndexError:
|
||||||
|
# Waiting for input
|
||||||
|
pass
|
||||||
|
|
||||||
|
render_screen(computer, screen)
|
||||||
|
|
||||||
|
ball_x = next(x for x, y in screen if screen[x, y] == 4)
|
||||||
|
|
||||||
|
paddle_x = statistics.mean(x for x, y in screen if screen[x, y] == 3)
|
||||||
|
|
||||||
|
if ball_x < paddle_x:
|
||||||
|
computer.input.append(-1)
|
||||||
|
elif ball_x > paddle_x:
|
||||||
|
computer.input.append(1)
|
||||||
|
else:
|
||||||
|
computer.input.append(0)
|
||||||
|
|
||||||
|
return screen[-1, 0]
|
||||||
71
2019/aoc2019/day14.py
Normal file
71
2019/aoc2019/day14.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import math
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import TextIO, Tuple
|
||||||
|
|
||||||
|
from networkx import DiGraph, topological_sort # type: ignore[import]
|
||||||
|
|
||||||
|
|
||||||
|
def read_pair(item: str) -> Tuple[str, int]:
|
||||||
|
amount, element = item.split(' ')
|
||||||
|
|
||||||
|
return element, int(amount)
|
||||||
|
|
||||||
|
|
||||||
|
def read_recipes(data: TextIO) -> DiGraph:
|
||||||
|
graph = DiGraph()
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
requisites, production = line.strip().split(' => ')
|
||||||
|
|
||||||
|
produced, produced_amount = read_pair(production)
|
||||||
|
graph.add_node(produced, weight=produced_amount)
|
||||||
|
|
||||||
|
for requisite in requisites.split(', '):
|
||||||
|
required, required_amount = read_pair(requisite)
|
||||||
|
graph.add_edge(produced, required, weight=required_amount)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
def ore_required(graph: DiGraph, fuel_required: int) -> int:
|
||||||
|
requirements = defaultdict(int)
|
||||||
|
requirements['FUEL'] = fuel_required
|
||||||
|
|
||||||
|
for element in topological_sort(graph):
|
||||||
|
if element not in requirements:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if element == 'ORE':
|
||||||
|
break
|
||||||
|
|
||||||
|
element_produced = graph.nodes[element]['weight']
|
||||||
|
productions_required = math.ceil(requirements[element] / element_produced)
|
||||||
|
|
||||||
|
for _, elem_required, amount_required in graph.edges(element, data='weight'):
|
||||||
|
requirements[elem_required] += amount_required * productions_required
|
||||||
|
|
||||||
|
return requirements['ORE']
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return ore_required(read_recipes(data), 1)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
ore_available = 1000000000000
|
||||||
|
graph = read_recipes(data)
|
||||||
|
|
||||||
|
min_possible = 1 # lower bound of ORE / ore_required(graph, 1) exists but is slower
|
||||||
|
max_possible = ore_available
|
||||||
|
|
||||||
|
while min_possible != max_possible:
|
||||||
|
check = min_possible + (max_possible - min_possible + 1) // 2
|
||||||
|
|
||||||
|
required = ore_required(graph, check)
|
||||||
|
|
||||||
|
if required <= ore_available:
|
||||||
|
min_possible = check
|
||||||
|
else:
|
||||||
|
max_possible = check - 1
|
||||||
|
|
||||||
|
return min_possible
|
||||||
102
2019/aoc2019/day15.py
Normal file
102
2019/aoc2019/day15.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
import networkx # type: ignore
|
||||||
|
|
||||||
|
from aoc2019.intcode import Computer, read_program
|
||||||
|
|
||||||
|
|
||||||
|
def step(computer: Computer, direction: int) -> int:
|
||||||
|
computer.input.append(direction)
|
||||||
|
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
except IndexError:
|
||||||
|
return computer.get_output()
|
||||||
|
|
||||||
|
sys.exit("computer terminated unexpectedly")
|
||||||
|
|
||||||
|
|
||||||
|
def inverse(direction: int):
|
||||||
|
if direction % 2 == 1:
|
||||||
|
return direction + 1
|
||||||
|
else:
|
||||||
|
return direction - 1
|
||||||
|
|
||||||
|
|
||||||
|
def read_graph(data: TextIO) -> networkx.Graph:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
pos = (0, 0)
|
||||||
|
tiles = defaultdict(int)
|
||||||
|
tiles[0, 0] = 1
|
||||||
|
|
||||||
|
prev = [((0, 0), 1)]
|
||||||
|
|
||||||
|
while prev:
|
||||||
|
x, y = pos
|
||||||
|
|
||||||
|
if (x, y + 1) not in tiles:
|
||||||
|
movement = 1
|
||||||
|
next_pos = (x, y + 1)
|
||||||
|
elif (x, y - 1) not in tiles:
|
||||||
|
movement = 2
|
||||||
|
next_pos = (x, y - 1)
|
||||||
|
elif (x - 1, y) not in tiles:
|
||||||
|
movement = 3
|
||||||
|
next_pos = (x - 1, y)
|
||||||
|
elif (x + 1, y) not in tiles:
|
||||||
|
movement = 4
|
||||||
|
next_pos = (x + 1, y)
|
||||||
|
else:
|
||||||
|
# No movement available, backtrack
|
||||||
|
prev_pos, prev_dir = prev.pop()
|
||||||
|
step(computer, inverse(prev_dir))
|
||||||
|
pos = prev_pos
|
||||||
|
continue
|
||||||
|
|
||||||
|
result = step(computer, movement)
|
||||||
|
tiles[next_pos] = result
|
||||||
|
|
||||||
|
if result != 0:
|
||||||
|
# Movement was successful
|
||||||
|
prev.append((pos, movement))
|
||||||
|
pos = next_pos
|
||||||
|
|
||||||
|
graph = networkx.Graph()
|
||||||
|
|
||||||
|
for pos, value in tiles.items():
|
||||||
|
if value == 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if value == 2:
|
||||||
|
# Create an imaginary edge to the oxygen
|
||||||
|
graph.add_edge('O', pos, weight=0)
|
||||||
|
|
||||||
|
x, y = pos
|
||||||
|
|
||||||
|
neighbours = [
|
||||||
|
(x - 1, y),
|
||||||
|
(x + 1, y),
|
||||||
|
(x, y - 1),
|
||||||
|
(x, y + 1),
|
||||||
|
]
|
||||||
|
|
||||||
|
for neighbour in neighbours:
|
||||||
|
if tiles[neighbour] != 0:
|
||||||
|
graph.add_edge(pos, neighbour)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
graph = read_graph(data)
|
||||||
|
|
||||||
|
return networkx.shortest_path_length(graph, (0, 0), 'O') - 1
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
graph = read_graph(data)
|
||||||
|
|
||||||
|
return networkx.eccentricity(graph, 'O') - 1
|
||||||
57
2019/aoc2019/day16.py
Normal file
57
2019/aoc2019/day16.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import math
|
||||||
|
from typing import List, TextIO
|
||||||
|
|
||||||
|
import numpy # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
def read_input(data: TextIO) -> List[int]:
|
||||||
|
line = next(data).strip()
|
||||||
|
|
||||||
|
return [int(c) for c in line]
|
||||||
|
|
||||||
|
|
||||||
|
def simulate(numbers: List[int]) -> str:
|
||||||
|
numbers = numpy.array(numbers)
|
||||||
|
pattern = numpy.array([0, 1, 0, -1], dtype=numpy.int32)
|
||||||
|
|
||||||
|
matrix = numpy.zeros((len(numbers), len(numbers)), dtype=numpy.int32)
|
||||||
|
|
||||||
|
for i in range(len(numbers)):
|
||||||
|
base = numpy.repeat(pattern, i + 1)
|
||||||
|
needed_repetitions = math.ceil((len(numbers) + 1) / len(base))
|
||||||
|
matrix[i, :] = numpy.tile(base, needed_repetitions)[1:len(numbers) + 1]
|
||||||
|
|
||||||
|
for _ in range(100):
|
||||||
|
numbers = numpy.abs(numpy.dot(matrix, numbers)) % 10
|
||||||
|
|
||||||
|
return ''.join(str(s) for s in numbers[:8])
|
||||||
|
|
||||||
|
|
||||||
|
def simulate2(numbers: List[int]) -> str:
|
||||||
|
numbers = numpy.tile(numpy.array(numbers, dtype=numpy.int32), 10000)
|
||||||
|
starting_index = 0
|
||||||
|
|
||||||
|
for n in numbers[:7]:
|
||||||
|
starting_index *= 10
|
||||||
|
starting_index += n
|
||||||
|
|
||||||
|
assert starting_index > len(numbers) / 2
|
||||||
|
|
||||||
|
numbers = numbers[starting_index:]
|
||||||
|
|
||||||
|
for _ in range(100):
|
||||||
|
numbers = numpy.abs(numpy.flip(numpy.cumsum(numpy.flip(numbers)))) % 10
|
||||||
|
|
||||||
|
return ''.join(str(s) for s in numbers[:8])
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> str:
|
||||||
|
numbers = read_input(data)
|
||||||
|
|
||||||
|
return simulate(numbers)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> str:
|
||||||
|
numbers = read_input(data)
|
||||||
|
|
||||||
|
return simulate2(numbers)
|
||||||
26
2019/aoc2019/day17.py
Normal file
26
2019/aoc2019/day17.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import Computer, read_program
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
output = ''.join(chr(c) for c in computer.output)
|
||||||
|
|
||||||
|
tiles = set()
|
||||||
|
|
||||||
|
for y, line in enumerate(output.splitlines()):
|
||||||
|
for x, c in enumerate(line):
|
||||||
|
if c == '#':
|
||||||
|
tiles.add((x, y))
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
for x, y in tiles:
|
||||||
|
if (x - 1, y) in tiles and (x + 1, y) in tiles and (x, y - 1) in tiles and (x, y + 1) in tiles:
|
||||||
|
total += x * y
|
||||||
|
|
||||||
|
return total
|
||||||
66
2019/aoc2019/day19.py
Normal file
66
2019/aoc2019/day19.py
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import copy
|
||||||
|
import itertools
|
||||||
|
from collections import deque
|
||||||
|
from typing import TextIO, Tuple
|
||||||
|
|
||||||
|
from aoc2019.intcode import Computer, read_program
|
||||||
|
|
||||||
|
|
||||||
|
def query_position(x: int, y: int, computer: Computer) -> bool:
|
||||||
|
computer = copy.deepcopy(computer)
|
||||||
|
|
||||||
|
computer.send_input(x)
|
||||||
|
computer.send_input(y)
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
return computer.get_output() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def find_line(y: int, x_min: int, x_max: int, computer: Computer) -> Tuple[int, int]:
|
||||||
|
# First find start of the line:
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
while not query_position(x_min, y, computer):
|
||||||
|
offset += 1
|
||||||
|
x_min += 1
|
||||||
|
|
||||||
|
x_max += offset
|
||||||
|
while query_position(x_max, y, computer):
|
||||||
|
x_max += 1
|
||||||
|
|
||||||
|
x_max -= 1
|
||||||
|
|
||||||
|
return x_min, x_max
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
x_min, x_max = (0, 0)
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
for y in range(50):
|
||||||
|
x_min, x_max = find_line(y, x_min, x_max, computer)
|
||||||
|
total += min(x_max, 49) - min(x_min, 50) + 1
|
||||||
|
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
x_min, x_max = (0, 0)
|
||||||
|
|
||||||
|
lines = deque()
|
||||||
|
|
||||||
|
for y in itertools.count():
|
||||||
|
x_min, x_max = find_line(y, x_min, x_max, computer)
|
||||||
|
lines.append((x_min, x_max))
|
||||||
|
|
||||||
|
if len(lines) == 100:
|
||||||
|
x_top_min, x_top_max = lines.popleft()
|
||||||
|
|
||||||
|
if x_top_max - x_min + 1 < 100:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return x_min * 10000 + y - 99
|
||||||
44
2019/aoc2019/day21.py
Normal file
44
2019/aoc2019/day21.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def send_input(computer: Computer, program: str) -> None:
|
||||||
|
for c in program:
|
||||||
|
computer.send_input(ord(c))
|
||||||
|
|
||||||
|
|
||||||
|
def run(data: TextIO, program: str) -> int:
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
|
||||||
|
send_input(computer, program)
|
||||||
|
computer.run()
|
||||||
|
|
||||||
|
return computer.output.pop()
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
program = """\
|
||||||
|
OR A J
|
||||||
|
AND B J
|
||||||
|
AND C J
|
||||||
|
NOT J J
|
||||||
|
AND D J
|
||||||
|
WALK
|
||||||
|
"""
|
||||||
|
|
||||||
|
return run(data, program)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
program = """\
|
||||||
|
NOT H J
|
||||||
|
OR C J
|
||||||
|
AND A J
|
||||||
|
AND B J
|
||||||
|
NOT J J
|
||||||
|
AND D J
|
||||||
|
RUN
|
||||||
|
"""
|
||||||
|
|
||||||
|
return run(data, program)
|
||||||
90
2019/aoc2019/day22.py
Normal file
90
2019/aoc2019/day22.py
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
from typing import List, TextIO
|
||||||
|
|
||||||
|
|
||||||
|
def shuffle(instructions: List[str], deck_size: int) -> List[int]:
|
||||||
|
deck = list(range(0, deck_size))
|
||||||
|
|
||||||
|
for instruction in instructions:
|
||||||
|
if "new stack" in instruction:
|
||||||
|
deck = list(reversed(deck))
|
||||||
|
continue
|
||||||
|
|
||||||
|
parts = instruction.split(" ")
|
||||||
|
if parts[0] == "cut":
|
||||||
|
by = int(parts[1])
|
||||||
|
|
||||||
|
new_deck = deck[by:]
|
||||||
|
new_deck += deck[:by]
|
||||||
|
|
||||||
|
deck = new_deck
|
||||||
|
else:
|
||||||
|
increment = int(parts[3])
|
||||||
|
|
||||||
|
new_deck = list(range(0, deck_size))
|
||||||
|
target_index = 0
|
||||||
|
|
||||||
|
for card in deck:
|
||||||
|
new_deck[target_index] = card
|
||||||
|
target_index = (target_index + increment) % len(deck)
|
||||||
|
|
||||||
|
deck = new_deck
|
||||||
|
|
||||||
|
return deck
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
instructions = [line.strip() for line in data]
|
||||||
|
|
||||||
|
result = shuffle(instructions, 10007)
|
||||||
|
|
||||||
|
for i, card in enumerate(result):
|
||||||
|
if card == 2019:
|
||||||
|
return i
|
||||||
|
|
||||||
|
raise Exception("Did not find card")
|
||||||
|
|
||||||
|
|
||||||
|
def modpow(a: int, b: int, m: int) -> int:
|
||||||
|
assert b >= 0
|
||||||
|
|
||||||
|
result = 1
|
||||||
|
n = a
|
||||||
|
|
||||||
|
while b > 0:
|
||||||
|
if b % 2:
|
||||||
|
result = (result * n) % m
|
||||||
|
|
||||||
|
b //= 2
|
||||||
|
n = (n * n) % m
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def inverse(a: int, m: int) -> int:
|
||||||
|
""" Computes the modulo multiplicative inverse """
|
||||||
|
return modpow(a, m - 2, m)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
deck_size = 119315717514047
|
||||||
|
shuffles = 101741582076661
|
||||||
|
|
||||||
|
a, b = 1, 0
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
parts = line.split(' ')
|
||||||
|
if 'new stack' in line:
|
||||||
|
la, lb = -1, -1
|
||||||
|
elif parts[0] == 'deal':
|
||||||
|
la, lb = int(parts[-1]), 0
|
||||||
|
else:
|
||||||
|
la, lb = 1, -int(parts[-1])
|
||||||
|
|
||||||
|
a = (la * a) % deck_size
|
||||||
|
b = (la * b + lb) % deck_size
|
||||||
|
|
||||||
|
final_a = modpow(a, shuffles, deck_size)
|
||||||
|
final_b = ((b * (final_a - 1)) * inverse(a - 1, deck_size)) % deck_size
|
||||||
|
|
||||||
|
return ((2020 - final_b) * inverse(final_a, deck_size)) % deck_size
|
||||||
|
|
||||||
78
2019/aoc2019/day23.py
Normal file
78
2019/aoc2019/day23.py
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
from typing import TextIO
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
computers = [Computer(program.copy()) for _ in range(50)]
|
||||||
|
|
||||||
|
for i, computer in enumerate(computers):
|
||||||
|
computer.send_input(i)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for computer in computers:
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
except IndexError:
|
||||||
|
computer.send_input(-1)
|
||||||
|
|
||||||
|
while len(computer.output) >= 3:
|
||||||
|
dest = computer.get_output()
|
||||||
|
x = computer.get_output()
|
||||||
|
y = computer.get_output()
|
||||||
|
|
||||||
|
if dest == 255:
|
||||||
|
return y
|
||||||
|
|
||||||
|
computers[dest].send_input(x)
|
||||||
|
computers[dest].send_input(y)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
program = read_program(data)
|
||||||
|
|
||||||
|
computers = [Computer(program.copy()) for _ in range(50)]
|
||||||
|
|
||||||
|
for i, computer in enumerate(computers):
|
||||||
|
computer.send_input(i)
|
||||||
|
|
||||||
|
nat_value = None
|
||||||
|
last_sent = None
|
||||||
|
|
||||||
|
while True:
|
||||||
|
is_idle = True
|
||||||
|
|
||||||
|
for computer in computers:
|
||||||
|
try:
|
||||||
|
computer.execute_current()
|
||||||
|
is_idle = False
|
||||||
|
except IndexError:
|
||||||
|
computer.send_input(-1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
while len(computer.output) >= 3:
|
||||||
|
dest = computer.get_output()
|
||||||
|
x = computer.get_output()
|
||||||
|
y = computer.get_output()
|
||||||
|
|
||||||
|
if dest == 255:
|
||||||
|
nat_value = (x, y)
|
||||||
|
else:
|
||||||
|
computers[dest].send_input(x)
|
||||||
|
computers[dest].send_input(y)
|
||||||
|
|
||||||
|
if is_idle:
|
||||||
|
x, y = nat_value
|
||||||
|
|
||||||
|
if last_sent == nat_value:
|
||||||
|
return y
|
||||||
|
else:
|
||||||
|
computers[0].send_input(x)
|
||||||
|
computers[0].send_input(y)
|
||||||
|
last_sent = nat_value
|
||||||
138
2019/aoc2019/day24.py
Normal file
138
2019/aoc2019/day24.py
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
from typing import TextIO, Iterable, Tuple, List
|
||||||
|
|
||||||
|
|
||||||
|
def read_board(data: TextIO) -> Tuple[Tuple[bool]]:
|
||||||
|
return tuple(
|
||||||
|
tuple(c == '#' for c in line.strip())
|
||||||
|
for line in data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def flatten(it: Iterable[Iterable]) -> Iterable:
|
||||||
|
for item in it:
|
||||||
|
yield from item
|
||||||
|
|
||||||
|
|
||||||
|
def neighbours(board: Tuple[Tuple[bool]], x: int, y: int) -> int:
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
if x > 0 and board[y][x - 1]:
|
||||||
|
n += 1
|
||||||
|
|
||||||
|
if x + 1 < len(board[0]) and board[y][x + 1]:
|
||||||
|
n += 1
|
||||||
|
|
||||||
|
if y > 0 and board[y - 1][x]:
|
||||||
|
n += 1
|
||||||
|
|
||||||
|
if y + 1 < len(board) and board[y + 1][x]:
|
||||||
|
n += 1
|
||||||
|
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def advance_board(board: Tuple[Tuple[bool]]) -> Tuple[Tuple[bool]]:
|
||||||
|
def create_row(y: int, row: Tuple[bool]):
|
||||||
|
new_row = []
|
||||||
|
for x, live in enumerate(row):
|
||||||
|
if live:
|
||||||
|
new_row.append(neighbours(board, x, y) == 1)
|
||||||
|
else:
|
||||||
|
new_row.append(neighbours(board, x, y) in [1, 2])
|
||||||
|
|
||||||
|
return tuple(new_row)
|
||||||
|
|
||||||
|
return tuple(create_row(y, row) for y, row in enumerate(board))
|
||||||
|
|
||||||
|
|
||||||
|
def neighbours2(board: List[Tuple[Tuple[bool]]], x: int, y: int, z: int) -> int:
|
||||||
|
existing = range(len(board))
|
||||||
|
|
||||||
|
if z in existing:
|
||||||
|
# Normal board count, minus the middle tile if applicable
|
||||||
|
n = neighbours(board[z], x, y) - board[z][2][2]
|
||||||
|
else:
|
||||||
|
n = 0
|
||||||
|
|
||||||
|
if z - 1 in existing:
|
||||||
|
if y == 2:
|
||||||
|
if x == 1:
|
||||||
|
n += sum(board[z - 1][iy][0] for iy in range(5))
|
||||||
|
elif x == 3:
|
||||||
|
n += sum(board[z - 1][iy][4] for iy in range(5))
|
||||||
|
elif x == 2:
|
||||||
|
if y == 1:
|
||||||
|
n += sum(board[z - 1][0])
|
||||||
|
elif y == 3:
|
||||||
|
n += sum(board[z - 1][4])
|
||||||
|
|
||||||
|
if z + 1 in existing:
|
||||||
|
if y == 0:
|
||||||
|
n += board[z + 1][1][2]
|
||||||
|
elif y == 4:
|
||||||
|
n += board[z + 1][3][2]
|
||||||
|
|
||||||
|
if x == 0:
|
||||||
|
n += board[z + 1][2][1]
|
||||||
|
elif x == 4:
|
||||||
|
n += board[z + 1][2][3]
|
||||||
|
|
||||||
|
return n
|
||||||
|
|
||||||
|
|
||||||
|
def advance_board2(board: List[Tuple[Tuple[bool]]]) -> List[Tuple[Tuple[bool]]]:
|
||||||
|
layers = []
|
||||||
|
|
||||||
|
for z in range(-1, len(board) + 1):
|
||||||
|
layer = []
|
||||||
|
|
||||||
|
for y in range(5):
|
||||||
|
row = []
|
||||||
|
|
||||||
|
for x in range(5):
|
||||||
|
if y == 2 and x == 2:
|
||||||
|
row.append(False)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if z in range(len(board)):
|
||||||
|
live = board[z][y][x]
|
||||||
|
else:
|
||||||
|
live = False
|
||||||
|
|
||||||
|
if live:
|
||||||
|
row.append(neighbours2(board, x, y, z) == 1)
|
||||||
|
else:
|
||||||
|
row.append(neighbours2(board, x, y, z) in [1, 2])
|
||||||
|
|
||||||
|
layer.append(tuple(row))
|
||||||
|
|
||||||
|
layers.append(tuple(layer))
|
||||||
|
|
||||||
|
if sum(flatten(layers[0])) == 0:
|
||||||
|
layers = layers[1:]
|
||||||
|
if sum(flatten(layers[-1])) == 0:
|
||||||
|
layers = layers[:-1]
|
||||||
|
|
||||||
|
return layers
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
board = read_board(data)
|
||||||
|
|
||||||
|
seen = set(board)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
board = advance_board(board)
|
||||||
|
if board in seen:
|
||||||
|
return sum(2 ** i for i, b in enumerate(flatten(board)) if b)
|
||||||
|
|
||||||
|
seen.add(board)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO, rounds: int = 200) -> int:
|
||||||
|
board = [read_board(data)]
|
||||||
|
|
||||||
|
for _ in range(rounds):
|
||||||
|
board = advance_board2(board)
|
||||||
|
|
||||||
|
return sum(flatten(flatten(board)))
|
||||||
123
2019/aoc2019/day25.py
Normal file
123
2019/aoc2019/day25.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import itertools
|
||||||
|
from typing import TextIO, Iterable, Tuple, Set
|
||||||
|
|
||||||
|
from aoc2019.intcode import read_program, Computer
|
||||||
|
|
||||||
|
|
||||||
|
def powerset(iterable: Iterable) -> Iterable[Tuple]:
|
||||||
|
s = list(iterable)
|
||||||
|
return itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s) + 1))
|
||||||
|
|
||||||
|
|
||||||
|
def print_output(computer: Computer) -> str:
|
||||||
|
output = ""
|
||||||
|
|
||||||
|
while len(computer.output):
|
||||||
|
output += chr(computer.get_output())
|
||||||
|
|
||||||
|
print(output, end='')
|
||||||
|
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
def load_save(save, computer: Computer):
|
||||||
|
computer.pointer, computer.relative_base, computer.program = save
|
||||||
|
|
||||||
|
|
||||||
|
def create_save(computer: Computer):
|
||||||
|
return computer.pointer, computer.relative_base, computer.program.copy()
|
||||||
|
|
||||||
|
|
||||||
|
def send_command(computer: Computer, command: str):
|
||||||
|
for c in command:
|
||||||
|
computer.send_input(ord(c))
|
||||||
|
|
||||||
|
computer.send_input(10) # manually send newline
|
||||||
|
|
||||||
|
|
||||||
|
def force(computer: Computer, direction: str, items: Set[str]):
|
||||||
|
# First, drop everything
|
||||||
|
for item in items:
|
||||||
|
send_command(computer, f"drop {item}")
|
||||||
|
|
||||||
|
holding = tuple()
|
||||||
|
|
||||||
|
for combination in powerset(items):
|
||||||
|
# drop everything we don't want to keep holding
|
||||||
|
for item in holding:
|
||||||
|
if item not in combination:
|
||||||
|
send_command(computer, f"drop {item}")
|
||||||
|
|
||||||
|
# pick up what we want to pick up
|
||||||
|
for item in combination:
|
||||||
|
if item not in holding:
|
||||||
|
send_command(computer, f"take {item}")
|
||||||
|
|
||||||
|
send_command(computer, direction)
|
||||||
|
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
print_output(computer)
|
||||||
|
return True
|
||||||
|
except IndexError:
|
||||||
|
print_output(computer)
|
||||||
|
holding = combination
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO):
|
||||||
|
print("This day must use a file as input as it requires the stdin for other things.")
|
||||||
|
|
||||||
|
computer = Computer(read_program(data))
|
||||||
|
items = set()
|
||||||
|
|
||||||
|
saves = {}
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
computer.run()
|
||||||
|
print_output(computer)
|
||||||
|
|
||||||
|
print("detected a death, loading auto save...")
|
||||||
|
load_save(saves['auto'], computer)
|
||||||
|
print("Command?")
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
print_output(computer)
|
||||||
|
|
||||||
|
saves['auto'] = create_save(computer)
|
||||||
|
|
||||||
|
try:
|
||||||
|
command = input().strip()
|
||||||
|
except EOFError:
|
||||||
|
return "exiting"
|
||||||
|
|
||||||
|
if command == "exit":
|
||||||
|
return "exiting"
|
||||||
|
elif command == "items":
|
||||||
|
print(items)
|
||||||
|
continue
|
||||||
|
elif command.startswith("save "):
|
||||||
|
save_name = command.removeprefix("save ")
|
||||||
|
saves[save_name] = create_save(computer)
|
||||||
|
print(f"Saved game state as {save_name}")
|
||||||
|
continue
|
||||||
|
elif command.startswith("load "):
|
||||||
|
save_name = command.removeprefix("load ")
|
||||||
|
load_save(saves[save_name], computer)
|
||||||
|
print(f"Loaded game state from {save_name}")
|
||||||
|
continue
|
||||||
|
elif command.startswith("take "):
|
||||||
|
items.add(command.removeprefix("take "))
|
||||||
|
elif command.startswith("drop "):
|
||||||
|
items.remove(command.removeprefix("drop "))
|
||||||
|
elif command.startswith("force "):
|
||||||
|
direction = command.removeprefix("force ")
|
||||||
|
if force(computer, direction, items):
|
||||||
|
return "success"
|
||||||
|
continue
|
||||||
|
|
||||||
|
send_command(computer, command)
|
||||||
|
|
||||||
150
2019/aoc2019/intcode.py
Normal file
150
2019/aoc2019/intcode.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import collections
|
||||||
|
from typing import List, TextIO, Tuple, Union
|
||||||
|
|
||||||
|
|
||||||
|
def read_program(data: TextIO) -> List[int]:
|
||||||
|
line = next(data)
|
||||||
|
|
||||||
|
return [int(i) for i in line.split(',')]
|
||||||
|
|
||||||
|
|
||||||
|
class Computer:
|
||||||
|
program: List[int]
|
||||||
|
pointer: int
|
||||||
|
relative_base: int
|
||||||
|
input: collections.deque[int]
|
||||||
|
output: collections.deque[int]
|
||||||
|
|
||||||
|
def __init__(self, program: List[int], pointer: int = 0) -> None:
|
||||||
|
self.program = program
|
||||||
|
self.pointer = pointer
|
||||||
|
self.relative_base = 0
|
||||||
|
self.input = collections.deque()
|
||||||
|
self.output = collections.deque()
|
||||||
|
|
||||||
|
def _mode_and_key(self, item: Union[int, Tuple[int, int]]) -> Tuple[int, int]:
|
||||||
|
if isinstance(item, int):
|
||||||
|
return 0, item
|
||||||
|
else:
|
||||||
|
mode, key = item
|
||||||
|
return mode, self.program[self.pointer + key]
|
||||||
|
|
||||||
|
def __getitem__(self, item: Union[int, Tuple[int, int]]) -> int:
|
||||||
|
mode, key = self._mode_and_key(item)
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
return key
|
||||||
|
elif mode == 0:
|
||||||
|
pass # Nothing to do here, handled below
|
||||||
|
elif mode == 2:
|
||||||
|
key += self.relative_base
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unsupported mode "{mode}"')
|
||||||
|
|
||||||
|
self._ensure_length(key + 1)
|
||||||
|
return self.program[key]
|
||||||
|
|
||||||
|
def __setitem__(self, item: Union[int, Tuple[int, int]], value: int) -> None:
|
||||||
|
mode, key = self._mode_and_key(item)
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
raise ValueError('Cannot assign to an immediate')
|
||||||
|
elif mode == 0:
|
||||||
|
pass # Nothing to do here, handled below
|
||||||
|
elif mode == 2:
|
||||||
|
key += self.relative_base
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unsupported mode "{mode}"')
|
||||||
|
|
||||||
|
self._ensure_length(key + 1)
|
||||||
|
self.program[key] = value
|
||||||
|
|
||||||
|
def _ensure_length(self, length: int) -> None:
|
||||||
|
if len(self.program) < length:
|
||||||
|
if 2 * len(self.program) >= length:
|
||||||
|
# Double current program size with 0s
|
||||||
|
self.program.extend(0 for _ in range(len(self.program)))
|
||||||
|
else:
|
||||||
|
# Resize until the desired length
|
||||||
|
self.program.extend(0 for _ in range(length - len(self.program)))
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
""" Run until failure """
|
||||||
|
while self.execute_current():
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_output(self) -> int:
|
||||||
|
return self.output.popleft()
|
||||||
|
|
||||||
|
def send_input(self, data: int):
|
||||||
|
self.input.append(data)
|
||||||
|
|
||||||
|
def execute_current(self) -> bool:
|
||||||
|
"""
|
||||||
|
Execute a single instruction
|
||||||
|
:return: True if the program should continue
|
||||||
|
"""
|
||||||
|
pointer = self.pointer
|
||||||
|
instruction = self[pointer]
|
||||||
|
opcode = instruction % 100
|
||||||
|
|
||||||
|
mode = [
|
||||||
|
(instruction // 100) % 10,
|
||||||
|
(instruction // 1000) % 10,
|
||||||
|
(instruction // 10000) % 10,
|
||||||
|
]
|
||||||
|
|
||||||
|
if opcode == 1:
|
||||||
|
# Add
|
||||||
|
self[mode[2], 3] = self[mode[0], 1] + self[mode[1], 2]
|
||||||
|
self.pointer += 4
|
||||||
|
elif opcode == 2:
|
||||||
|
# Multiply
|
||||||
|
self[mode[2], 3] = self[mode[0], 1] * self[mode[1], 2]
|
||||||
|
self.pointer += 4
|
||||||
|
elif opcode == 3:
|
||||||
|
# Input
|
||||||
|
self[mode[0], 1] = self.input.popleft()
|
||||||
|
self.pointer += 2
|
||||||
|
elif opcode == 4:
|
||||||
|
# Output
|
||||||
|
self.output.append(self[mode[0], 1])
|
||||||
|
self.pointer += 2
|
||||||
|
elif opcode == 5:
|
||||||
|
# Jump if true
|
||||||
|
if self[mode[0], 1] != 0:
|
||||||
|
self.pointer = self[mode[1], 2]
|
||||||
|
else:
|
||||||
|
self.pointer += 3
|
||||||
|
elif opcode == 6:
|
||||||
|
# Jump if false
|
||||||
|
if self[mode[0], 1] == 0:
|
||||||
|
self.pointer = self[mode[1], 2]
|
||||||
|
else:
|
||||||
|
self.pointer += 3
|
||||||
|
elif opcode == 7:
|
||||||
|
# Less than
|
||||||
|
if self[mode[0], 1] < self[mode[1], 2]:
|
||||||
|
self[mode[2], 3] = 1
|
||||||
|
else:
|
||||||
|
self[mode[2], 3] = 0
|
||||||
|
self.pointer += 4
|
||||||
|
elif opcode == 8:
|
||||||
|
# Equals
|
||||||
|
if self[mode[0], 1] == self[mode[1], 2]:
|
||||||
|
self[mode[2], 3] = 1
|
||||||
|
else:
|
||||||
|
self[mode[2], 3] = 0
|
||||||
|
self.pointer += 4
|
||||||
|
elif opcode == 9:
|
||||||
|
# Adjust relative base
|
||||||
|
self.relative_base += self[mode[0], 1]
|
||||||
|
self.pointer += 2
|
||||||
|
elif opcode == 99:
|
||||||
|
# Halt
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unknown opcode {opcode} at {pointer}')
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
1
2019/inputs/23.txt
Normal file
1
2019/inputs/23.txt
Normal file
File diff suppressed because one or more lines are too long
1
2019/inputs/25.txt
Normal file
1
2019/inputs/25.txt
Normal file
File diff suppressed because one or more lines are too long
3
2019/requirements.txt
Normal file
3
2019/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pytest
|
||||||
|
networkx
|
||||||
|
numpy
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
static inline int required(int weight) {
|
|
||||||
return weight / 3 - 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day01_part1(std::istream &input, std::ostream &output) {
|
|
||||||
int total = 0;
|
|
||||||
for (int current; input >> current;) {
|
|
||||||
total += required(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
output << total << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day01_part2(std::istream &input, std::ostream &output) {
|
|
||||||
int total = 0;
|
|
||||||
for (int current; input >> current;) {
|
|
||||||
for (int fuel = required(current); fuel > 0; fuel = required(fuel)) {
|
|
||||||
total += fuel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << total << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#include <exception>
|
|
||||||
#include <iostream>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
static int run_program(std::vector<std::int64_t> program) {
|
|
||||||
aoc2019::IntCodeComputer computer(std::move(program));
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
return computer[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day02_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto program = IntCodeComputer::read_intcode(input);
|
|
||||||
program[1] = 12;
|
|
||||||
program[2] = 2;
|
|
||||||
output << run_program(std::move(program)) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day02_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto program = IntCodeComputer::read_intcode(input);
|
|
||||||
|
|
||||||
for (int noun = 0; noun < 100; ++noun) {
|
|
||||||
for (int verb = 0; verb < 100; ++verb) {
|
|
||||||
program[1] = noun;
|
|
||||||
program[2] = verb;
|
|
||||||
if (run_program(program) == 19690720) {
|
|
||||||
output << 100 * noun + verb << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw std::domain_error("No valid solution.");
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#include <cassert>
|
|
||||||
#include <charconv>
|
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
|
|
||||||
const std::unordered_map<char, point_t> DIRECTION_MAP = {
|
|
||||||
{'U', {0, -1}},
|
|
||||||
{'D', {0, 1}},
|
|
||||||
{'L', {-1, 0}},
|
|
||||||
{'R', {1, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<point_t, int> get_points(std::string_view line) {
|
|
||||||
std::unordered_map<point_t, int> points{};
|
|
||||||
point_t pos = {};
|
|
||||||
|
|
||||||
int steps = 0;
|
|
||||||
for (auto entry = aoc2019::strtok(line); !line.empty() || !entry.empty(); entry = aoc2019::strtok(line)) {
|
|
||||||
const auto dir = DIRECTION_MAP.at(entry[0]);
|
|
||||||
std::size_t amount = 0;
|
|
||||||
aoc2019::from_chars(entry.substr(1), amount);
|
|
||||||
assert(amount > 0 && "Must have some valid direction");
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < amount; ++i) {
|
|
||||||
++steps;
|
|
||||||
pos += dir;
|
|
||||||
if (!points.count(pos)) {
|
|
||||||
points[pos] = steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return points;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::unordered_map<point_t, int>, std::unordered_map<point_t, int>> read_input(std::istream& input) {
|
|
||||||
std::string buffer;
|
|
||||||
std::getline(input, buffer);
|
|
||||||
auto a = get_points(buffer);
|
|
||||||
std::getline(input, buffer);
|
|
||||||
auto b = get_points(buffer);
|
|
||||||
|
|
||||||
return { std::move(a), std::move(b) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day03_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto [a, b] = read_input(input);
|
|
||||||
|
|
||||||
int best = std::numeric_limits<int>::max();
|
|
||||||
|
|
||||||
for (const auto& point : a) {
|
|
||||||
if (b.count(point.first) && point.first.l1() < best) {
|
|
||||||
best = point.first.l1();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << best << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day03_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto [a, b] = read_input(input);
|
|
||||||
|
|
||||||
int best = std::numeric_limits<int>::max();
|
|
||||||
|
|
||||||
for (const auto& ap : a) {
|
|
||||||
const auto bp = b.find(ap.first);
|
|
||||||
|
|
||||||
if (bp != b.cend() && (ap.second + bp->second) < best) {
|
|
||||||
best = ap.second + bp->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << best << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <utility>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr bool is_valid_pass(int num) {
|
|
||||||
bool has_double = false;
|
|
||||||
int prev = 11;
|
|
||||||
|
|
||||||
for (; num != 0; num /= 10) {
|
|
||||||
int digit = num % 10;
|
|
||||||
|
|
||||||
if (digit == prev) {
|
|
||||||
has_double = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (digit > prev) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = digit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return has_double;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr bool is_valid_pass2(int num) {
|
|
||||||
int prev = 11;
|
|
||||||
bool has_double = false;
|
|
||||||
int run = 1;
|
|
||||||
|
|
||||||
for (; num != 0; num /= 10) {
|
|
||||||
int digit = num % 10;
|
|
||||||
|
|
||||||
if (digit == prev) {
|
|
||||||
++run;
|
|
||||||
} else {
|
|
||||||
if (run == 2) {
|
|
||||||
has_double = true;
|
|
||||||
}
|
|
||||||
run = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (digit > prev) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = digit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return has_double || run == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, int> read_input(std::istream& input) {
|
|
||||||
int a, b;
|
|
||||||
input >> a;
|
|
||||||
input.ignore();
|
|
||||||
input >> b;
|
|
||||||
|
|
||||||
return {a, b};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day04_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto [start_range, end_range] = read_input(input);
|
|
||||||
|
|
||||||
int num_valid = 0;
|
|
||||||
for (; start_range <= end_range; ++start_range) {
|
|
||||||
num_valid += is_valid_pass(start_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
output << num_valid << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day04_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto [start_range, end_range] = read_input(input);
|
|
||||||
|
|
||||||
int num_valid = 0;
|
|
||||||
for (; start_range <= end_range; ++start_range) {
|
|
||||||
num_valid += is_valid_pass2(start_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
output << num_valid << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Poor man's unit tests
|
|
||||||
static_assert(is_valid_pass(122345));
|
|
||||||
static_assert(is_valid_pass(111111));
|
|
||||||
static_assert(!is_valid_pass(223450));
|
|
||||||
static_assert(!is_valid_pass(123678));
|
|
||||||
|
|
||||||
static_assert(is_valid_pass2(112233));
|
|
||||||
static_assert(!is_valid_pass2(123444));
|
|
||||||
static_assert(is_valid_pass2(111122));
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
void aoc2019::day05_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto program = IntCodeComputer::read_intcode(input);
|
|
||||||
auto result = run_intcode(program, { 1 });
|
|
||||||
output << result.back() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day05_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto program = IntCodeComputer::read_intcode(input);
|
|
||||||
auto result = run_intcode(program, { 5 });
|
|
||||||
output << result.back() << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#include <deque>
|
|
||||||
#include <iostream>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::vector<std::pair<std::string, std::string>> read_orbits(std::istream &input) {
|
|
||||||
std::vector<std::pair<std::string, std::string>> result;
|
|
||||||
std::string name1, name2;
|
|
||||||
|
|
||||||
while (std::getline(input, name1, ')')) {
|
|
||||||
std::getline(input, name2);
|
|
||||||
|
|
||||||
result.emplace_back(name1, name2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day06_part1(std::istream &input, std::ostream &output) {
|
|
||||||
std::unordered_map<std::string, std::vector<std::string>> orbits;
|
|
||||||
|
|
||||||
for (auto[a, b] : read_orbits(input)) {
|
|
||||||
orbits[std::move(a)].emplace_back(std::move(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<std::pair<std::string, int>> todo = {{"COM", 0}};
|
|
||||||
int total_orbits = 0;
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
auto[name, offset] = todo.front();
|
|
||||||
todo.pop_front();
|
|
||||||
|
|
||||||
total_orbits += offset;
|
|
||||||
|
|
||||||
for (const auto& partner : orbits[name]) {
|
|
||||||
todo.emplace_back(partner, offset + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << total_orbits << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day06_part2(std::istream &input, std::ostream &output) {
|
|
||||||
std::unordered_map<std::string, std::string> ancestors;
|
|
||||||
|
|
||||||
for (auto[a, b] : read_orbits(input)) {
|
|
||||||
ancestors[std::move(b)] = std::move(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, int> santa_ancestors;
|
|
||||||
|
|
||||||
for (auto current = ancestors["SAN"]; current != "COM"; current = ancestors[current]) {
|
|
||||||
santa_ancestors[ancestors[current]] = santa_ancestors[current] + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dist = 0;
|
|
||||||
for (auto current = ancestors["YOU"]; current != "COM"; current = ancestors[current], ++dist) {
|
|
||||||
if (auto it = santa_ancestors.find(current); it != santa_ancestors.end()) {
|
|
||||||
output << dist + it->second << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::domain_error("No valid path.");
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
using aoc2019::IntCodeComputer;
|
|
||||||
|
|
||||||
std::int64_t simulate(const std::vector<std::int64_t> &program, const std::array<std::int64_t, 5> &phases) {
|
|
||||||
std::int64_t state = 0;
|
|
||||||
for (auto phase : phases) {
|
|
||||||
std::deque<std::int64_t> outputs;
|
|
||||||
IntCodeComputer computer{program, {phase, state}};
|
|
||||||
computer.connectOutput(outputs);
|
|
||||||
computer.run();
|
|
||||||
state = outputs.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
int simulate2(const std::vector<std::int64_t> &program, const std::array<int, 5> &phases) {
|
|
||||||
std::vector<IntCodeComputer> computers;
|
|
||||||
for (int phase : phases) {
|
|
||||||
computers.emplace_back(program, std::deque<int64_t>{phase});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < computers.size(); ++i) {
|
|
||||||
computers[i].connectOutput(computers[(i + 1) % 5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
computers[0].sendInput(0);
|
|
||||||
|
|
||||||
while (std::any_of(computers.begin(), computers.end(), [](const auto &c) { return !c.isTerminated();})) {
|
|
||||||
for (auto& computer : computers) {
|
|
||||||
computer.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return computers[0].currentInputs().back();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day07_part1(std::istream &input, std::ostream &output) {
|
|
||||||
const auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
|
||||||
std::array<std::int64_t, 5> phases{0, 1, 2, 3, 4};
|
|
||||||
|
|
||||||
std::int64_t best = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
best = std::max(simulate(program, phases), best);
|
|
||||||
} while (std::next_permutation(phases.begin(), phases.end()));
|
|
||||||
|
|
||||||
output << best << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day07_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
|
||||||
std::array<int, 5> phases{5, 6, 7, 8, 9};
|
|
||||||
|
|
||||||
int best = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
best = std::max(simulate2(program, phases), best);
|
|
||||||
} while (std::next_permutation(phases.begin(), phases.end()));
|
|
||||||
|
|
||||||
output << best << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr std::size_t WIDTH = 25;
|
|
||||||
constexpr std::size_t HEIGHT = 6;
|
|
||||||
constexpr std::size_t TILE_SIZE = WIDTH * HEIGHT;
|
|
||||||
|
|
||||||
enum Color {
|
|
||||||
BLACK = '0',
|
|
||||||
WHITE = '1',
|
|
||||||
TRANSPARENT = '2',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void aoc2019::day08_part1(std::istream &input, std::ostream &output) {
|
|
||||||
std::string buffer;
|
|
||||||
std::getline(input, buffer);
|
|
||||||
|
|
||||||
std::string_view image = buffer;
|
|
||||||
auto best = std::numeric_limits<int>::max();
|
|
||||||
auto best_score = 0;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < buffer.length(); i += TILE_SIZE) {
|
|
||||||
auto tile = image.substr(i, TILE_SIZE);
|
|
||||||
|
|
||||||
auto zeros = std::count(tile.begin(), tile.end(), '0');
|
|
||||||
|
|
||||||
if (zeros < best) {
|
|
||||||
best = zeros;
|
|
||||||
|
|
||||||
best_score = std::count(tile.begin(), tile.end(), '1') * std::count(tile.begin(), tile.end(), '2');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << best_score << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day08_part2(std::istream &input, std::ostream &output) {
|
|
||||||
std::string buffer;
|
|
||||||
std::getline(input, buffer);
|
|
||||||
|
|
||||||
std::string_view image = buffer;
|
|
||||||
|
|
||||||
std::array<Color, TILE_SIZE> final_image;
|
|
||||||
std::fill(final_image.begin(), final_image.end(), TRANSPARENT);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < buffer.length(); i += TILE_SIZE) {
|
|
||||||
auto tile = image.substr(i, TILE_SIZE);
|
|
||||||
|
|
||||||
for (int j = 0; j < TILE_SIZE; ++j) {
|
|
||||||
if (final_image[j] == TRANSPARENT) {
|
|
||||||
final_image[j] = static_cast<Color>(tile[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < final_image.size(); ++i) {
|
|
||||||
output << (final_image[i] == WHITE ? '#' : ' ');
|
|
||||||
if (i % WIDTH == WIDTH - 1) {
|
|
||||||
output << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <deque>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
void aoc2019::day09_part1(std::istream &input, std::ostream &output) {
|
|
||||||
std::deque<std::int64_t> outputs;
|
|
||||||
|
|
||||||
IntCodeComputer computer(input, { 1 });
|
|
||||||
computer.connectOutput(outputs);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
if (outputs.size() != 1) {
|
|
||||||
std::cerr << "Error: " << outputs.size() << std::endl;
|
|
||||||
for (auto c : outputs) {
|
|
||||||
std::cerr << c << std::endl;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output << outputs.front() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day09_part2(std::istream &input, std::ostream &output) {
|
|
||||||
std::deque<std::int64_t> outputs;
|
|
||||||
|
|
||||||
IntCodeComputer computer(input, { 2 });
|
|
||||||
computer.connectOutput(outputs);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
output << outputs.front() << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <numeric>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <cmath>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
|
|
||||||
std::vector<point_t> read_points(std::istream &input) {
|
|
||||||
std::vector<point_t> result;
|
|
||||||
|
|
||||||
int y = 0;
|
|
||||||
|
|
||||||
for (std::string buffer; std::getline(input, buffer); ++y) {
|
|
||||||
std::size_t x = 0;
|
|
||||||
|
|
||||||
while ((x = buffer.find('#', x)) != std::string::npos) {
|
|
||||||
result.push_back({(int) x, y});
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
point_t simplify(point_t x) {
|
|
||||||
auto gcd = std::abs(std::gcd(x[0], x[1]));
|
|
||||||
if (gcd > 1) {
|
|
||||||
return {x[0] / gcd, x[1] / gcd};
|
|
||||||
}
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::size_t, std::size_t> part1(const std::vector<point_t> &points) {
|
|
||||||
std::size_t best = 0;
|
|
||||||
std::size_t best_index = 0;
|
|
||||||
std::unordered_set<point_t> visible;
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < points.size(); ++i) {
|
|
||||||
visible.clear();
|
|
||||||
|
|
||||||
const auto point = points[i];
|
|
||||||
|
|
||||||
for (auto asteroid : points) {
|
|
||||||
if (asteroid == point) continue;
|
|
||||||
visible.insert(simplify(asteroid - point));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visible.size() > best) {
|
|
||||||
best = visible.size();
|
|
||||||
best_index = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
best = std::max(visible.size(), best);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {best, best_index};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day10_part1(std::istream &input, std::ostream &output) {
|
|
||||||
const auto points = read_points(input);
|
|
||||||
|
|
||||||
auto[best, _] = part1(points);
|
|
||||||
|
|
||||||
output << best << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day10_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto points = read_points(input);
|
|
||||||
const auto[_, base] = part1(points);
|
|
||||||
const auto base_point = points[base];
|
|
||||||
|
|
||||||
std::unordered_map<point_t, std::vector<point_t>> angle_points;
|
|
||||||
|
|
||||||
for (auto point : points) {
|
|
||||||
if (point == base_point) continue;
|
|
||||||
auto diff = point - base_point;
|
|
||||||
|
|
||||||
angle_points[simplify(diff)].push_back(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<float, point_t>> angles;
|
|
||||||
|
|
||||||
for (auto &entry : angle_points) {
|
|
||||||
angles.emplace_back(std::atan2(entry.first[1], entry.first[0]), entry.first);
|
|
||||||
// Sort entries in descending order of distance so we can pop_back() them
|
|
||||||
std::sort(entry.second.begin(), entry.second.end(), [](auto a, auto b) { return a.l1() > b.l1(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(angles.begin(), angles.end(), std::greater<>{});
|
|
||||||
|
|
||||||
const auto starting_point = std::make_pair(float(0.5 * M_PI),
|
|
||||||
point_t{std::numeric_limits<int>::max(),
|
|
||||||
std::numeric_limits<int>::max()});
|
|
||||||
|
|
||||||
auto it = std::lower_bound(angles.begin(), angles.end(), starting_point, std::greater<>{});
|
|
||||||
|
|
||||||
for (int hits = 0; hits < 199; ++hits) {
|
|
||||||
angle_points[it->second].pop_back();
|
|
||||||
|
|
||||||
// Advance it to the next asteroid we can hit.
|
|
||||||
while (angle_points[it->second].empty()) {
|
|
||||||
++it;
|
|
||||||
if (it == angles.end()) {
|
|
||||||
it = angles.begin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto final_asteroid = angle_points[it->second].back() + base_point;
|
|
||||||
|
|
||||||
output << final_asteroid[0] * 100 + final_asteroid[1] << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
using aoc2019::IntCodeComputer;
|
|
||||||
|
|
||||||
inline point_t turn_right(point_t direction) {
|
|
||||||
return {-direction[1], direction[0]};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline point_t turn_left(point_t direction) {
|
|
||||||
return {direction[1], -direction[0]};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<point_t, bool> simulate(std::istream &input, bool background = false) {
|
|
||||||
std::unordered_map<point_t, bool> image;
|
|
||||||
|
|
||||||
point_t direction{0, -1};
|
|
||||||
point_t pos = {0, 0};
|
|
||||||
|
|
||||||
IntCodeComputer computer(IntCodeComputer::read_intcode(input), {});
|
|
||||||
std::deque<std::int64_t> outputs;
|
|
||||||
|
|
||||||
computer.connectOutput(outputs);
|
|
||||||
|
|
||||||
while (!computer.isTerminated()) {
|
|
||||||
const auto it = image.find(pos);
|
|
||||||
computer.sendInput(it != image.end() ? it->second : background);
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
if (!outputs.empty()) {
|
|
||||||
assert(outputs.size() == 2);
|
|
||||||
auto color = outputs.front();
|
|
||||||
auto turn = outputs.back();
|
|
||||||
outputs.clear();
|
|
||||||
|
|
||||||
image[pos] = color;
|
|
||||||
|
|
||||||
if (turn) {
|
|
||||||
direction = turn_right(direction);
|
|
||||||
} else {
|
|
||||||
direction = turn_left(direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
pos += direction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day11_part1(std::istream &input, std::ostream &output) {
|
|
||||||
const auto result = simulate(input);
|
|
||||||
|
|
||||||
output << result.size() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day11_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto result = simulate(input, true);
|
|
||||||
|
|
||||||
// Determine bounding box
|
|
||||||
auto[lower,upper] = aoc2019::bounding_box(result);
|
|
||||||
|
|
||||||
for (int y = lower[1]; y <= upper[1]; ++y) {
|
|
||||||
for (int x = lower[0]; x <= upper[0]; ++x) {
|
|
||||||
if (auto it = result.find({x, y}); it != result.end() && it->second) {
|
|
||||||
output << '#';
|
|
||||||
} else {
|
|
||||||
output << ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <numeric>
|
|
||||||
#include <vector>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 3> point_t;
|
|
||||||
using aoc2019::from_chars;
|
|
||||||
|
|
||||||
std::vector<point_t> read_moons(std::istream &input) {
|
|
||||||
std::vector<point_t> moons;
|
|
||||||
|
|
||||||
point_t moon;
|
|
||||||
|
|
||||||
while (aoc2019::read_line_numbers_and_garbage<int>(input, moon.begin())) {
|
|
||||||
moons.push_back(moon);
|
|
||||||
}
|
|
||||||
|
|
||||||
return moons;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_velocity(const point_t &a, point_t &va, const point_t &b, point_t &vb) {
|
|
||||||
for (int i = 0; i < a.size(); ++i) {
|
|
||||||
if (a[i] < b[i]) {
|
|
||||||
va[i]++;
|
|
||||||
vb[i]--;
|
|
||||||
} else if (a[i] > b[i]) {
|
|
||||||
va[i]--;
|
|
||||||
vb[i]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_velocities(const std::vector<point_t> &positions, std::vector<point_t> &velocities) {
|
|
||||||
for (int i = 0; i < positions.size(); ++i) {
|
|
||||||
for (int j = i + 1; j < positions.size(); ++j) {
|
|
||||||
update_velocity(positions[i], velocities[i], positions[j], velocities[j]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void simulate_step(std::vector<point_t> &moons, std::vector<point_t> &velocities) {
|
|
||||||
update_velocities(moons, velocities);
|
|
||||||
|
|
||||||
for (int j = 0; j < moons.size(); ++j) {
|
|
||||||
moons[j] += velocities[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day12_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto moons = read_moons(input);
|
|
||||||
std::vector<point_t> velocities(moons.size());
|
|
||||||
|
|
||||||
for (int i = 0; i < 1000; ++i) {
|
|
||||||
simulate_step(moons, velocities);
|
|
||||||
}
|
|
||||||
|
|
||||||
int energy = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < moons.size(); ++i) {
|
|
||||||
energy += moons[i].l1() * velocities[i].l1();
|
|
||||||
}
|
|
||||||
|
|
||||||
output << energy << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day12_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto moons = read_moons(input);
|
|
||||||
auto moons_mut = moons;
|
|
||||||
std::vector<point_t> velocities(moons.size());
|
|
||||||
|
|
||||||
std::array<uint64_t, 3> recurrence = {0, 0, 0};
|
|
||||||
|
|
||||||
std::uint64_t steps = 0;
|
|
||||||
|
|
||||||
while (!std::all_of(recurrence.begin(), recurrence.end(), [](auto x) { return x > 0; })) {
|
|
||||||
simulate_step(moons_mut, velocities);
|
|
||||||
++steps;
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
if (!recurrence[i]) {
|
|
||||||
bool back_again =
|
|
||||||
std::all_of(velocities.begin(), velocities.end(), [i](const auto &x) { return !x[i]; })
|
|
||||||
&& std::equal(moons_mut.begin(), moons_mut.end(), moons.begin(),
|
|
||||||
[i](const auto &a, const auto &b) {
|
|
||||||
return a[i] == b[i];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (back_again) {
|
|
||||||
recurrence[i] = steps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = std::lcm(recurrence[0], std::lcm(recurrence[1], recurrence[2]));
|
|
||||||
output << result << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#ifdef ANIMATE_DAY13
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
#endif
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int64_t, 2> point_t;
|
|
||||||
|
|
||||||
enum class Tile {
|
|
||||||
EMPTY,
|
|
||||||
WALL,
|
|
||||||
BLOCK,
|
|
||||||
PADDLE,
|
|
||||||
BALL,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unordered_map<point_t, Tile> Screen;
|
|
||||||
|
|
||||||
std::optional<std::int64_t> update_screen(std::deque<std::int64_t> &output_buffer, Screen &screen) {
|
|
||||||
std::optional<std::int64_t> score;
|
|
||||||
while (!output_buffer.empty()) {
|
|
||||||
auto x = output_buffer.front(); output_buffer.pop_front();
|
|
||||||
auto y = output_buffer.front(); output_buffer.pop_front();
|
|
||||||
auto type = output_buffer.front(); output_buffer.pop_front();
|
|
||||||
|
|
||||||
if (x == -1 && y == 0) {
|
|
||||||
score = type;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
screen[{x, y}] = static_cast<Tile>(type);
|
|
||||||
}
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw_screen(const Screen &screen, std::ostream& output) {
|
|
||||||
// Determine bounding box
|
|
||||||
using limits = std::numeric_limits<int>;
|
|
||||||
|
|
||||||
const auto [lower, upper] = aoc2019::bounding_box(screen);
|
|
||||||
|
|
||||||
for (auto y = lower[1]; y <= upper[1]; ++y) {
|
|
||||||
for (auto x = lower[0]; x <= upper[0]; ++x) {
|
|
||||||
char c = ' ';
|
|
||||||
if (auto it = screen.find({x, y}); it != screen.end()) {
|
|
||||||
switch (it->second) {
|
|
||||||
case Tile::EMPTY:
|
|
||||||
c = ' ';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Tile::BALL:
|
|
||||||
c = '*';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Tile::BLOCK:
|
|
||||||
c = '=';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Tile::PADDLE:
|
|
||||||
c = '_';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Tile::WALL:
|
|
||||||
c = '#';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << c;
|
|
||||||
}
|
|
||||||
|
|
||||||
output << '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto find_pos(const Screen &screen, Tile to_find) {
|
|
||||||
return std::find_if(screen.begin(), screen.end(), [to_find](const auto& x) {
|
|
||||||
return x.second == to_find;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day13_part1(std::istream &input, std::ostream &output) {
|
|
||||||
Screen screen;
|
|
||||||
aoc2019::IntCodeComputer computer(aoc2019::IntCodeComputer::read_intcode(input));
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
computer.run();
|
|
||||||
update_screen(output_buffer, screen);
|
|
||||||
|
|
||||||
output << std::count_if(screen.begin(), screen.end(), [](const auto &x) { return x.second == Tile::BLOCK; })
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day13_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto program = aoc2019::IntCodeComputer::read_intcode(input);
|
|
||||||
program[0] = 2;
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer computer(std::move(program));
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
Screen screen;
|
|
||||||
|
|
||||||
std::int64_t score = 0;
|
|
||||||
|
|
||||||
while (!computer.isTerminated()) {
|
|
||||||
computer.run();
|
|
||||||
auto new_score = update_screen(output_buffer, screen);
|
|
||||||
if (new_score) {
|
|
||||||
score = *new_score;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ANIMATE_DAY13
|
|
||||||
output << "Score: " << score << std::endl;
|
|
||||||
draw_screen(screen, output);
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(40));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
auto ball_pos = find_pos(screen, Tile::BALL)->first;
|
|
||||||
auto paddle_pos = find_pos(screen, Tile::PADDLE)->first;
|
|
||||||
|
|
||||||
if (ball_pos[0] < paddle_pos[0]) {
|
|
||||||
computer.sendInput(-1);
|
|
||||||
} else if (ball_pos[0] > paddle_pos[0]) {
|
|
||||||
computer.sendInput(1);
|
|
||||||
} else {
|
|
||||||
computer.sendInput(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << score << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <regex>
|
|
||||||
#include <charconv>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef std::pair<std::string, std::int64_t> requirement_t;
|
|
||||||
typedef std::vector<requirement_t> reqlist_t;
|
|
||||||
|
|
||||||
std::map<reqlist_t, reqlist_t> read_recipes(std::istream &input) {
|
|
||||||
std::map<reqlist_t, reqlist_t> recipes;
|
|
||||||
|
|
||||||
std::string buffer;
|
|
||||||
std::regex listing_regex("(\\d+) ([A-Z]+)");
|
|
||||||
|
|
||||||
std::int64_t amount;
|
|
||||||
|
|
||||||
while (std::getline(input, buffer)) {
|
|
||||||
reqlist_t requirements, production;
|
|
||||||
|
|
||||||
std::string_view line = buffer;
|
|
||||||
|
|
||||||
auto split_point = line.find(" => ");
|
|
||||||
|
|
||||||
auto requirements_part = line.substr(0, split_point);
|
|
||||||
auto production_part = line.substr(split_point + 4);
|
|
||||||
|
|
||||||
for (auto it = std::regex_token_iterator(requirements_part.begin(), requirements_part.end(), listing_regex,
|
|
||||||
{1, 2}); it != std::cregex_token_iterator(); ++it) {
|
|
||||||
std::from_chars(it->first, it->second, amount);
|
|
||||||
++it;
|
|
||||||
|
|
||||||
requirements.emplace_back(*it, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = std::regex_token_iterator(production_part.begin(), production_part.end(), listing_regex,
|
|
||||||
{1, 2}); it != std::cregex_token_iterator(); ++it) {
|
|
||||||
std::from_chars(it->first, it->second, amount);
|
|
||||||
++it;
|
|
||||||
|
|
||||||
production.emplace_back(*it, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
recipes[std::move(production)] = std::move(requirements);
|
|
||||||
}
|
|
||||||
|
|
||||||
return recipes;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Map>
|
|
||||||
std::unordered_map<std::string, reqlist_t> element_creators(const Map &map) {
|
|
||||||
std::unordered_map<std::string, reqlist_t> inverted;
|
|
||||||
|
|
||||||
for (auto &entry : map) {
|
|
||||||
for (auto &x : entry.first) {
|
|
||||||
inverted[x.first] = entry.first;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inverted;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> topological_order(const std::map<reqlist_t, reqlist_t> &recipes) {
|
|
||||||
std::vector<std::string> order;
|
|
||||||
|
|
||||||
std::unordered_map<std::string, std::vector<std::string>> edges;
|
|
||||||
for (auto &entry : recipes) {
|
|
||||||
for (auto &production : entry.first) {
|
|
||||||
std::transform(entry.second.begin(), entry.second.end(), std::back_inserter(edges[production.first]),
|
|
||||||
[](const auto &x) {
|
|
||||||
return x.first;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return aoc2019::topological_sort(edges);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int64_t ore_to_fuel(const std::map<reqlist_t, reqlist_t> &recipes, std::int64_t amount = 1) {
|
|
||||||
auto inverted = element_creators(recipes);
|
|
||||||
auto order = topological_order(recipes);
|
|
||||||
|
|
||||||
std::unordered_map<std::string_view, std::int64_t> total_requirements;
|
|
||||||
total_requirements["FUEL"] = amount;
|
|
||||||
|
|
||||||
for (const auto &element : order) {
|
|
||||||
if (element == "ORE") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto number_required = total_requirements[element];
|
|
||||||
if (number_required <= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto &productions = inverted.at(element);
|
|
||||||
const auto &requirements = recipes.at(productions);
|
|
||||||
|
|
||||||
auto number_produced = std::find_if(productions.begin(), productions.end(),
|
|
||||||
[element](const auto &x) { return x.first == element; })->second;
|
|
||||||
|
|
||||||
auto productions_needed = number_required / number_produced + (number_required % number_produced ? 1 : 0);
|
|
||||||
|
|
||||||
for (auto &requirement : requirements) {
|
|
||||||
total_requirements[requirement.first] += requirement.second * productions_needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &production : productions) {
|
|
||||||
total_requirements[production.first] -= productions_needed * production.second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return total_requirements["ORE"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day14_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto recipes = read_recipes(input);
|
|
||||||
|
|
||||||
output << ore_to_fuel(recipes) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day14_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto recipes = read_recipes(input);
|
|
||||||
|
|
||||||
constexpr std::int64_t ore_stock = 1000000000000;
|
|
||||||
|
|
||||||
std::int64_t min = 1, max = ore_stock + 1; // assumption: 1 ore produces < 1 fuel.
|
|
||||||
while (max - min > 1) {
|
|
||||||
auto cur = (max + min) / 2;
|
|
||||||
|
|
||||||
if (ore_to_fuel(recipes, cur) < ore_stock) {
|
|
||||||
min = cur;
|
|
||||||
} else {
|
|
||||||
max = cur - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << (max + min) / 2 << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
|
|
||||||
enum class Tile {
|
|
||||||
Wall,
|
|
||||||
Empty,
|
|
||||||
Oxygen,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class Mark {
|
|
||||||
None,
|
|
||||||
Temp,
|
|
||||||
Permanent,
|
|
||||||
};
|
|
||||||
|
|
||||||
const std::unordered_map<point_t, std::int64_t> DIRECTIONS{
|
|
||||||
{{0, -1}, 1},
|
|
||||||
{{0, 1}, 2},
|
|
||||||
{{-1, 0}, 3},
|
|
||||||
{{1, 0}, 4},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<point_t, Tile> read_map(std::istream &input) {
|
|
||||||
aoc2019::IntCodeComputer computer(input);
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
|
|
||||||
point_t pos = {0, 0};
|
|
||||||
std::deque<point_t> prev;
|
|
||||||
std::unordered_map<point_t, Tile> map{{pos, Tile::Empty}};
|
|
||||||
std::unordered_map<point_t, Mark> markings{{pos, Mark::Temp}};
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
std::optional<point_t> next_step;
|
|
||||||
|
|
||||||
for (auto &direction : DIRECTIONS) {
|
|
||||||
if (markings[pos + direction.first] == Mark::None) {
|
|
||||||
next_step = direction.first;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (next_step) {
|
|
||||||
const auto next_pos = pos + *next_step;
|
|
||||||
computer.sendInput(DIRECTIONS.at(*next_step));
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
assert(!output_buffer.empty());
|
|
||||||
|
|
||||||
switch (output_buffer.front()) {
|
|
||||||
case 0:
|
|
||||||
markings[next_pos] = Mark::Permanent;
|
|
||||||
map[next_pos] = Tile::Wall;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
case 2:
|
|
||||||
prev.push_front(pos);
|
|
||||||
markings[next_pos] = Mark::Temp;
|
|
||||||
map[next_pos] = static_cast<Tile>(output_buffer.front());
|
|
||||||
pos = next_pos;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw std::domain_error("Invalid data from remote");
|
|
||||||
}
|
|
||||||
output_buffer.pop_front();
|
|
||||||
assert(output_buffer.empty());
|
|
||||||
} else {
|
|
||||||
markings[pos] = Mark::Permanent;
|
|
||||||
// Nowhere left to go, move back.
|
|
||||||
if (prev.empty()) {
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto prev_pos = prev.front();
|
|
||||||
auto step = DIRECTIONS.at(prev_pos - pos);
|
|
||||||
prev.pop_front();
|
|
||||||
computer.sendInput(step);
|
|
||||||
computer.run();
|
|
||||||
// We should be able to travel back
|
|
||||||
assert(output_buffer.front() == 1);
|
|
||||||
output_buffer.pop_front();
|
|
||||||
pos = prev_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Callback>
|
|
||||||
int bfs(const std::unordered_map<point_t, Tile> &map, point_t starting_point, Callback callback) {
|
|
||||||
std::deque<std::pair<point_t, int>> todo{{starting_point, 0}};
|
|
||||||
std::unordered_set<point_t> visited{{0, 0}};
|
|
||||||
|
|
||||||
int max_dist = 0;
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
auto[cur, dist] = todo.front();
|
|
||||||
todo.pop_front();
|
|
||||||
|
|
||||||
max_dist = std::max(max_dist, dist);
|
|
||||||
|
|
||||||
for (auto &dir : DIRECTIONS) {
|
|
||||||
auto new_pos = cur + dir.first;
|
|
||||||
if (!visited.count(new_pos)) {
|
|
||||||
visited.insert(new_pos);
|
|
||||||
|
|
||||||
if (callback(map.at(new_pos))) {
|
|
||||||
return dist + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (map.at(new_pos)) {
|
|
||||||
case Tile::Oxygen:
|
|
||||||
case Tile::Empty:
|
|
||||||
todo.emplace_back(new_pos, dist + 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return max_dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day15_part1(std::istream &input, std::ostream &output) {
|
|
||||||
const auto map = read_map(input);
|
|
||||||
|
|
||||||
auto dist = bfs(map, {0, 0}, [](Tile x) { return x == Tile::Oxygen; });
|
|
||||||
|
|
||||||
output << dist << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day15_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto map = read_map(input);
|
|
||||||
|
|
||||||
auto starting_point = std::find_if(map.begin(), map.end(), [](auto &x) { return x.second == Tile::Oxygen; })->first;
|
|
||||||
|
|
||||||
auto dist = bfs(map, starting_point, [](Tile x) { return false; });
|
|
||||||
|
|
||||||
output << dist << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <array>
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
|
||||||
#include <iterator>
|
|
||||||
#include <numeric>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::array<int, 4> base_pattern{0, 1, 0, -1};
|
|
||||||
|
|
||||||
int get_modifier(int rank, int pos) {
|
|
||||||
pos += 1;
|
|
||||||
pos /= rank + 1;
|
|
||||||
|
|
||||||
return base_pattern[pos % 4];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> read_input(std::istream &input) {
|
|
||||||
std::vector<int> result;
|
|
||||||
|
|
||||||
for (char c; input >> c;) {
|
|
||||||
assert(std::isdigit(c));
|
|
||||||
result.push_back(c - '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void simulate(std::vector<int> numbers, std::ostream &output) {
|
|
||||||
std::vector<int> new_numbers(numbers.size());
|
|
||||||
std::vector<int> partial_sums(numbers.size());
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
for (int rank = 0; rank < numbers.size(); ++rank) {
|
|
||||||
std::partial_sum(numbers.begin() + rank, numbers.end(), partial_sums.begin() + rank);
|
|
||||||
int n = 0;
|
|
||||||
for (int pos = rank; pos < numbers.size(); pos += rank + 1) {
|
|
||||||
int run = std::min(rank + 1, (int) numbers.size() - pos);
|
|
||||||
if (int modifier = get_modifier(rank, pos); modifier) {
|
|
||||||
n += modifier * (partial_sums[pos + run - 1] - partial_sums[pos] + numbers[pos]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
n = std::abs(n % 10);
|
|
||||||
|
|
||||||
new_numbers[rank] = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::swap(numbers, new_numbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::copy(numbers.begin(), numbers.begin() + 8, std::ostream_iterator<int>(output));
|
|
||||||
output << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_offset(const std::vector<int> &numbers) {
|
|
||||||
int offset = 0;
|
|
||||||
for (int i = 0; i < 7; ++i) {
|
|
||||||
offset *= 10;
|
|
||||||
offset += numbers[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> numbers_from_offset(const std::vector<int> &numbers, unsigned int offset) {
|
|
||||||
constexpr auto repetitions = 10000;
|
|
||||||
const auto desired_length = repetitions * numbers.size() - offset;
|
|
||||||
|
|
||||||
std::vector<int> numbers_after;
|
|
||||||
numbers_after.reserve(desired_length);
|
|
||||||
numbers_after.insert(numbers_after.end(), numbers.begin() + (offset % numbers.size()), numbers.end());
|
|
||||||
|
|
||||||
while (numbers_after.size() < desired_length) {
|
|
||||||
auto remaining = desired_length - numbers_after.size();
|
|
||||||
if (remaining >= numbers.size()) {
|
|
||||||
numbers_after.insert(numbers_after.end(), numbers.begin(), numbers.end());
|
|
||||||
} else {
|
|
||||||
numbers_after.insert(numbers_after.end(), numbers.begin(), numbers.end() + remaining);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return numbers_after;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day16_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto numbers = read_input(input);
|
|
||||||
|
|
||||||
simulate(std::move(numbers), output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day16_part2(std::istream &input, std::ostream &output) {
|
|
||||||
auto numbers = read_input(input);
|
|
||||||
|
|
||||||
const int offset = get_offset(numbers);
|
|
||||||
|
|
||||||
numbers = numbers_from_offset(numbers, offset);
|
|
||||||
|
|
||||||
std::vector<int> new_numbers(numbers.size());
|
|
||||||
std::vector<int> partial_sums(numbers.size());
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
std::partial_sum(numbers.rbegin(), numbers.rend(), partial_sums.rbegin());
|
|
||||||
|
|
||||||
std::transform(partial_sums.begin(), partial_sums.end(), new_numbers.begin(), [](int x) {
|
|
||||||
return x % 10;
|
|
||||||
});
|
|
||||||
|
|
||||||
std::swap(numbers, new_numbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::copy(numbers.begin(), numbers.begin() + 8, std::ostream_iterator<int>(output));
|
|
||||||
output << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <string_view>
|
|
||||||
#include <cassert>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
|
|
||||||
const std::unordered_map<char, point_t> DIRECTIONS{
|
|
||||||
{'^', {0, -1}},
|
|
||||||
{'>', {0, 1}},
|
|
||||||
{'v', {1, 0}},
|
|
||||||
{'<', {-1, 0}},
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<point_t, char> read_scaffold(const std::deque<std::int64_t> &data) {
|
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
std::unordered_map<point_t, char> map;
|
|
||||||
for (auto n : data) {
|
|
||||||
if (n == '\n') {
|
|
||||||
if (x == 0) {
|
|
||||||
// Double newline, end of map
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++y;
|
|
||||||
x = 0;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
map[{x, y}] = (char) n;
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day17_part1(std::istream &input, std::ostream &output) {
|
|
||||||
IntCodeComputer computer(input);
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
const auto map = read_scaffold(output_buffer);
|
|
||||||
|
|
||||||
std::int64_t total = 0;
|
|
||||||
|
|
||||||
for (auto &entry : map) {
|
|
||||||
if (entry.second == '.') continue;
|
|
||||||
|
|
||||||
bool is_intersection = std::all_of(DIRECTIONS.begin(), DIRECTIONS.end(), [&map, &entry](auto &x) {
|
|
||||||
auto it = map.find(x.second + entry.first);
|
|
||||||
return it != map.end() && it->second != '.';
|
|
||||||
});
|
|
||||||
|
|
||||||
if (is_intersection) {
|
|
||||||
total += entry.first[0] * entry.first[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << total << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day17_part2(std::istream &input, std::ostream &output) {
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer computer(input);
|
|
||||||
computer[0] = 2;
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
|
|
||||||
std::array<std::string_view, 3> programs = {
|
|
||||||
"L,6,R,8,L,4,R,8,L,12\n",
|
|
||||||
"L,12,R,10,L,4\n",
|
|
||||||
"L,12,L,6,L,4,L,4\n",
|
|
||||||
};
|
|
||||||
|
|
||||||
auto combined_programs = "A,B,B,C,B,C,B,C,A,A\n"sv;
|
|
||||||
|
|
||||||
computer.sendInputs(combined_programs);
|
|
||||||
|
|
||||||
for (auto program : programs) {
|
|
||||||
computer.sendInputs(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't give me output.
|
|
||||||
computer.sendInputs("n\n");
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
assert(!output_buffer.empty());
|
|
||||||
|
|
||||||
if (output_buffer.size() == 1) {
|
|
||||||
output << output_buffer.front() << std::endl;
|
|
||||||
} else {
|
|
||||||
std::copy(output_buffer.begin(), output_buffer.end(), std::ostreambuf_iterator<char>(output));
|
|
||||||
output << output_buffer.back() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import fileinput
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def turn_left(direction):
|
|
||||||
x, y = direction
|
|
||||||
return (y, -x)
|
|
||||||
|
|
||||||
|
|
||||||
def turn_right(direction):
|
|
||||||
x, y = direction
|
|
||||||
return (-y, x)
|
|
||||||
|
|
||||||
|
|
||||||
def add(pos, direction):
|
|
||||||
return tuple(a + b for a, b in zip(pos, direction))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
chart = [line.strip() for line in fileinput.input()]
|
|
||||||
|
|
||||||
pos = None
|
|
||||||
|
|
||||||
for y, line in enumerate(chart):
|
|
||||||
x = line.find('^')
|
|
||||||
if x >= 0:
|
|
||||||
pos = (x, y)
|
|
||||||
break
|
|
||||||
|
|
||||||
if not pos:
|
|
||||||
sys.exit('starting point not found')
|
|
||||||
|
|
||||||
route = ['L']
|
|
||||||
|
|
||||||
direction = (-1, 0)
|
|
||||||
|
|
||||||
def bounds_check(pos):
|
|
||||||
x, y = pos
|
|
||||||
|
|
||||||
return x >= 0 and y >= 0 and y < len(chart)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
# try to move forward
|
|
||||||
next_pos = add(direction, pos)
|
|
||||||
dist = 0
|
|
||||||
|
|
||||||
while bounds_check(next_pos) and chart[next_pos[1]][next_pos[0]] == '#':
|
|
||||||
dist += 1
|
|
||||||
pos = next_pos
|
|
||||||
next_pos = add(direction, pos)
|
|
||||||
|
|
||||||
if dist:
|
|
||||||
route.append(dist)
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
for move, new_dir in zip(('L', 'R'), (turn_left(direction), turn_right(direction))):
|
|
||||||
next_pos = add(pos, new_dir)
|
|
||||||
if bounds_check(next_pos) and chart[next_pos[1]][next_pos[0]] == '#':
|
|
||||||
route.append(move)
|
|
||||||
direction = new_dir
|
|
||||||
break
|
|
||||||
|
|
||||||
printable_route = []
|
|
||||||
for x in route:
|
|
||||||
if x == 'L' or x == 'R':
|
|
||||||
printable_route.append(x)
|
|
||||||
else:
|
|
||||||
printable_route += ['M'] * x
|
|
||||||
|
|
||||||
print(','.join(str(x) for x in route))
|
|
||||||
print(','.join(printable_route))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <bit>
|
|
||||||
#include <map>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
static_assert(sizeof(int) >= 4, "Int should be at least 32 bits.");
|
|
||||||
|
|
||||||
using namespace std::string_view_literals;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
|
|
||||||
typedef std::vector<std::string> map_t;
|
|
||||||
|
|
||||||
std::array<point_t, 4> DIRECTIONS = {{
|
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
map_t read_map(std::istream &input) {
|
|
||||||
std::string buffer;
|
|
||||||
map_t map;
|
|
||||||
|
|
||||||
while (std::getline(input, buffer)) {
|
|
||||||
map.push_back(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
point_t find(const std::vector<std::string> &map, char needle) {
|
|
||||||
for (int y = 0; y < map.size(); ++y) {
|
|
||||||
auto x = map[y].find(needle);
|
|
||||||
if (x != std::string::npos) {
|
|
||||||
return {(int) x, y};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::invalid_argument("Can't find it!");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::pair<char, int>> find_edges(const map_t &map, point_t starting_point) {
|
|
||||||
std::vector<std::pair<char, int>> edges;
|
|
||||||
std::queue<std::pair<int, point_t>> todo;
|
|
||||||
todo.emplace(0, starting_point);
|
|
||||||
|
|
||||||
std::unordered_set<point_t> visited{starting_point};
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, pos] = todo.front();
|
|
||||||
todo.pop();
|
|
||||||
|
|
||||||
for (auto &direction : DIRECTIONS) {
|
|
||||||
auto next_pos = pos + direction;
|
|
||||||
const char at = map[next_pos[1]][next_pos[0]];
|
|
||||||
|
|
||||||
if (at == '#' || visited.count(next_pos)) {
|
|
||||||
// Wall or already visited, ignore
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.insert(next_pos);
|
|
||||||
|
|
||||||
if (std::isalpha(at)) {
|
|
||||||
// Don't walk through stuff
|
|
||||||
edges.emplace_back(at, dist + 1);
|
|
||||||
} else {
|
|
||||||
todo.emplace(dist + 1, next_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return edges;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto compute_implied_graph(const map_t &map) {
|
|
||||||
std::unordered_map<char, std::vector<std::pair<char, int>>> implied_graph;
|
|
||||||
|
|
||||||
for (int y = 0; y < map.size(); ++y) {
|
|
||||||
for (int x = 0; x < map[y].size(); ++x) {
|
|
||||||
char at = map[y][x];
|
|
||||||
if ("@/^*"sv.find(at) != std::string_view::npos || std::isalpha(at)) {
|
|
||||||
implied_graph[at] = find_edges(map, {x, y});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return implied_graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline unsigned int key_index(char c) {
|
|
||||||
return 1u << static_cast<unsigned int>(c - 'A');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day18_part1(std::istream &input, std::ostream &output) {
|
|
||||||
using state_t = std::tuple<unsigned int, char>;
|
|
||||||
|
|
||||||
const auto map = read_map(input);
|
|
||||||
|
|
||||||
auto implied_graph = compute_implied_graph(map);
|
|
||||||
|
|
||||||
std::priority_queue<std::pair<int, state_t>, std::vector<std::pair<int, state_t>>, std::greater<>> todo;
|
|
||||||
std::map<state_t, int> visited;
|
|
||||||
todo.emplace(0, std::make_pair(0, '@'));
|
|
||||||
|
|
||||||
auto target_size = std::count_if(implied_graph.cbegin(), implied_graph.cend(),
|
|
||||||
[](auto &x) { return std::islower(x.first); });
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, state] = todo.top();
|
|
||||||
todo.pop();
|
|
||||||
|
|
||||||
if (visited[state] < dist) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto[keys, pos] = state;
|
|
||||||
|
|
||||||
if (std::__popcount(keys) == target_size) {
|
|
||||||
output << dist << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &edge : implied_graph.at(pos)) {
|
|
||||||
auto next_dist = dist + edge.second;
|
|
||||||
auto next_keys = keys;
|
|
||||||
if (std::islower(edge.first)) {
|
|
||||||
// Add the key to our collection
|
|
||||||
next_keys |= key_index(edge.first);;
|
|
||||||
} else if (std::isupper(edge.first)) {
|
|
||||||
// Check if we have the required key already
|
|
||||||
if (!(next_keys & key_index(edge.first))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state_t next_state = {next_keys, edge.first};
|
|
||||||
if (auto it = visited.find(next_state); it == visited.end() || it->second > next_dist) {
|
|
||||||
visited[next_state] = next_dist;
|
|
||||||
todo.emplace(next_dist, next_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::logic_error("Should have terminated by now.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day18_part2(std::istream &input, std::ostream &output) {
|
|
||||||
using state_t = std::pair<unsigned int, std::array<char, 4>>;
|
|
||||||
|
|
||||||
auto map = read_map(input);
|
|
||||||
|
|
||||||
// problem statement says to duplicate @ but where's the fun in that
|
|
||||||
const auto initial_pos = find(map, '@');
|
|
||||||
|
|
||||||
// problem statement says to duplicate @ but where's the fun in that, let's have different starting positions
|
|
||||||
std::array<std::string_view, 3> overlay = {
|
|
||||||
"@#*",
|
|
||||||
"###",
|
|
||||||
"^#/",
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int y = 0; y < 3; ++y) {
|
|
||||||
auto &row = map[initial_pos[1] + y - 1];
|
|
||||||
std::copy(overlay[y].begin(), overlay[y].end(), row.data() + initial_pos[0] - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto implied_graph = compute_implied_graph(map);
|
|
||||||
|
|
||||||
std::priority_queue<std::pair<int, state_t>, std::vector<std::pair<int, state_t>>, std::greater<>> todo;
|
|
||||||
std::map<std::pair<unsigned int, char>, int> visited;
|
|
||||||
todo.emplace(0, state_t(0, {'@', '*', '^', '/'}));
|
|
||||||
|
|
||||||
auto target_size = std::count_if(implied_graph.cbegin(), implied_graph.cend(),
|
|
||||||
[](auto &x) { return std::islower(x.first); });
|
|
||||||
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, state] = todo.top();
|
|
||||||
todo.pop();
|
|
||||||
|
|
||||||
auto[keys, pos] = state;
|
|
||||||
|
|
||||||
if (std::__popcount(keys) == target_size) {
|
|
||||||
output << dist << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
auto next_pos = pos;
|
|
||||||
for (const auto &edge : implied_graph.at(pos[i])) {
|
|
||||||
auto next_dist = dist + edge.second;
|
|
||||||
auto next_keys = keys;
|
|
||||||
if (std::islower(edge.first)) {
|
|
||||||
// Add the key to our collection
|
|
||||||
next_keys |= key_index(edge.first);;
|
|
||||||
} else if (std::isupper(edge.first)) {
|
|
||||||
// Check if we have the required key already
|
|
||||||
if (!(next_keys & key_index(edge.first))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_pos[i] = edge.first;
|
|
||||||
|
|
||||||
state_t next_state = {next_keys, next_pos};
|
|
||||||
if (auto it = visited.find({next_keys, next_pos[i]}); it == visited.end() || it->second > next_dist) {
|
|
||||||
visited[{next_keys, next_pos[i]}] = next_dist;
|
|
||||||
todo.emplace(next_dist, next_state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
output << "Not implemented\n";
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include <queue>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
bool bounds_check(aoc2019::IntCodeComputer computer, std::int64_t x, std::int64_t y) {
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
|
|
||||||
computer.sendInput(x);
|
|
||||||
computer.sendInput(y);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
assert(computer.isTerminated());
|
|
||||||
assert(!output_buffer.empty());
|
|
||||||
|
|
||||||
return output_buffer.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
class Beam {
|
|
||||||
private:
|
|
||||||
aoc2019::IntCodeComputer computer;
|
|
||||||
std::int64_t last_width = 1;
|
|
||||||
std::int64_t last_start = 0;
|
|
||||||
std::int64_t y = 0;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Beam(std::istream &input) : computer(input) {};
|
|
||||||
|
|
||||||
std::pair<std::int64_t, std::int64_t> next() {
|
|
||||||
auto x = last_start;
|
|
||||||
|
|
||||||
while (!bounds_check(computer, x, y)) {
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_start = x;
|
|
||||||
x += last_width - 1;
|
|
||||||
|
|
||||||
while (bounds_check(computer, x, y)) {
|
|
||||||
++x;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_width = x - last_start;
|
|
||||||
++y;
|
|
||||||
|
|
||||||
return {last_start, last_width};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day19_part1(std::istream &input, std::ostream &output) {
|
|
||||||
Beam beam(input);
|
|
||||||
|
|
||||||
std::int64_t covered = 0;
|
|
||||||
for (std::int64_t y = 0; y < 50; ++y) {
|
|
||||||
const auto[start, width] = beam.next();
|
|
||||||
|
|
||||||
if (start >= 50) break;
|
|
||||||
|
|
||||||
covered += std::min(50 - start, width);
|
|
||||||
}
|
|
||||||
|
|
||||||
output << covered << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day19_part2(std::istream &input, std::ostream &output) {
|
|
||||||
Beam beam(input);
|
|
||||||
std::queue<std::int64_t> beam_ends;
|
|
||||||
|
|
||||||
constexpr std::int64_t DIMENSION = 100;
|
|
||||||
|
|
||||||
for (std::int64_t y = 0; true; ++y) {
|
|
||||||
const auto[start, width] = beam.next();
|
|
||||||
|
|
||||||
beam_ends.push(start + width);
|
|
||||||
if (beam_ends.size() == DIMENSION) {
|
|
||||||
auto end = beam_ends.front();
|
|
||||||
if (end - start >= DIMENSION) {
|
|
||||||
auto result = start * 10000 + y - DIMENSION + 1;
|
|
||||||
output << result << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
beam_ends.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <queue>
|
|
||||||
#include <set>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "point.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
typedef aoc2019::Point<int, 2> point_t;
|
|
||||||
typedef std::vector<std::string> map_t;
|
|
||||||
|
|
||||||
std::array<point_t, 4> DIRECTIONS = {{
|
|
||||||
{0, -1},
|
|
||||||
{0, 1},
|
|
||||||
{-1, 0},
|
|
||||||
{1, 0},
|
|
||||||
}};
|
|
||||||
|
|
||||||
std::vector<std::string> read_map(std::istream &input) {
|
|
||||||
std::string buffer;
|
|
||||||
std::vector<std::string> map;
|
|
||||||
|
|
||||||
while (std::getline(input, buffer)) {
|
|
||||||
map.push_back(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto get_portals(const map_t &map) {
|
|
||||||
std::unordered_map<point_t, std::string> portals;
|
|
||||||
|
|
||||||
// First find horizontal portals
|
|
||||||
for (int y = 0; y < map.size(); ++y) {
|
|
||||||
for (int x = 0; x < map[y].size() - 1; ++x) {
|
|
||||||
if (std::isalpha(map[y][x]) && std::isalpha(map[y][x + 1])) {
|
|
||||||
// find out the entry point
|
|
||||||
point_t entry_point = {0, y};
|
|
||||||
if (x > 0 && map[y][x - 1] == '.') {
|
|
||||||
entry_point[0] = x - 1;
|
|
||||||
} else {
|
|
||||||
entry_point[0] = x + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
portals[entry_point] = map[y].substr(x, 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char name[3] = {0, 0, 0};
|
|
||||||
for (int x = 0; x < map[0].size(); ++x) {
|
|
||||||
for (int y = 0; y < map.size() - 1; ++y) {
|
|
||||||
if (std::isalpha(map[y][x]) && std::isalpha(map[y + 1][x])) {
|
|
||||||
name[0] = map[y][x];
|
|
||||||
name[1] = map[y + 1][x];
|
|
||||||
|
|
||||||
point_t entry_point = {x, 0};
|
|
||||||
if (y > 0 && map[y - 1][x] == '.') {
|
|
||||||
entry_point[1] = y - 1;
|
|
||||||
} else {
|
|
||||||
entry_point[1] = y + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
portals[entry_point] = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return portals;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<point_t, std::vector<std::pair<int, point_t>>>
|
|
||||||
get_implicit_graph(const map_t &map, const std::unordered_map<point_t, std::string> &portals) {
|
|
||||||
std::unordered_map<std::string, point_t> half_links;
|
|
||||||
|
|
||||||
std::unordered_map<point_t, std::vector<std::pair<int, point_t>>> graph;
|
|
||||||
|
|
||||||
for (auto &entry : portals) {
|
|
||||||
if (auto it = half_links.find(entry.second); it != half_links.end()) {
|
|
||||||
// Connect up the portals
|
|
||||||
graph[it->second].emplace_back(1, entry.first);
|
|
||||||
graph[entry.first].emplace_back(1, it->second);
|
|
||||||
} else {
|
|
||||||
half_links[entry.second] = entry.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a BFS from the node to see what we can reach.
|
|
||||||
std::deque<std::pair<int, point_t>> todo{{0, entry.first}};
|
|
||||||
std::unordered_set<point_t> visited{entry.first};
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, pos] = todo.front();
|
|
||||||
todo.pop_front();
|
|
||||||
|
|
||||||
for (auto &direction : DIRECTIONS) {
|
|
||||||
auto next_pos = pos + direction;
|
|
||||||
|
|
||||||
if (map[next_pos[1]][next_pos[0]] != '.' || visited.count(next_pos)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portals.count(next_pos)) {
|
|
||||||
graph[entry.first].emplace_back(dist + 1, next_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
todo.emplace_back(dist + 1, next_pos);
|
|
||||||
visited.insert(next_pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return graph;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day20_part1(std::istream &input, std::ostream &output) {
|
|
||||||
const auto map = read_map(input);
|
|
||||||
const auto portals = get_portals(map);
|
|
||||||
|
|
||||||
const auto starting_point = std::find_if(portals.begin(), portals.end(), [](auto &x) {
|
|
||||||
return x.second == "AA";
|
|
||||||
})->first;
|
|
||||||
|
|
||||||
auto graph = get_implicit_graph(map, portals);
|
|
||||||
|
|
||||||
std::unordered_set<point_t> visited;
|
|
||||||
std::priority_queue<std::pair<int, point_t>, std::vector<std::pair<int, point_t>>, std::greater<>> todo;
|
|
||||||
todo.emplace(0, starting_point);
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, pos] = todo.top();
|
|
||||||
todo.pop();
|
|
||||||
|
|
||||||
if (visited.count(pos)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.insert(pos);
|
|
||||||
|
|
||||||
if (portals.at(pos) == "ZZ") {
|
|
||||||
output << dist << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &edge : graph[pos]) {
|
|
||||||
if (visited.count(edge.second)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
todo.emplace(dist + edge.first, edge.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::domain_error("No valid route.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day20_part2(std::istream &input, std::ostream &output) {
|
|
||||||
const auto map = read_map(input);
|
|
||||||
const auto portals = get_portals(map);
|
|
||||||
|
|
||||||
using state_t = std::pair<int, point_t>;
|
|
||||||
|
|
||||||
const auto starting_point = std::find_if(portals.begin(), portals.end(), [](auto &x) {
|
|
||||||
return x.second == "AA";
|
|
||||||
})->first;
|
|
||||||
|
|
||||||
auto graph = get_implicit_graph(map, portals);
|
|
||||||
|
|
||||||
std::set<state_t> visited;
|
|
||||||
std::priority_queue<std::tuple<int, int, point_t>, std::vector<std::tuple<int, int, point_t>>, std::greater<>> todo;
|
|
||||||
todo.emplace(0, 0, starting_point);
|
|
||||||
|
|
||||||
const int outer_x_min = 2;
|
|
||||||
const int outer_x_max = map[0].size() - 3;
|
|
||||||
const int outer_y_min = 2;
|
|
||||||
const int outer_y_max = map.size() - 3;
|
|
||||||
|
|
||||||
while (!todo.empty()) {
|
|
||||||
const auto[dist, level, pos] = todo.top();
|
|
||||||
todo.pop();
|
|
||||||
|
|
||||||
if (visited.count({level, pos})) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
visited.emplace(level, pos);
|
|
||||||
|
|
||||||
if (level == 0 && portals.at(pos) == "ZZ") {
|
|
||||||
output << dist << std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &edge : graph[pos]) {
|
|
||||||
int mod = 0;
|
|
||||||
if (edge.first == 1) {
|
|
||||||
// Taking a portal, determine which level we're going to
|
|
||||||
if (pos[0] == outer_x_max || pos[0] == outer_x_min || pos[1] == outer_y_max || pos[1] == outer_y_min) {
|
|
||||||
mod = -1;
|
|
||||||
} else {
|
|
||||||
mod = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level + mod < 0 || visited.count({level + mod, edge.second})) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
todo.emplace(dist + edge.first, level + mod, edge.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw std::domain_error("No valid route.");
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
void solve(std::istream &input, std::string_view program, std::ostream &output) {
|
|
||||||
aoc2019::IntCodeComputer computer(input);
|
|
||||||
std::deque<std::int64_t> output_buffer;
|
|
||||||
computer.connectOutput(output_buffer);
|
|
||||||
computer.run();
|
|
||||||
output_buffer.clear();
|
|
||||||
|
|
||||||
computer.sendInputs(program);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
if (output_buffer.back() < 127) {
|
|
||||||
for (char c : output_buffer) {
|
|
||||||
output << c;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
output << output_buffer.back() << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day21_part1(std::istream &input, std::ostream &output) {
|
|
||||||
std::string_view program = "OR A J\n" // Check if any of the next 3 places is a hole
|
|
||||||
"AND B J\n"
|
|
||||||
"AND C J\n"
|
|
||||||
"NOT J J\n"
|
|
||||||
"AND D J\n" // Jump if the landing space is clear
|
|
||||||
"WALK\n";
|
|
||||||
|
|
||||||
solve(input, program, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day21_part2(std::istream &input, std::ostream &output) {
|
|
||||||
std::string_view program = "NOT H J\n" // If you can safely jump twice
|
|
||||||
"OR C J\n" // And either of the next 3 places contains a hole
|
|
||||||
"AND A J\n"
|
|
||||||
"AND B J\n"
|
|
||||||
"NOT J J\n"
|
|
||||||
"AND D J\n" // And we can land our first jump, then jump.
|
|
||||||
"RUN\n";
|
|
||||||
|
|
||||||
solve(input, program, output);
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
enum class Operation {
|
|
||||||
Stack,
|
|
||||||
Deal,
|
|
||||||
Cut
|
|
||||||
};
|
|
||||||
|
|
||||||
using Move = std::pair<Operation, int>;
|
|
||||||
|
|
||||||
std::vector<Move> read_moves(std::istream &input) {
|
|
||||||
std::string buffer;
|
|
||||||
std::vector<Move> moves;
|
|
||||||
|
|
||||||
while (std::getline(input, buffer)) {
|
|
||||||
std::string_view line = buffer;
|
|
||||||
if (!line.find("deal into new stack")) {
|
|
||||||
moves.emplace_back(Operation::Stack, 0);
|
|
||||||
} else if (!line.find("deal with increment ")) {
|
|
||||||
int new_increment;
|
|
||||||
aoc2019::from_chars(line.substr(20), new_increment);
|
|
||||||
moves.emplace_back(Operation::Deal, new_increment);
|
|
||||||
} else {
|
|
||||||
// cut
|
|
||||||
int new_offset;
|
|
||||||
aoc2019::from_chars(line.substr(4), new_offset);
|
|
||||||
|
|
||||||
moves.emplace_back(Operation::Cut, new_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return moves;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::int64_t mmi(std::int64_t a, std::int64_t n) {
|
|
||||||
std::int64_t t = 0, newt = 1, r = n, newr = a;
|
|
||||||
|
|
||||||
while (newr != 0) {
|
|
||||||
auto q = r / newr;
|
|
||||||
// Poor man's simultaneous assignment
|
|
||||||
std::tie(t, newt) = std::make_tuple(newt, t - q * newt);
|
|
||||||
std::tie(r, newr) = std::make_tuple(newr, r - q * newr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r > 1) {
|
|
||||||
throw std::invalid_argument("Not invertible.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t < 0) t += n;
|
|
||||||
|
|
||||||
assert((t * a) % n == 1);
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::pair<std::int64_t, std::int64_t> pow(std::int64_t a, std::int64_t b, std::int64_t n, const std::int64_t M) {
|
|
||||||
__int128 ra = 0, rb = 0;
|
|
||||||
|
|
||||||
while (n > 0) {
|
|
||||||
if (n % 2) {
|
|
||||||
ra = (ra + a) % M;
|
|
||||||
rb = (rb + b) % M;
|
|
||||||
}
|
|
||||||
|
|
||||||
// f(x) = ax + b
|
|
||||||
// f(f(x)) = a(ax + b) + b
|
|
||||||
// = aax + ab + b
|
|
||||||
__int128 na = a * (__int128) a;
|
|
||||||
__int128 nb = b * (__int128) a + b;
|
|
||||||
|
|
||||||
a = na % M;
|
|
||||||
b = nb % M;
|
|
||||||
|
|
||||||
n /= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {ra, rb};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day22_part1(std::istream &input, std::ostream &output) {
|
|
||||||
constexpr int DECK_SIZE = 10007;
|
|
||||||
|
|
||||||
int pos = 2019;
|
|
||||||
|
|
||||||
for (auto move : read_moves(input)) {
|
|
||||||
int argument = move.second;
|
|
||||||
switch (move.first) {
|
|
||||||
case Operation::Stack:
|
|
||||||
pos = DECK_SIZE - 1 - pos;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Operation::Deal:
|
|
||||||
pos = pos * argument % DECK_SIZE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Operation::Cut:
|
|
||||||
pos = (pos - argument) % DECK_SIZE;
|
|
||||||
if (pos < 0) pos += DECK_SIZE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output << pos << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day22_part2(std::istream &input, std::ostream &output) {
|
|
||||||
constexpr std::int64_t DECK_SIZE = 119315717514047;
|
|
||||||
constexpr std::int64_t SHUFFLES = 101741582076661;
|
|
||||||
|
|
||||||
assert(mmi(3, 11) == 4);
|
|
||||||
|
|
||||||
std::int64_t a = 1, b = 0;
|
|
||||||
|
|
||||||
for (auto move : read_moves(input)) {
|
|
||||||
std::int64_t argument = move.second;
|
|
||||||
switch (move.first) {
|
|
||||||
case Operation::Stack:
|
|
||||||
a = -a;
|
|
||||||
b = DECK_SIZE - b - 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Operation::Cut:
|
|
||||||
b = (b + argument) % DECK_SIZE;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Operation::Deal:
|
|
||||||
__int128 inv = mmi(argument, DECK_SIZE);
|
|
||||||
a = (a * inv) % DECK_SIZE;
|
|
||||||
b = (b * inv) % DECK_SIZE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto[ra, rb] = pow(a, b, SHUFFLES, DECK_SIZE);
|
|
||||||
|
|
||||||
output << ra << ',' << rb << std::endl;
|
|
||||||
|
|
||||||
auto result = (2020 * ra + rb) % DECK_SIZE;
|
|
||||||
if (result < 0) {
|
|
||||||
result += DECK_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
output << result << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
void aoc2019::day23_part1(std::istream &input, std::ostream &output) {
|
|
||||||
output << "Not implemented\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day23_part2(std::istream &input, std::ostream &output) {
|
|
||||||
output << "Not implemented\n";
|
|
||||||
}
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <set>
|
|
||||||
#include <numeric>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
using field_t = std::array<std::array<bool, 5>, 5>;
|
|
||||||
|
|
||||||
constexpr int EDGE = 4;
|
|
||||||
constexpr int MIDPOINT = 2;
|
|
||||||
|
|
||||||
field_t read_input(std::istream &input) {
|
|
||||||
std::string buffer;
|
|
||||||
field_t map;
|
|
||||||
|
|
||||||
int y = 0;
|
|
||||||
|
|
||||||
while (std::getline(input, buffer)) {
|
|
||||||
auto &row = map[y++];
|
|
||||||
|
|
||||||
std::transform(buffer.begin(), buffer.end(), row.begin(), [](char c) { return c == '#'; });
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
void next_gen(const field_t &source, field_t &sink) {
|
|
||||||
for (int y = 0; y < source.size(); ++y) {
|
|
||||||
for (int x = 0; x < source[y].size(); ++x) {
|
|
||||||
int neighbours = source[y][x] ? -1 : 0;
|
|
||||||
for (int dy = -1; dy <= 1; ++dy) {
|
|
||||||
if (dy + y < 0 || dy + y >= source.size()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int dx = -1; dx <= 1; ++dx) {
|
|
||||||
if (dx + x < 0 || dx + x >= source[y].size() || dx * dy) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
neighbours += source[y + dy][x + dx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sink[y][x] = neighbours == 1 || (!source[y][x] && neighbours == 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int num_bees(const field_t &field) {
|
|
||||||
int total = 0;
|
|
||||||
for (auto &row : field) {
|
|
||||||
total += std::count(row.begin(), row.end(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<int, field_t> advance(const std::unordered_map<int, field_t> &state) {
|
|
||||||
const auto dimension_range = std::minmax_element(state.begin(), state.end());
|
|
||||||
const auto min = dimension_range.first->first - 1;
|
|
||||||
const auto max = dimension_range.second->first + 1;
|
|
||||||
|
|
||||||
std::unordered_map<int, field_t> next_gen;
|
|
||||||
|
|
||||||
auto has_bee = [&state](int dimension, int x, int y) {
|
|
||||||
if (auto it = state.find(dimension); it != state.end()) {
|
|
||||||
return it->second[y][x];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int dimension = min; dimension <= max; ++dimension) {
|
|
||||||
field_t field{};
|
|
||||||
if (auto it = state.find(dimension); it != state.end()) {
|
|
||||||
field = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto get_neighbours = [has_bee,dimension](int x, int y) {
|
|
||||||
int neighbours = 0;
|
|
||||||
|
|
||||||
// Cell above
|
|
||||||
if (y == 0) {
|
|
||||||
neighbours += has_bee(dimension + 1, MIDPOINT, 1);
|
|
||||||
} else if (y == 3 && x == MIDPOINT) {
|
|
||||||
for (int sx = 0; sx < 5; ++sx) {
|
|
||||||
neighbours += has_bee(dimension - 1, sx, EDGE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neighbours += has_bee(dimension, x, y - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cell below
|
|
||||||
if (y == EDGE) {
|
|
||||||
neighbours += has_bee(dimension + 1, MIDPOINT, 3);
|
|
||||||
} else if (y == 1 && x == MIDPOINT) {
|
|
||||||
for (int sx = 0; sx < 5; ++sx) {
|
|
||||||
neighbours += has_bee(dimension - 1, sx, 0);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neighbours += has_bee(dimension, x, y + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cell left
|
|
||||||
if (x == 0) {
|
|
||||||
neighbours += has_bee(dimension + 1, 1, 2);
|
|
||||||
} else if (x == 3 && y == MIDPOINT) {
|
|
||||||
for (int sy = 0; sy < 5; ++sy) {
|
|
||||||
neighbours += has_bee(dimension - 1, EDGE, sy);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neighbours += has_bee(dimension, x - 1, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cell right
|
|
||||||
if (x == EDGE) {
|
|
||||||
neighbours += has_bee(dimension + 1, 3, MIDPOINT);
|
|
||||||
} else if (x == 1 && y == MIDPOINT) {
|
|
||||||
for (int sy = 0; sy < 5; ++sy) {
|
|
||||||
neighbours += has_bee(dimension - 1, 0, sy);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
neighbours += has_bee(dimension, x + 1, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return neighbours;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
for (int y = 0; y < 5; ++y) {
|
|
||||||
for (int x = 0; x < 5; ++x) {
|
|
||||||
auto neighbours = get_neighbours(x, y);
|
|
||||||
field[y][x] = neighbours == 1 || (neighbours == 2 && !field[y][x]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't evolve the midpoint.
|
|
||||||
field[2][2] = false;
|
|
||||||
|
|
||||||
if (num_bees(field) || (dimension != min && dimension != max)) {
|
|
||||||
next_gen[dimension] = field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return next_gen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day24_part1(std::istream &input, std::ostream &output) {
|
|
||||||
auto map = read_input(input);
|
|
||||||
auto copy = map;
|
|
||||||
|
|
||||||
std::set<field_t> seen;
|
|
||||||
do {
|
|
||||||
seen.insert(map);
|
|
||||||
next_gen(map, copy);
|
|
||||||
std::swap(map, copy);
|
|
||||||
} while (!seen.count(map));
|
|
||||||
|
|
||||||
unsigned int pow = 1;
|
|
||||||
unsigned int diversity = 0;
|
|
||||||
for (auto &row : map) {
|
|
||||||
for (auto b : row) {
|
|
||||||
if (b) {
|
|
||||||
diversity += pow;
|
|
||||||
}
|
|
||||||
|
|
||||||
pow <<= 1u;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output << diversity << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day24_part2(std::istream &input, std::ostream &output) {
|
|
||||||
std::unordered_map<int, field_t> fields;
|
|
||||||
fields[0] = read_input(input);
|
|
||||||
|
|
||||||
for (int gen = 0; gen < 200; ++gen) {
|
|
||||||
fields = advance(fields);
|
|
||||||
}
|
|
||||||
|
|
||||||
int total = std::accumulate(fields.begin(), fields.end(), 0, [](auto cur, const auto &it) {
|
|
||||||
return cur + num_bees(it.second);
|
|
||||||
});
|
|
||||||
|
|
||||||
output << total << std::endl;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "days.hpp"
|
|
||||||
|
|
||||||
void aoc2019::day25_part1(std::istream &input, std::ostream &output) {
|
|
||||||
output << "Not implemented\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::day25_part2(std::istream &input, std::ostream &output) {
|
|
||||||
output << "Not implemented\n";
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
|
|
||||||
namespace aoc2019 {
|
|
||||||
// Declarations of all implemented days.
|
|
||||||
void day01_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day01_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day02_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day02_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day03_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day03_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day04_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day04_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day05_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day05_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day06_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day06_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day07_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day07_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day08_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day08_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day09_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day09_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day10_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day10_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day11_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day11_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day12_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day12_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day13_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day13_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day14_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day14_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day15_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day15_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day16_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day16_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day17_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day17_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day18_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day18_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day19_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day19_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day20_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day20_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day21_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day21_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day22_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day22_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day23_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day23_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day24_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day24_part2(std::istream &input, std::ostream &output);
|
|
||||||
void day25_part1(std::istream &input, std::ostream &output);
|
|
||||||
void day25_part2(std::istream &input, std::ostream &output);
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#include <array>
|
|
||||||
#include "days.hpp"
|
|
||||||
#include "implementations.hpp"
|
|
||||||
|
|
||||||
constexpr const std::array<std::array<aoc2019::solution_t, 2>, 25> SOLUTIONS = {{
|
|
||||||
{aoc2019::day01_part1, aoc2019::day01_part2},
|
|
||||||
{aoc2019::day02_part1, aoc2019::day02_part2},
|
|
||||||
{aoc2019::day03_part1, aoc2019::day03_part2},
|
|
||||||
{aoc2019::day04_part1, aoc2019::day04_part2},
|
|
||||||
{aoc2019::day05_part1, aoc2019::day05_part2},
|
|
||||||
{aoc2019::day06_part1, aoc2019::day06_part2},
|
|
||||||
{aoc2019::day07_part1, aoc2019::day07_part2},
|
|
||||||
{aoc2019::day08_part1, aoc2019::day08_part2},
|
|
||||||
{aoc2019::day09_part1, aoc2019::day09_part2},
|
|
||||||
{aoc2019::day10_part1, aoc2019::day10_part2},
|
|
||||||
{aoc2019::day11_part1, aoc2019::day11_part2},
|
|
||||||
{aoc2019::day12_part1, aoc2019::day12_part2},
|
|
||||||
{aoc2019::day13_part1, aoc2019::day13_part2},
|
|
||||||
{aoc2019::day14_part1, aoc2019::day14_part2},
|
|
||||||
{aoc2019::day15_part1, aoc2019::day15_part2},
|
|
||||||
{aoc2019::day16_part1, aoc2019::day16_part2},
|
|
||||||
{aoc2019::day17_part1, aoc2019::day17_part2},
|
|
||||||
{aoc2019::day18_part1, aoc2019::day18_part2},
|
|
||||||
{aoc2019::day19_part1, aoc2019::day19_part2},
|
|
||||||
{aoc2019::day20_part1, aoc2019::day20_part2},
|
|
||||||
{aoc2019::day21_part1, aoc2019::day21_part2},
|
|
||||||
{aoc2019::day22_part1, aoc2019::day22_part2},
|
|
||||||
{aoc2019::day23_part1, aoc2019::day23_part2},
|
|
||||||
{aoc2019::day24_part1, aoc2019::day24_part2},
|
|
||||||
{aoc2019::day25_part1, aoc2019::day25_part2},
|
|
||||||
}};
|
|
||||||
|
|
||||||
aoc2019::solution_t aoc2019::get_implementation(int day, bool part2) {
|
|
||||||
return SOLUTIONS.at(day - 1).at((int) part2);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iosfwd>
|
|
||||||
|
|
||||||
namespace aoc2019 {
|
|
||||||
typedef void (*solution_t)(std::istream &, std::ostream &);
|
|
||||||
|
|
||||||
solution_t get_implementation(int day, bool part2 = false);
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
namespace aoc2019 {
|
|
||||||
template<class T, std::size_t L>
|
|
||||||
class Point : public std::array<T, L> {
|
|
||||||
public:
|
|
||||||
constexpr Point& operator +=(Point other) {
|
|
||||||
for (std::size_t i = 0; i < L; ++i) {
|
|
||||||
(*this)[i] += other[i];
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Point operator+(Point other) const {
|
|
||||||
auto result = *this;
|
|
||||||
result += other;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Point& operator -=(Point other) {
|
|
||||||
for (std::size_t i = 0; i < L; ++i) {
|
|
||||||
(*this)[i] -= other[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr Point operator-(Point other) const {
|
|
||||||
auto result = *this;
|
|
||||||
result -= other;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr T l1() const {
|
|
||||||
T result = 0;
|
|
||||||
for (auto e : *this) {
|
|
||||||
result += std::abs(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename ValueType, std::size_t N, typename Ignored>
|
|
||||||
std::pair<Point<ValueType, N>, Point<ValueType, N>> bounding_box(const std::unordered_map<Point<ValueType, N>, Ignored> &data) {
|
|
||||||
Point<ValueType, N> lower, upper;
|
|
||||||
std::fill(lower.begin(), lower.end(), std::numeric_limits<ValueType>::max());
|
|
||||||
std::fill(upper.begin(), upper.end(), std::numeric_limits<ValueType>::min());
|
|
||||||
|
|
||||||
for (auto &entry : data) {
|
|
||||||
for (int i = 0; i < N; ++i) {
|
|
||||||
lower[i] = std::min(entry.first[i], lower[i]);
|
|
||||||
upper[i] = std::max(entry.first[i], upper[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {lower, upper};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
// Make point usable with unordered collections.
|
|
||||||
template<class T, std::size_t L> struct hash<aoc2019::Point<T, L>> {
|
|
||||||
size_t operator()(const aoc2019::Point<T, L> &o) const {
|
|
||||||
size_t seed = 0;
|
|
||||||
for (auto i : o) {
|
|
||||||
aoc2019::combine_hash(seed, i);
|
|
||||||
}
|
|
||||||
return seed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
#include "implementations.hpp"
|
|
||||||
#include <charconv>
|
|
||||||
#include <chrono>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string_view>
|
|
||||||
#include <optional>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
struct AoCOptions {
|
|
||||||
aoc2019::solution_t implementation;
|
|
||||||
bool run_timer;
|
|
||||||
std::optional<std::ifstream> input_file;
|
|
||||||
};
|
|
||||||
|
|
||||||
static AoCOptions parse_options(const int argc, const char* argv[]) {
|
|
||||||
using namespace std::literals;
|
|
||||||
AoCOptions options{};
|
|
||||||
|
|
||||||
auto show_help = [argv] (int exit_status = 0) {
|
|
||||||
std::cerr << "Usage: " << argv[0] << " [--timer|-t] [--part2|-2] [--help|-h] DAY\n"
|
|
||||||
<< "\t--timer|-t: print execution time\n"
|
|
||||||
<< "\t--input ARG|-fARG: use given input file as puzzle input"
|
|
||||||
<< "\t--part2|-2: run part 2\n"
|
|
||||||
<< "\t --help|-h: show this message\n";
|
|
||||||
std::exit(exit_status);
|
|
||||||
};
|
|
||||||
|
|
||||||
int day = -1;
|
|
||||||
bool part2 = false;
|
|
||||||
|
|
||||||
// Here follows a manual implementation of getopt, since getopt doesn't work on windows…
|
|
||||||
for (int i = 1; i < argc; ++i) {
|
|
||||||
std::string_view arg(argv[i]);
|
|
||||||
if (arg[0] == '-') {
|
|
||||||
// Handle flag arguments
|
|
||||||
if (arg[1] != '-') {
|
|
||||||
// Shorthand flags
|
|
||||||
for (int j = 1; j < arg.size(); ++j) {
|
|
||||||
switch (arg[j]) {
|
|
||||||
case '2':
|
|
||||||
part2 = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 't':
|
|
||||||
options.run_timer = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'h':
|
|
||||||
show_help();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'f':
|
|
||||||
if (j == arg.size() - 1) {
|
|
||||||
if (i == argc - 1) {
|
|
||||||
std::cerr << "Option -f requires an argument.";
|
|
||||||
show_help(1);
|
|
||||||
} else {
|
|
||||||
options.input_file = std::ifstream(argv[i + 1]);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
options.input_file = std::ifstream(std::string(arg.substr(j)));
|
|
||||||
j = arg.size();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
std::cerr << "Unknown flag '" << arg[j] << "'.\n\n";
|
|
||||||
show_help(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle long form versions
|
|
||||||
if (arg == "--timer"sv) {
|
|
||||||
part2 = true;
|
|
||||||
} else if (arg == "--timer"sv) {
|
|
||||||
options.run_timer = true;
|
|
||||||
} else if (arg == "--help"sv) {
|
|
||||||
show_help();
|
|
||||||
} else if (arg == "--input"sv) {
|
|
||||||
if (i == argc - 1) {
|
|
||||||
std::cerr << "Option -f requires an argument.";
|
|
||||||
show_help(1);
|
|
||||||
} else {
|
|
||||||
options.input_file = std::ifstream(argv[i + 1]);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
show_help(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (day != -1) {
|
|
||||||
// Double date specification, bail.
|
|
||||||
show_help(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse the date number
|
|
||||||
if (auto res = std::from_chars(arg.data(), arg.data() + arg.size(), day); res.ec != std::errc()) {
|
|
||||||
auto error_code = std::make_error_code(res.ec);
|
|
||||||
std::cerr << error_code.message() << "\n\n";
|
|
||||||
show_help(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (day == -1) {
|
|
||||||
std::cerr << "Argument DAY is required.\n\n";
|
|
||||||
show_help(1);
|
|
||||||
} else if (day < 1 || day > 25) {
|
|
||||||
std::cerr << "Invalid day. Valid range: [1, 25]\n";
|
|
||||||
show_help(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.implementation = aoc2019::get_implementation(day, part2);
|
|
||||||
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
|
||||||
auto options = parse_options(argc, argv);
|
|
||||||
|
|
||||||
if (options.implementation != nullptr) {
|
|
||||||
const auto start = std::chrono::high_resolution_clock::now();
|
|
||||||
options.implementation(options.input_file ? *options.input_file : std::cin, std::cout);
|
|
||||||
if (options.run_timer) {
|
|
||||||
const std::chrono::duration<double> duration = std::chrono::high_resolution_clock::now() - start;
|
|
||||||
std::cerr << "Time taken: " << duration.count() << "s\n";
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
std::cerr << "Unimplemented.\n";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include "utils.hpp"
|
|
||||||
|
|
||||||
std::string_view aoc2019::strtok(std::string_view &str, char token) {
|
|
||||||
auto next_delim = str.find(token);
|
|
||||||
auto next = str.substr(0, next_delim);
|
|
||||||
if (next_delim == std::string_view::npos) {
|
|
||||||
str = {};
|
|
||||||
} else {
|
|
||||||
str = str.substr(next_delim + 1);
|
|
||||||
}
|
|
||||||
return next;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::deque<int64_t> aoc2019::run_intcode(std::vector<int64_t> program, std::deque<int64_t> inputs) {
|
|
||||||
std::deque<std::int64_t> outputs;
|
|
||||||
IntCodeComputer computer(std::move(program), std::move(inputs));
|
|
||||||
computer.connectOutput(outputs);
|
|
||||||
|
|
||||||
computer.run();
|
|
||||||
|
|
||||||
return outputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::interpret_value(int pos) {
|
|
||||||
value_t immediate;
|
|
||||||
switch (pos) {
|
|
||||||
case 1:
|
|
||||||
immediate = program[ip] / 100 % 10;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
immediate = program[ip] / 1000 % 10;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
immediate = program[ip] / 10000 % 10;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw std::out_of_range("Invalid position");
|
|
||||||
}
|
|
||||||
|
|
||||||
value_t index;
|
|
||||||
|
|
||||||
switch (immediate) {
|
|
||||||
case 0:
|
|
||||||
index = program[ip + pos];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
index = ip + pos;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
index = program[ip + pos] + relative;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw std::out_of_range("Invalid mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (program.size() <= index) {
|
|
||||||
program.resize(index + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return program[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::IntCodeComputer::connectOutput(aoc2019::IntCodeComputer &computer) {
|
|
||||||
outputSink = &computer.inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::IntCodeComputer::connectOutput(std::deque<value_t> &sink) {
|
|
||||||
outputSink = &sink;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool aoc2019::IntCodeComputer::isTerminated() const {
|
|
||||||
return halted;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::deque<aoc2019::IntCodeComputer::value_t> &aoc2019::IntCodeComputer::currentInputs() const {
|
|
||||||
return inputs;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<aoc2019::IntCodeComputer::value_t> aoc2019::IntCodeComputer::read_intcode(std::istream &input) {
|
|
||||||
std::vector<value_t> program;
|
|
||||||
for (value_t current; input >> current; input.ignore()) {
|
|
||||||
program.push_back(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::IntCodeComputer::run() {
|
|
||||||
while (ip < program.size()) {
|
|
||||||
switch (program[ip] % 100) {
|
|
||||||
case 1:
|
|
||||||
interpret_value(3) = interpret_value(1) + interpret_value(2);
|
|
||||||
ip += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
interpret_value(3) = interpret_value(1) * interpret_value(2);
|
|
||||||
ip += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
if (inputs.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
interpret_value(1) = inputs.front();
|
|
||||||
inputs.pop_front();
|
|
||||||
ip += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
outputSink->push_back(interpret_value(1));
|
|
||||||
ip += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5: // Jump if non-zero
|
|
||||||
if (interpret_value(1)) {
|
|
||||||
ip = interpret_value(2);
|
|
||||||
} else {
|
|
||||||
ip += 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6: // Jump if zero
|
|
||||||
if (!interpret_value(1)) {
|
|
||||||
ip = interpret_value(2);
|
|
||||||
} else {
|
|
||||||
ip += 3;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 7: // less than
|
|
||||||
interpret_value(3) = interpret_value(1) < interpret_value(2);
|
|
||||||
ip += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 8: // equality
|
|
||||||
interpret_value(3) = interpret_value(1) == interpret_value(2) ? 1 : 0;
|
|
||||||
ip += 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 9:
|
|
||||||
relative += interpret_value(1);
|
|
||||||
ip += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 99:
|
|
||||||
halted = true;
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
char buffer[30];
|
|
||||||
std::snprintf(buffer, sizeof(buffer), "Invalid opcode: %d", program[ip]);
|
|
||||||
|
|
||||||
throw std::domain_error(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer::IntCodeComputer(std::vector<value_t> program, std::deque<value_t> initial_inputs) :
|
|
||||||
program{std::move(program)}, inputs{std::move(initial_inputs)} {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer::IntCodeComputer(std::istream &program_stream, std::deque<value_t> initial_inputs) :
|
|
||||||
program(read_intcode(program_stream)), inputs(std::move(initial_inputs)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::IntCodeComputer::sendInput(aoc2019::IntCodeComputer::value_t input) {
|
|
||||||
inputs.push_back(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::operator[](std::size_t index) {
|
|
||||||
return program[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const aoc2019::IntCodeComputer::value_t &aoc2019::IntCodeComputer::operator[](std::size_t index) const {
|
|
||||||
return program[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void aoc2019::IntCodeComputer::sendInputs(std::string_view str) {
|
|
||||||
for (char c : str) {
|
|
||||||
sendInput(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <charconv>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
|
||||||
#include <iosfwd>
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace aoc2019 {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline std::from_chars_result from_chars(std::string_view str, T &value) {
|
|
||||||
return std::from_chars(str.data(), str.data() + str.size(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void combine_hash(std::size_t &seed, const T &o) {
|
|
||||||
// Algorithm taken from boost::combine_hash.
|
|
||||||
std::hash<T> hash{};
|
|
||||||
seed ^= hash(o) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename ValueType, typename OutputIt>
|
|
||||||
std::istream &read_line_numbers_and_garbage(std::istream &input, OutputIt output) {
|
|
||||||
ValueType v;
|
|
||||||
char c;
|
|
||||||
while (input && (c = input.peek()) != '\n') {
|
|
||||||
if (c == '-' || std::isdigit(c)) {
|
|
||||||
input >> v;
|
|
||||||
*output = v;
|
|
||||||
++output;
|
|
||||||
} else {
|
|
||||||
input.ignore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
input.get();
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string_view strtok(std::string_view &str, char token = ',');
|
|
||||||
|
|
||||||
std::deque<int64_t> run_intcode(std::vector<std::int64_t> program, std::deque<std::int64_t> inputs = {});
|
|
||||||
|
|
||||||
template<class Node>
|
|
||||||
std::vector<Node> topological_sort(const std::unordered_map<Node, std::vector<Node>> &edge_list) {
|
|
||||||
std::unordered_map<Node, int> incoming_edges;
|
|
||||||
|
|
||||||
for (auto &entry : edge_list) {
|
|
||||||
// Ensure entry for parent exist
|
|
||||||
incoming_edges[entry.first] += 0;
|
|
||||||
|
|
||||||
for (auto &node : entry.second) {
|
|
||||||
incoming_edges[node]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Node> order;
|
|
||||||
std::deque<Node> childless;
|
|
||||||
|
|
||||||
for (auto &entry : incoming_edges) {
|
|
||||||
if (!entry.second) {
|
|
||||||
childless.push_back(entry.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!childless.empty()) {
|
|
||||||
auto current = childless.front();
|
|
||||||
childless.pop_front();
|
|
||||||
order.emplace_back(current);
|
|
||||||
|
|
||||||
if (auto it = edge_list.find(current); it != edge_list.end()) {
|
|
||||||
for (const auto &parent : it->second) {
|
|
||||||
if (--incoming_edges[parent] == 0) {
|
|
||||||
childless.push_back(parent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (order.size() != incoming_edges.size()) {
|
|
||||||
throw std::domain_error("Not a DAG.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
|
|
||||||
class IntCodeComputer {
|
|
||||||
public:
|
|
||||||
typedef std::int64_t value_t;
|
|
||||||
|
|
||||||
explicit IntCodeComputer(std::vector<value_t> program, std::deque<value_t> initial_inputs = {});
|
|
||||||
explicit IntCodeComputer(std::istream &program_stream, std::deque<value_t> initial_inputs = {});
|
|
||||||
|
|
||||||
void run();
|
|
||||||
void connectOutput(IntCodeComputer &computer);
|
|
||||||
void connectOutput(std::deque<value_t> &sink);
|
|
||||||
void sendInput(value_t input);
|
|
||||||
|
|
||||||
void sendInputs(std::string_view str);
|
|
||||||
|
|
||||||
[[nodiscard]] bool isTerminated() const;
|
|
||||||
|
|
||||||
[[nodiscard]] const std::deque<value_t> ¤tInputs() const;
|
|
||||||
|
|
||||||
value_t &operator[](std::size_t index);
|
|
||||||
const value_t &operator[](std::size_t index) const;
|
|
||||||
|
|
||||||
static std::vector<value_t> read_intcode(std::istream &input);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<value_t> program;
|
|
||||||
std::deque<value_t> inputs = {};
|
|
||||||
std::deque<value_t> *outputSink = nullptr;
|
|
||||||
int ip = 0;
|
|
||||||
int relative = 0;
|
|
||||||
bool halted = false;
|
|
||||||
|
|
||||||
[[nodiscard]] value_t &interpret_value(int pos);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
0
2019/tests/__init__.py
Normal file
0
2019/tests/__init__.py
Normal file
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
100756
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
33583
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
R75,D30,R83,U83,L12,D49,R71,U7,L72
|
|
||||||
U62,R66,U55,R34,D71,R55,D58,R83
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
159
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51
|
|
||||||
U98,R91,D20,R16,D67,R40,U7,R15,U6,R7
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
135
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
R8,U5,L5,D3
|
|
||||||
U7,R6,D4,L4
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
6
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
03-1-1.in
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
610
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
03-1-2.in
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
410
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
03-1-3.in
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
30
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
COM)B
|
|
||||||
B)C
|
|
||||||
C)D
|
|
||||||
D)E
|
|
||||||
E)F
|
|
||||||
B)G
|
|
||||||
G)H
|
|
||||||
D)I
|
|
||||||
E)J
|
|
||||||
J)K
|
|
||||||
K)L
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
42
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
COM)B
|
|
||||||
B)C
|
|
||||||
C)D
|
|
||||||
D)E
|
|
||||||
E)F
|
|
||||||
B)G
|
|
||||||
G)H
|
|
||||||
D)I
|
|
||||||
E)J
|
|
||||||
J)K
|
|
||||||
K)L
|
|
||||||
K)YOU
|
|
||||||
I)SAN
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
4
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
43210
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
3,23,3,24,1002,24,10,24,1002,23,-1,23,
|
|
||||||
101,5,23,23,1,24,23,23,4,23,99,0,0
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
54321
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
3,31,3,32,1002,32,10,32,1001,31,-2,31,1007,31,0,33,
|
|
||||||
1002,33,7,33,1,33,31,31,1,32,31,31,4,31,99,0,0,0
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
65210
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,
|
|
||||||
27,4,27,1001,28,-1,28,1005,28,6,99,0,0,5
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user