├── .activate.sh ├── .deactivate.sh ├── .eslintrc.json ├── .gitignore ├── .pre-commit-config.yaml ├── .rubocop.yml ├── README.md ├── day00 ├── __init__.py └── template.py ├── day01 ├── __init__.py ├── input.txt ├── part1.cpp ├── part1.py ├── part2.cpp └── part2.py ├── day02 ├── __init__.py ├── input.txt ├── part1.c ├── part1.js ├── part1.py ├── part1.sqlite.sql ├── part2.c ├── part2.js └── part2.py ├── day03 ├── __init__.py ├── input.txt ├── part1.py ├── part1.sh ├── part2.py └── part2.sh ├── day04 ├── __init__.py ├── input.txt ├── part1.java ├── part1.py ├── part1.sqlite.sql ├── part2.java ├── part2.py └── part2.sqlite.sql ├── day05 ├── Cargo.lock ├── Cargo.toml ├── __init__.py ├── input.txt ├── input2.txt ├── part1.js ├── part1.py ├── part1.sqlite.sql ├── part2.js ├── part2.py ├── part2.sqlite.sql └── src │ └── bin │ ├── part1.rs │ └── part2.rs ├── day06 ├── __init__.py ├── input.txt ├── part1.pl ├── part1.py ├── part2.pl └── part2.py ├── day07 ├── __init__.py ├── input.txt ├── part1.js ├── part1.py ├── part2.js └── part2.py ├── day08 ├── __init__.py ├── input.txt ├── make_intcode_program.py ├── part1.intcode ├── part1.py ├── part2.lua └── part2.py ├── day09 ├── __init__.py ├── input.txt ├── intdis.py ├── part1.nim ├── part1.py ├── part1.sqlite.sql ├── part2.nim ├── part2.py └── part2.sqlite.sql ├── day10 ├── __init__.py ├── input.txt ├── part1.ps1 ├── part1.py ├── part2.ps1 └── part2.py ├── day11 ├── __init__.py ├── input.txt ├── part1.py ├── part1.swift ├── part2.py └── part2.swift ├── day12 ├── __init__.py ├── input.txt ├── part1.kt ├── part1.py ├── part2.kt └── part2.py ├── day13 ├── __init__.py ├── input.txt ├── part1.py ├── part1.rb ├── part2.py └── part2.rb ├── day14 ├── __init__.py ├── input.txt ├── part1.py ├── part1.py0.py ├── part2.py └── part2.py0.py ├── day15 ├── __init__.py ├── input.txt ├── part1.go ├── part1.py ├── part2.go └── part2.py ├── day16 ├── __init__.py ├── input.txt ├── part1.cr ├── part1.py ├── part2.cr └── part2.py ├── day17 ├── __init__.py ├── input.txt ├── part1.php ├── part1.py ├── part2.php └── part2.py ├── day18 ├── __init__.py ├── input.txt ├── part1.py └── part2.py ├── day19 ├── __init__.py ├── input.txt ├── part1.py └── part2.py ├── day20 ├── __init__.py ├── input.txt ├── part1.py └── part2.py ├── day21 ├── __init__.py ├── input.txt ├── main.py ├── part1.py └── part2.py ├── day22 ├── __init__.py ├── input.txt ├── part1.py └── part2.py ├── day23 ├── input.txt ├── part1.py └── part2.py ├── day24 ├── __init__.py ├── input.txt ├── part1.py └── part2.py ├── day25 ├── __init__.py ├── input.txt └── part1.py ├── requirements.txt ├── setup.cfg └── support ├── setup.cfg ├── setup.py └── support.py /.activate.sh: -------------------------------------------------------------------------------- 1 | venv/bin/activate -------------------------------------------------------------------------------- /.deactivate.sh: -------------------------------------------------------------------------------- 1 | deactivate 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint:recommended", 3 | "parserOptions": { 4 | "ecmaVersion": 2017 5 | }, 6 | "env": { 7 | "node": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.egg-info 2 | *.pyc 3 | */index.htm 4 | /.mypy_cache 5 | /venv 6 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: check-json 6 | - id: trailing-whitespace 7 | - id: end-of-file-fixer 8 | exclude: Cargo\.lock$ 9 | - id: check-yaml 10 | - repo: https://github.com/PyCQA/flake8 11 | rev: 4.0.1 12 | hooks: 13 | - id: flake8 14 | - repo: https://github.com/pre-commit/mirrors-autopep8 15 | rev: v1.5.7 16 | hooks: 17 | - id: autopep8 18 | - repo: https://github.com/asottile/reorder_python_imports 19 | rev: v2.6.0 20 | hooks: 21 | - id: reorder-python-imports 22 | args: [--py3-plus, '--application-directories=.:support'] 23 | - repo: https://github.com/asottile/pyupgrade 24 | rev: v2.29.0 25 | hooks: 26 | - id: pyupgrade 27 | args: [--py36-plus] 28 | exclude: \.py0\. 29 | - repo: https://github.com/pre-commit/mirrors-mypy 30 | rev: v0.910-1 31 | hooks: 32 | - id: mypy 33 | exclude: \.py0\. 34 | - repo: https://github.com/pre-commit/mirrors-eslint 35 | rev: v8.0.1 36 | hooks: 37 | - id: eslint 38 | args: [--fix] 39 | - repo: local 40 | hooks: 41 | - id: rubocop 42 | name: rubocop 43 | entry: rubocop --auto-correct 44 | types: [ruby] 45 | language: ruby 46 | additional_dependencies: ['rubocop:0.52.0'] 47 | - id: gofmt 48 | name: gofmt 49 | language: system 50 | entry: gofmt -l -w 51 | files: \.go$ 52 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Metrics/BlockLength: 2 | Enabled: false 3 | Metrics/BlockNesting: 4 | Enabled: false 5 | Metrics/MethodLength: 6 | Enabled: false 7 | -------------------------------------------------------------------------------- /day00/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day00/__init__.py -------------------------------------------------------------------------------- /day00/template.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import pytest 4 | 5 | from support import timing 6 | 7 | 8 | def compute(s: str) -> int: 9 | # TODO: implement solution here! 10 | return 0 11 | 12 | 13 | @pytest.mark.parametrize( 14 | ('input_s', 'expected'), 15 | ( 16 | # put given test cases here 17 | ), 18 | ) 19 | def test(input_s: str, expected: int) -> None: 20 | assert compute(input_s) == expected 21 | 22 | 23 | def main() -> int: 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument('data_file') 26 | args = parser.parse_args() 27 | 28 | with open(args.data_file) as f, timing(): 29 | print(compute(f.read())) 30 | 31 | return 0 32 | 33 | 34 | if __name__ == '__main__': 35 | exit(main()) 36 | -------------------------------------------------------------------------------- /day01/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day01/__init__.py -------------------------------------------------------------------------------- /day01/input.txt: -------------------------------------------------------------------------------- 1 | 120183 2 | 105169 3 | 58942 4 | 105283 5 | 100729 6 | 57778 7 | 59601 8 | 86011 9 | 148930 10 | 76533 11 | 132625 12 | 79105 13 | 66334 14 | 87695 15 | 148836 16 | 129384 17 | 71225 18 | 96010 19 | 67221 20 | 139037 21 | 90010 22 | 72531 23 | 145543 24 | 137983 25 | 63687 26 | 131307 27 | 62602 28 | 129223 29 | 76717 30 | 98896 31 | 58484 32 | 127996 33 | 128840 34 | 60723 35 | 149932 36 | 141443 37 | 96997 38 | 96196 39 | 104670 40 | 104055 41 | 129552 42 | 54426 43 | 104507 44 | 80241 45 | 91570 46 | 140053 47 | 106108 48 | 119792 49 | 133703 50 | 66387 51 | 129594 52 | 144794 53 | 91962 54 | 134610 55 | 97937 56 | 111599 57 | 77667 58 | 133644 59 | 89207 60 | 121935 61 | 80434 62 | 147413 63 | 94091 64 | 110244 65 | 59255 66 | 53071 67 | 133121 68 | 55972 69 | 122369 70 | 95605 71 | 142303 72 | 120242 73 | 113412 74 | 107519 75 | 88325 76 | 85243 77 | 104752 78 | 85418 79 | 101515 80 | 145236 81 | 107302 82 | 142970 83 | 87763 84 | 112798 85 | 105469 86 | 88303 87 | 91668 88 | 129187 89 | 115297 90 | 56238 91 | 69358 92 | 109148 93 | 99943 94 | 96480 95 | 98344 96 | 77777 97 | 98973 98 | 138814 99 | 106194 100 | 128739 101 | -------------------------------------------------------------------------------- /day01/part1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using std::cerr; 5 | using std::cout; 6 | using std::endl; 7 | using std::ifstream; 8 | 9 | 10 | int main(int argc, char* argv[]) { 11 | if (argc != 2) { 12 | cerr << "usage: " << argv[0] << " FILENAME" << endl; 13 | return 1; 14 | } 15 | 16 | ifstream f{argv[1]}; 17 | if (!f) { 18 | cerr << "could not open file: " << argv[1] << endl; 19 | return 1; 20 | } 21 | 22 | int total = 0; 23 | int n; 24 | while (f >> n) { 25 | total += n / 3 - 2; 26 | } 27 | cout << total << endl; 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /day01/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import pytest 4 | 5 | from support import timing 6 | 7 | """\ 8 | to find the fuel required for a module, take its mass, divide by three, 9 | round down, and subtract 2. 10 | 11 | For example: 12 | 13 | For a mass of 12, divide by 3 and round down to get 4, then subtract 2 to get 2 14 | For a mass of 14, dividing by 3 and rounding down still yields 4, so the fuel 15 | required is also 2. 16 | For a mass of 1969, the fuel required is 654. 17 | For a mass of 100756, the fuel required is 33583. 18 | """ 19 | 20 | 21 | def compute(s: str) -> int: 22 | total = 0 23 | for line in s.splitlines(): 24 | line_val = int(line) 25 | total += line_val // 3 - 2 26 | return total 27 | 28 | 29 | def compute_oneline(s: str) -> int: 30 | return sum(int(line) // 3 - 2 for line in s.splitlines()) 31 | 32 | 33 | @pytest.mark.parametrize( 34 | ('input_s', 'expected'), 35 | ( 36 | ('12', 2), 37 | ('14', 2), 38 | ('1969', 654), 39 | ('100756', 33583), 40 | ), 41 | ) 42 | def test(input_s: str, expected: int) -> None: 43 | assert compute(input_s) == expected 44 | 45 | 46 | def main() -> int: 47 | parser = argparse.ArgumentParser() 48 | parser.add_argument('data_file') 49 | args = parser.parse_args() 50 | 51 | with open(args.data_file) as f, timing('original'): 52 | print(compute(f.read())) 53 | 54 | with open(args.data_file) as f, timing('oneline'): 55 | print(compute_oneline(f.read())) 56 | 57 | return 0 58 | 59 | 60 | if __name__ == '__main__': 61 | exit(main()) 62 | -------------------------------------------------------------------------------- /day01/part2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using std::cerr; 5 | using std::cout; 6 | using std::endl; 7 | using std::ifstream; 8 | 9 | 10 | int fuelVal(int weight) { 11 | int val = weight / 3 - 2; 12 | if (val <= 0) { 13 | return 0; 14 | } else { 15 | return val + fuelVal(val); 16 | } 17 | } 18 | 19 | 20 | int main(int argc, char* argv[]) { 21 | if (argc != 2) { 22 | cerr << "usage: " << argv[0] << " FILENAME" << endl; 23 | return 1; 24 | } 25 | 26 | ifstream f{argv[1]}; 27 | if (!f) { 28 | cerr << "could not open file: " << argv[1] << endl; 29 | return 1; 30 | } 31 | 32 | int total = 0; 33 | int n; 34 | while (f >> n) { 35 | total += fuelVal(n); 36 | } 37 | cout << total << endl; 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /day01/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import pytest 4 | 5 | from support import timing 6 | 7 | 8 | def fuel_for_value(n: int) -> int: 9 | return max(n // 3 - 2, 0) 10 | 11 | 12 | def compute(s: str) -> int: 13 | total = 0 14 | for line in s.splitlines(): 15 | prev = int(line) 16 | while prev > 0: 17 | prev = fuel_for_value(prev) 18 | total += prev 19 | return total 20 | 21 | 22 | def _compute_recursive_inner(n: int) -> int: 23 | val = fuel_for_value(n) 24 | if val == 0: 25 | return val 26 | else: 27 | return val + _compute_recursive_inner(val) 28 | 29 | 30 | def compute_recursive(s: str) -> int: 31 | total = 0 32 | for line in s.splitlines(): 33 | total += _compute_recursive_inner(int(line)) 34 | return total 35 | 36 | 37 | @pytest.mark.parametrize( 38 | ('input_s', 'expected'), 39 | ( 40 | ('14', 2), 41 | ('1969', 966), 42 | ('100756', 50346), 43 | ), 44 | ) 45 | def test(input_s: str, expected: int) -> None: 46 | assert compute(input_s) == expected 47 | 48 | 49 | def main() -> int: 50 | parser = argparse.ArgumentParser() 51 | parser.add_argument('data_file') 52 | args = parser.parse_args() 53 | 54 | with open(args.data_file) as f, timing('original'): 55 | print(compute(f.read())) 56 | with open(args.data_file) as f, timing('recursive'): 57 | print(compute_recursive(f.read())) 58 | 59 | return 0 60 | 61 | 62 | if __name__ == '__main__': 63 | exit(main()) 64 | -------------------------------------------------------------------------------- /day02/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day02/__init__.py -------------------------------------------------------------------------------- /day02/input.txt: -------------------------------------------------------------------------------- 1 | 1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,9,1,19,1,19,6,23,2,6,23,27,2,27,9,31,1,5,31,35,1,35,10,39,2,39,9,43,1,5,43,47,2,47,10,51,1,51,6,55,1,5,55,59,2,6,59,63,2,63,6,67,1,5,67,71,1,71,9,75,2,75,10,79,1,79,5,83,1,10,83,87,1,5,87,91,2,13,91,95,1,95,10,99,2,99,13,103,1,103,5,107,1,107,13,111,2,111,9,115,1,6,115,119,2,119,6,123,1,123,6,127,1,127,9,131,1,6,131,135,1,135,2,139,1,139,10,0,99,2,0,14,0 2 | -------------------------------------------------------------------------------- /day02/part1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | long compute(long* prog) { 8 | size_t pc = 0; 9 | while (true) { 10 | long opc = prog[pc]; 11 | if (opc == 99) { 12 | return prog[0]; 13 | } else if (opc == 1) { 14 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]]; 15 | } else if (opc == 2) { 16 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]]; 17 | } else { 18 | assert(!"unreachable"); 19 | } 20 | pc += 4; 21 | } 22 | assert(!"unreachable"); 23 | } 24 | 25 | int main(int argc, char* argv[]) { 26 | if (argc != 2) { 27 | fprintf(stderr, "usage: %s FILENAME\n", argv[0]); 28 | return 1; 29 | } 30 | 31 | FILE* f = fopen(argv[1], "r"); 32 | if (f == NULL) { 33 | fprintf(stderr, "invalid filename: %s\n", argv[1]); 34 | return 1; 35 | } 36 | 37 | size_t size = 4; 38 | size_t idx = 0; 39 | long* arr = malloc(sizeof *arr * size); 40 | 41 | if (fscanf(f, "%ld", arr + idx) != 1) { 42 | fprintf(stderr, "expected numbers????\n"); 43 | fclose(f); 44 | return 1; 45 | } 46 | idx += 1; 47 | 48 | while (fscanf(f, ",%ld", arr + idx) == 1) { 49 | idx += 1; 50 | if (idx >= size) { 51 | long* new_arr = malloc(sizeof *new_arr * size * 2); 52 | memcpy(new_arr, arr, sizeof *arr * size); 53 | free(arr); 54 | arr = new_arr; 55 | size *= 2; 56 | } 57 | } 58 | fclose(f); 59 | 60 | arr[1] = 12; 61 | arr[2] = 2; 62 | printf("%ld\n", compute(arr)); 63 | 64 | free(arr); 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /day02/part1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog) { 12 | let pc = 0; 13 | for (;;) { 14 | const opc = prog[pc]; 15 | switch (opc) { 16 | case 99: 17 | return prog[0]; 18 | case 1: 19 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]]; 20 | break; 21 | case 2: 22 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]]; 23 | break; 24 | default: 25 | throw new Error('OHNOES!'); 26 | } 27 | pc += 4; 28 | } 29 | } 30 | 31 | let prog = 32 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 33 | .trim() 34 | .split(/,/) 35 | .map((s) => parseInt(s, 10)); 36 | 37 | prog[1] = 12; 38 | prog[2] = 2; 39 | 40 | console.log(compute(prog)); 41 | -------------------------------------------------------------------------------- /day02/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import List 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | def compute_not_broken(prog: List[int]) -> int: 10 | pc = 0 11 | while True: 12 | opc = prog[pc] 13 | if opc == 99: 14 | return prog[0] 15 | elif opc == 1: 16 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]] 17 | elif opc == 2: 18 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]] 19 | else: 20 | raise AssertionError(f'unreachable? {prog} {pc}') 21 | pc += 4 22 | raise AssertionError(f'unreachable? {prog} {pc}') 23 | 24 | 25 | def compute(s: str) -> int: 26 | prog = [int(part) for part in s.strip().split(',')] 27 | prog[1] = 12 28 | prog[2] = 2 29 | return compute_not_broken(prog) 30 | 31 | 32 | @pytest.mark.parametrize( 33 | ('input_l', 'expected'), 34 | ( 35 | ([1, 0, 0, 0, 99], 2), 36 | ), 37 | ) 38 | def test(input_l: List[int], expected: int) -> None: 39 | assert compute_not_broken(input_l) == expected 40 | 41 | 42 | def main() -> int: 43 | parser = argparse.ArgumentParser() 44 | parser.add_argument('data_file') 45 | args = parser.parse_args() 46 | 47 | with open(args.data_file) as f, timing(): 48 | print(compute(f.read())) 49 | 50 | return 0 51 | 52 | 53 | if __name__ == '__main__': 54 | exit(main()) 55 | -------------------------------------------------------------------------------- /day02/part1.sqlite.sql: -------------------------------------------------------------------------------- 1 | -- our puzzle input 2 | CREATE TABLE input (value STRING); 3 | INSERT INTO input VALUES ('1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,9,1,19,1,19,6,23,2,6,23,27,2,27,9,31,1,5,31,35,1,35,10,39,2,39,9,43,1,5,43,47,2,47,10,51,1,51,6,55,1,5,55,59,2,6,59,63,2,63,6,67,1,5,67,71,1,71,9,75,2,75,10,79,1,79,5,83,1,10,83,87,1,5,87,91,2,13,91,95,1,95,10,99,2,99,13,103,1,103,5,107,1,107,13,111,2,111,9,115,1,6,115,119,2,119,6,123,1,123,6,127,1,127,9,131,1,6,131,135,1,135,2,139,1,139,10,0,99,2,0,14,0'); 4 | -- INSERT INTO input VALUES ('1,0,0,0,99'); 5 | -- INSERT INTO input VALUES ('1,1,1,4,99,5,6,0,99'); 6 | 7 | -- convert the input into a table 8 | CREATE TABLE prog (ROWID INT, i INT); 9 | WITH RECURSIVE 10 | nn (ROWID, n, rest) 11 | AS ( 12 | SELECT 13 | 0, 14 | (SELECT SUBSTR(input.value, 0, INSTR(input.value, ',')) FROM input), 15 | (SELECT SUBSTR(input.value, INSTR(input.value, ',') + 1) FROM input) 16 | UNION ALL 17 | SELECT 18 | nn.ROWID + 1, 19 | CASE INSTR(nn.rest, ',') 20 | WHEN 0 THEN nn.rest 21 | ELSE SUBSTR(nn.rest, 0, INSTR(nn.rest, ',')) 22 | END, 23 | CASE INSTR(nn.rest, ',') 24 | WHEN 0 THEN '' 25 | ELSE SUBSTR(nn.rest, INSTR(nn.rest, ',') + 1) 26 | END 27 | FROM nn 28 | WHERE LENGTH(nn.rest) > 0 29 | ) 30 | INSERT INTO prog (ROWID, i) 31 | SELECT nn.ROWID, nn.n FROM nn; 32 | 33 | -- the program counter and stored procedure magic 34 | CREATE TABLE pc (value INT); 35 | PRAGMA recursive_triggers = on; 36 | CREATE TEMP TRIGGER ttrig 37 | AFTER UPDATE OF value ON pc 38 | WHEN (SELECT prog.i FROM prog WHERE ROWID = NEW.value) != 99 39 | BEGIN 40 | UPDATE prog 41 | SET i = ( 42 | CASE (SELECT prog.i FROM prog WHERE ROWID = NEW.value) 43 | WHEN 1 THEN ( 44 | ( 45 | SELECT prog.i 46 | FROM prog 47 | WHERE ROWID = ( 48 | SELECT prog.i FROM prog WHERE ROWID = NEW.value + 1 49 | ) 50 | ) + 51 | ( 52 | SELECT prog.i 53 | FROM prog 54 | WHERE ROWID = ( 55 | SELECT prog.i FROM prog WHERE ROWID = NEW.value + 2 56 | ) 57 | ) 58 | ) 59 | WHEN 2 THEN ( 60 | ( 61 | SELECT prog.i 62 | FROM prog 63 | WHERE ROWID = ( 64 | SELECT prog.i FROM prog WHERE ROWID = NEW.value + 1 65 | ) 66 | ) * 67 | ( 68 | SELECT prog.i 69 | FROM prog 70 | WHERE ROWID = ( 71 | SELECT prog.i FROM prog WHERE ROWID = NEW.value + 2 72 | ) 73 | ) 74 | ) 75 | END 76 | ) 77 | WHERE ROWID = (SELECT prog.i FROM prog WHERE ROWID = NEW.value + 3); 78 | UPDATE pc SET value = NEW.value + 4; 79 | END; 80 | 81 | -- seed 1 and 2 82 | UPDATE prog SET i = 12 WHERE ROWID = 1; 83 | UPDATE prog SET i = 2 WHERE ROWID = 2; 84 | 85 | -- trigger the code 86 | INSERT INTO pc VALUES (-1); 87 | UPDATE pc SET value = 0; 88 | 89 | SELECT prog.i FROM prog WHERE ROWID = 0; 90 | -------------------------------------------------------------------------------- /day02/part2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | long compute(long* prog) { 8 | size_t pc = 0; 9 | while (true) { 10 | long opc = prog[pc]; 11 | if (opc == 99) { 12 | return prog[0]; 13 | } else if (opc == 1) { 14 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]]; 15 | } else if (opc == 2) { 16 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]]; 17 | } else { 18 | assert(!"unreachable"); 19 | } 20 | pc += 4; 21 | } 22 | assert(!"unreachable"); 23 | } 24 | 25 | int main(int argc, char* argv[]) { 26 | if (argc != 2) { 27 | fprintf(stderr, "usage: %s FILENAME\n", argv[0]); 28 | return 1; 29 | } 30 | 31 | FILE* f = fopen(argv[1], "r"); 32 | if (f == NULL) { 33 | fprintf(stderr, "invalid filename: %s\n", argv[1]); 34 | return 1; 35 | } 36 | 37 | size_t size = 4; 38 | size_t idx = 0; 39 | long* arr = malloc(sizeof *arr * size); 40 | 41 | if (fscanf(f, "%ld", arr + idx) != 1) { 42 | fprintf(stderr, "expected numbers????\n"); 43 | fclose(f); 44 | return 1; 45 | } 46 | idx += 1; 47 | 48 | while (fscanf(f, ",%ld", arr + idx) == 1) { 49 | idx += 1; 50 | if (idx >= size) { 51 | long* new_arr = malloc(sizeof *new_arr * size * 2); 52 | memcpy(new_arr, arr, sizeof *arr * size); 53 | free(arr); 54 | arr = new_arr; 55 | size *= 2; 56 | } 57 | } 58 | fclose(f); 59 | 60 | long* tmp = malloc(sizeof *tmp * idx); 61 | for (long noun = 0; noun < 100; noun += 1) { 62 | for (long verb = 0; verb < 100; verb += 1) { 63 | memcpy(tmp, arr, sizeof *arr * idx); 64 | tmp[1] = noun; 65 | tmp[2] = verb; 66 | if (compute(tmp) == 19690720) { 67 | printf("%ld\n", 100 * noun + verb); 68 | free(tmp); 69 | free(arr); 70 | return 0; 71 | } 72 | } 73 | } 74 | 75 | assert(!"unreachable"); 76 | } 77 | -------------------------------------------------------------------------------- /day02/part2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog) { 12 | let pc = 0; 13 | for (;;) { 14 | const opc = prog[pc]; 15 | switch (opc) { 16 | case 99: 17 | return prog[0]; 18 | case 1: 19 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]]; 20 | break; 21 | case 2: 22 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]]; 23 | break; 24 | default: 25 | throw new Error('OHNOES!'); 26 | } 27 | pc += 4; 28 | } 29 | } 30 | 31 | const prog = 32 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 33 | .trim() 34 | .split(/,/) 35 | .map((s) => parseInt(s, 10)); 36 | 37 | function main() { 38 | for (let noun = 0; noun < 100; noun += 1) { 39 | for (let verb = 0; verb < 100; verb += 1) { 40 | let progCopy = prog.slice(); 41 | progCopy[1] = noun; 42 | progCopy[2] = verb; 43 | if (compute(progCopy) === 19690720) { 44 | return 100 * noun + verb; 45 | } 46 | } 47 | } 48 | } 49 | 50 | console.log(main()); 51 | -------------------------------------------------------------------------------- /day02/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import List 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | def compute_not_broken(prog: List[int]) -> int: 10 | pc = 0 11 | while True: 12 | opc = prog[pc] 13 | if opc == 99: 14 | return prog[0] 15 | elif opc == 1: 16 | prog[prog[pc + 3]] = prog[prog[pc + 2]] + prog[prog[pc + 1]] 17 | elif opc == 2: 18 | prog[prog[pc + 3]] = prog[prog[pc + 2]] * prog[prog[pc + 1]] 19 | else: 20 | raise AssertionError(f'unreachable? {prog} {pc}') 21 | pc += 4 22 | raise AssertionError(f'unreachable? {prog} {pc}') 23 | 24 | 25 | def compute(s: str) -> int: 26 | prog = [int(part) for part in s.strip().split(',')] 27 | for noun in range(100): 28 | for verb in range(100): 29 | prog_tmp = prog[:] 30 | prog_tmp[1] = noun 31 | prog_tmp[2] = verb 32 | if compute_not_broken(prog_tmp) == 19690720: 33 | return 100 * noun + verb 34 | raise AssertionError('unreachable?') 35 | 36 | 37 | @pytest.mark.parametrize( 38 | ('input_l', 'expected'), 39 | ( 40 | ([1, 0, 0, 0, 99], 2), 41 | ), 42 | ) 43 | def test(input_l: List[int], expected: int) -> None: 44 | assert compute_not_broken(input_l) == expected 45 | 46 | 47 | def main() -> int: 48 | parser = argparse.ArgumentParser() 49 | parser.add_argument('data_file') 50 | args = parser.parse_args() 51 | 52 | with open(args.data_file) as f, timing(): 53 | print(compute(f.read())) 54 | 55 | return 0 56 | 57 | 58 | if __name__ == '__main__': 59 | exit(main()) 60 | -------------------------------------------------------------------------------- /day03/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day03/__init__.py -------------------------------------------------------------------------------- /day03/input.txt: -------------------------------------------------------------------------------- 1 | R1009,U286,L371,U985,R372,D887,R311,U609,L180,D986,L901,D592,R298,U955,R681,D68,R453,U654,L898,U498,R365,D863,L974,U333,L267,D230,R706,D67,L814,D280,R931,D539,R217,U384,L314,D162,L280,U484,L915,D512,L974,D220,R292,U465,L976,U837,R28,U68,L98,D177,L780,U732,R696,D412,L715,U993,L617,U999,R304,D277,R889,D604,R199,U498,R302,U958,R443,U957,R453,U362,R704,U301,R813,U404,L150,D673,L407,D233,L901,D965,R602,U615,R496,U467,R849,U530,L205,D43,R709,U127,L35,U801,L565,D890,R90,D763,R95,D542,R84,D421,L298,D58,R794,U722,R205,U830,L149,D759,L950,D708,L727,U401,L187,D598,L390,D469,R375,U985,L723,U63,L983,D39,L160,U276,R822,D504,L298,D484,L425,U228,L984,D623,L936,U624,L851,D748,L266,D576,L898,U783,L374,D276,R757,U89,L649,U73,L447,D11,L539,U291,L507,U208,R167,D874,L596,D235,R334,U328,R41,D212,L544,D72,L972,D790,L282,U662,R452,U892,L830,D86,L252,U701,L215,U179,L480,U963,L897,U489,R223,U757,R804,U373,R844,D518,R145,U304,L24,D988,R605,D644,R415,U34,L889,D827,R854,U836,R837,D334,L664,D883,L900,U448,R152,U473,R243,D147,L711,U642,R757,U272,R192,U741,L522,U785,L872,D128,L161,D347,L967,D295,R831,U535,R329,D752,R720,D806,R897,D320,R391,D737,L719,U652,L54,D271,L855,D112,R382,U959,R909,D687,L699,U892,L96,D537,L365,D182,R886,U566,R929,U532,L255,U823,R833,U542,R234,D339,R409,U100,L466,U572,L162,U843,L635,D153,L704,D317,L534,U205,R611,D672,L462,D506,L243,U509,L819,D787,R448,D353,R162,U108,R850,D919,R259,U877,R50,D733,L875,U106,L890,D275,L904,U849,L855,U314,L291,U170,L627,U608,R783,U404,R294 2 | L1010,D347,R554,U465,L30,D816,R891,D778,R184,U253,R694,U346,L743,D298,L956,U703,R528,D16,L404,D818,L640,D50,R534,D99,L555,U974,L779,D774,L690,U19,R973,D588,L631,U35,L410,D332,L74,D858,R213,U889,R977,U803,L624,U627,R601,U499,L213,U692,L234,U401,L894,U733,R414,D431,R712,D284,R965,D624,R848,D17,R86,D285,R502,U516,L709,U343,L558,D615,L150,D590,R113,D887,R469,U584,L434,D9,L994,D704,R740,D541,R95,U219,L634,D184,R714,U81,L426,D437,R927,U232,L361,D756,R685,D206,R116,U844,R807,U811,L382,D338,L660,D997,L551,D294,L895,D208,R37,D90,R44,D131,R77,U883,R449,D24,R441,U659,R826,U259,R98,D548,R118,D470,L259,U170,R518,U731,L287,U191,L45,D672,L691,U117,R156,U308,R230,U112,L938,U644,R911,U110,L1,U162,R943,U433,R98,U610,R428,U231,R35,U590,R554,U612,R191,U261,R793,U3,R507,U632,L571,D535,R30,U281,L613,U199,R168,D948,R486,U913,R534,U131,R974,U399,L525,D174,L595,D567,L394,D969,L779,U346,L969,D943,L845,D727,R128,U241,L616,U117,R791,D419,L913,D949,R628,D738,R776,D294,L175,D708,R568,U484,R589,D930,L416,D114,L823,U16,R260,U450,R534,D94,R695,D982,R186,D422,L789,D886,L761,U30,R182,U930,L483,U863,L318,U343,L380,U650,R542,U92,L339,D390,L55,U343,L641,D556,R616,U936,R118,D997,R936,D979,L594,U326,L975,U52,L89,U679,L91,D969,R878,D798,R193,D858,R95,D989,R389,U960,R106,D564,R48,D151,L121,D241,L369,D476,L24,D229,R601,U849,L632,U894,R27,U200,L698,U788,L330,D73,R405,D526,L154,U942,L504,D579,L815,D643,L81,U172,R879,U28,R715,U367,L366,D964,R16,D415,L501,D176,R641,U523,L979,D556,R831 3 | -------------------------------------------------------------------------------- /day03/part1.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "usage: $0 FILENAME" 1>&2 6 | exit 1 7 | elif [ ! -f "$1" ]; then 8 | echo "file does not exist: $1" 1>&2 9 | exit 1 10 | fi 11 | 12 | lines=() 13 | while IFS= read -r line; do 14 | lines+=("$line,") 15 | done < "$1" 16 | 17 | declare -A grid 18 | x=0 19 | y=0 20 | while IFS= read -d, -r part; do 21 | direction="${part:0:1}" 22 | magnitude="${part:1}" 23 | for ((i=0;i Tuple[int, int]: 14 | return tup1[0] + tup2[0], tup1[1] + tup2[1] 15 | 16 | 17 | DIRECTIONS = { 18 | 'R': (1, 0), 19 | 'L': (-1, 0), 20 | 'U': (0, 1), 21 | 'D': (0, -1), 22 | } 23 | 24 | 25 | def trace_wire(wire: str, callback: Callable[[Tuple[int, int]], None]) -> None: 26 | position = (0, 0) 27 | for part in wire.split(','): 28 | direction, magnitude = part[0], int(part[1:]) 29 | direction_tup = DIRECTIONS[direction] 30 | for _ in range(magnitude): 31 | position = tup_add(position, direction_tup) 32 | callback(position) 33 | 34 | 35 | def compute(s: str) -> int: 36 | wire1, wire2 = s.strip().splitlines() 37 | intersections = [] 38 | 39 | grid: Dict[Tuple[int, int], int] 40 | grid = collections.defaultdict(lambda: sys.maxsize) 41 | 42 | i = 0 43 | 44 | def record_minimum_position(position: Tuple[int, int]) -> None: 45 | nonlocal i 46 | i += 1 47 | grid[position] = min(grid[position], i) 48 | 49 | trace_wire(wire1, record_minimum_position) 50 | 51 | i = 0 52 | 53 | def record_intersection_actions(position: Tuple[int, int]) -> None: 54 | nonlocal i 55 | i += 1 56 | if position in grid: 57 | intersections.append(grid[position] + i) 58 | 59 | trace_wire(wire2, record_intersection_actions) 60 | 61 | return min(intersections) 62 | 63 | 64 | INPUT1 = '''\ 65 | R75,D30,R83,U83,L12,D49,R71,U7,L72 66 | U62,R66,U55,R34,D71,R55,D58,R83 67 | ''' 68 | INPUT2 = '''\ 69 | R98,U47,R26,D63,R33,U87,L62,D20,R33,U53,R51 70 | U98,R91,D20,R16,D67,R40,U7,R15,U6,R7 71 | ''' 72 | 73 | 74 | @pytest.mark.parametrize( 75 | ('input_s', 'expected'), 76 | ( 77 | (INPUT1, 610), 78 | (INPUT2, 410), 79 | ), 80 | ) 81 | def test(input_s: str, expected: int) -> None: 82 | assert compute(input_s) == expected 83 | 84 | 85 | def main() -> int: 86 | parser = argparse.ArgumentParser() 87 | parser.add_argument('data_file') 88 | args = parser.parse_args() 89 | 90 | with open(args.data_file) as f, timing(): 91 | print(compute(f.read())) 92 | 93 | return 0 94 | 95 | 96 | if __name__ == '__main__': 97 | exit(main()) 98 | -------------------------------------------------------------------------------- /day03/part2.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "usage: $0 FILENAME" 1>&2 6 | exit 1 7 | elif [ ! -f "$1" ]; then 8 | echo "file does not exist: $1" 1>&2 9 | exit 1 10 | fi 11 | 12 | lines=() 13 | while IFS= read -r line; do 14 | lines+=("$line,") 15 | done < "$1" 16 | 17 | declare -A grid 18 | x=0 19 | y=0 20 | n=0 21 | while IFS= read -d, -r part; do 22 | direction="${part:0:1}" 23 | magnitude="${part:1}" 24 | for ((i=0;i chars[1] || chars[1] > chars[2] || 19 | chars[2] > chars[3] || chars[3] > chars[4] || 20 | chars[4] > chars[5] 21 | ) { 22 | continue; 23 | } 24 | 25 | 26 | Boolean adjacentSame = false; 27 | char prev = chars[0]; 28 | for (int j = 1; j < chars.length; j += 1) { 29 | if (prev == chars[j]) { 30 | adjacentSame = true; 31 | break; 32 | } 33 | prev = chars[j]; 34 | } 35 | 36 | if (!adjacentSame) { 37 | continue; 38 | } 39 | 40 | n += 1; 41 | } 42 | System.out.println(n); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /day04/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | """\ 10 | It is a six-digit number. 11 | The value is within the range given in your puzzle input. 12 | Two adjacent digits are the same (like 22 in 122345). 13 | Going from left to right, the digits never decrease; they only ever increase 14 | or stay the same (like 111123 or 135679). 15 | """ 16 | 17 | 18 | def compute(s: str) -> int: 19 | beg_s, end_s = s.strip().split('-') 20 | beg, end = int(beg_s), int(end_s) 21 | n = 0 22 | for i in range(beg, end + 1): 23 | str_i = str(i) 24 | 25 | if len(str_i) != 6: # length must be 6 26 | continue 27 | 28 | if ''.join(sorted(str_i)) != str_i: # non-decreasing order 29 | continue 30 | 31 | counts = collections.Counter(str_i) 32 | (_, count), = counts.most_common(1) 33 | if count == 1: # some repeated digit 34 | continue 35 | 36 | n += 1 37 | 38 | return n 39 | 40 | 41 | @pytest.mark.parametrize( 42 | ('input_s', 'expected'), 43 | ( 44 | # put given test cases here 45 | ), 46 | ) 47 | def test(input_s: str, expected: int) -> None: 48 | assert compute(input_s) == expected 49 | 50 | 51 | def main() -> int: 52 | parser = argparse.ArgumentParser() 53 | parser.add_argument('data_file') 54 | args = parser.parse_args() 55 | 56 | with open(args.data_file) as f, timing(): 57 | print(compute(f.read())) 58 | 59 | return 0 60 | 61 | 62 | if __name__ == '__main__': 63 | exit(main()) 64 | -------------------------------------------------------------------------------- /day04/part1.sqlite.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS input; 2 | DROP TABLE IF EXISTS range; 3 | DROP TABLE IF EXISTS ints; 4 | 5 | -- our puzzle input 6 | CREATE TABLE input (value STRING); 7 | INSERT INTO input VALUES ('134564-585159'); 8 | 9 | -- we'll store the integer range derived from our puzzle input 10 | CREATE TABLE range (start INT NOT NULL, end INT NOT NULL); 11 | INSERT INTO range (start, end) 12 | SELECT 13 | SUBSTR(input.value, 0, INSTR(input.value, '-')), 14 | SUBSTR(input.value, INSTR(input.value, '-') + 1) 15 | FROM input; 16 | 17 | -- here's the magic to create our integers table 18 | -- adapted from https://stackoverflow.com/a/38821165/812183 19 | CREATE TABLE ints (i INT); 20 | WITH RECURSIVE 21 | nn (n) 22 | as ( 23 | SELECT (SELECT range.start FROM range) 24 | UNION ALL 25 | SELECT n + 1 AS newn FROM nn 26 | WHERE newn <= (SELECT range.end FROM range) 27 | ) 28 | INSERT INTO ints 29 | SELECT nn.n FROM nn; 30 | 31 | SELECT COUNT(1) FROM ints 32 | WHERE 33 | LENGTH(ints.i) = 6 AND 34 | ( 35 | ints.i LIKE '%00%' OR 36 | ints.i LIKE '%11%' OR 37 | ints.i LIKE '%22%' OR 38 | ints.i LIKE '%33%' OR 39 | ints.i LIKE '%44%' OR 40 | ints.i LIKE '%55%' OR 41 | ints.i LIKE '%66%' OR 42 | ints.i LIKE '%77%' OR 43 | ints.i LIKE '%88%' OR 44 | ints.i LIKE '%99%' 45 | ) AND ( 46 | SUBSTR(ints.i, 1, 1) <= SUBSTR(ints.i, 2, 1) AND 47 | SUBSTR(ints.i, 2, 1) <= SUBSTR(ints.i, 3, 1) AND 48 | SUBSTR(ints.i, 3, 1) <= SUBSTR(ints.i, 4, 1) AND 49 | SUBSTR(ints.i, 4, 1) <= SUBSTR(ints.i, 5, 1) AND 50 | SUBSTR(ints.i, 5, 1) <= SUBSTR(ints.i, 6, 1) 51 | ) 52 | ; 53 | -------------------------------------------------------------------------------- /day04/part2.java: -------------------------------------------------------------------------------- 1 | import java.io.*; 2 | import java.util.*; 3 | 4 | class Main { 5 | public static void main(String[] args) { 6 | String input = "134564-585159"; 7 | String[] parts = input.split("-"); 8 | int start = Integer.parseInt(parts[0]); 9 | int end = Integer.parseInt(parts[1]); 10 | int n = 0; 11 | for (int i = start; i <= end; i += 1) { 12 | char[] chars = Integer.toString(i).toCharArray(); 13 | if (chars.length != 6) { 14 | continue; 15 | } 16 | 17 | if ( 18 | chars[0] > chars[1] || chars[1] > chars[2] || 19 | chars[2] > chars[3] || chars[3] > chars[4] || 20 | chars[4] > chars[5] 21 | ) { 22 | continue; 23 | } 24 | 25 | 26 | Boolean adjacentSame = false; 27 | char prev = chars[0]; 28 | for (int j = 1; j < chars.length; j += 1) { 29 | if (prev == chars[j]) { 30 | adjacentSame = true; 31 | break; 32 | } 33 | prev = chars[j]; 34 | } 35 | 36 | if (!adjacentSame) { 37 | continue; 38 | } 39 | 40 | TreeMap counter = new TreeMap<>(); 41 | for (int j = 0; j < chars.length; j += 1) { 42 | if (counter.containsKey(chars[j])) { 43 | counter.put(chars[j], counter.get(chars[j]) + 1); 44 | } else { 45 | counter.put(chars[j], 1); 46 | } 47 | } 48 | 49 | Boolean seenTwo = false; 50 | for (int val : counter.values()) { 51 | if (val == 2) { 52 | seenTwo = true; 53 | break; 54 | } 55 | } 56 | 57 | if (!seenTwo) { 58 | continue; 59 | } 60 | 61 | n += 1; 62 | } 63 | System.out.println(n); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /day04/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | def compute(s: str) -> int: 10 | beg_s, end_s = s.strip().split('-') 11 | beg, end = int(beg_s), int(end_s) 12 | n = 0 13 | for i in range(beg, end): 14 | str_i = str(i) 15 | 16 | if len(str_i) != 6: # length must be 6 17 | continue 18 | 19 | if ''.join(sorted(str_i)) != str_i: # non-decreasing order 20 | continue 21 | 22 | counts = collections.Counter(str_i) 23 | if 2 not in counts.values(): # some repeated digit, with exactly 2 24 | continue 25 | 26 | n += 1 27 | 28 | return n 29 | 30 | 31 | @pytest.mark.parametrize( 32 | ('input_s', 'expected'), 33 | ( 34 | # put given test cases here 35 | ), 36 | ) 37 | def test(input_s: str, expected: int) -> None: 38 | assert compute(input_s) == expected 39 | 40 | 41 | def main() -> int: 42 | parser = argparse.ArgumentParser() 43 | parser.add_argument('data_file') 44 | args = parser.parse_args() 45 | 46 | with open(args.data_file) as f, timing(): 47 | print(compute(f.read())) 48 | 49 | return 0 50 | 51 | 52 | if __name__ == '__main__': 53 | exit(main()) 54 | -------------------------------------------------------------------------------- /day04/part2.sqlite.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS input; 2 | DROP TABLE IF EXISTS range; 3 | DROP TABLE IF EXISTS ints; 4 | 5 | -- our puzzle input 6 | CREATE TABLE input (value STRING); 7 | INSERT INTO input VALUES ('134564-585159'); 8 | 9 | -- we'll store the integer range derived from our puzzle input 10 | CREATE TABLE range (start INT NOT NULL, end INT NOT NULL); 11 | INSERT INTO range (start, end) 12 | SELECT 13 | SUBSTR(input.value, 0, INSTR(input.value, '-')), 14 | SUBSTR(input.value, INSTR(input.value, '-') + 1) 15 | FROM input; 16 | 17 | -- here's the magic to create our integers table 18 | -- adapted from https://stackoverflow.com/a/38821165/812183 19 | CREATE TABLE ints (i INT); 20 | WITH RECURSIVE 21 | nn (n) 22 | as ( 23 | SELECT (SELECT range.start FROM range) 24 | UNION ALL 25 | SELECT n + 1 AS newn FROM nn 26 | WHERE newn <= (SELECT range.end FROM range) 27 | ) 28 | INSERT INTO ints 29 | SELECT nn.n FROM nn; 30 | 31 | SELECT COUNT(1) FROM ints 32 | WHERE 33 | LENGTH(ints.i) = 6 AND 34 | ( 35 | (ints.i LIKE '%00%' AND NOT ints.i LIKE '%000%') OR 36 | (ints.i LIKE '%11%' AND NOT ints.i LIKE '%111%') OR 37 | (ints.i LIKE '%22%' AND NOT ints.i LIKE '%222%') OR 38 | (ints.i LIKE '%33%' AND NOT ints.i LIKE '%333%') OR 39 | (ints.i LIKE '%44%' AND NOT ints.i LIKE '%444%') OR 40 | (ints.i LIKE '%55%' AND NOT ints.i LIKE '%555%') OR 41 | (ints.i LIKE '%66%' AND NOT ints.i LIKE '%666%') OR 42 | (ints.i LIKE '%77%' AND NOT ints.i LIKE '%777%') OR 43 | (ints.i LIKE '%88%' AND NOT ints.i LIKE '%888%') OR 44 | (ints.i LIKE '%99%' AND NOT ints.i LIKE '%999%') 45 | ) AND ( 46 | SUBSTR(ints.i, 1, 1) <= SUBSTR(ints.i, 2, 1) AND 47 | SUBSTR(ints.i, 2, 1) <= SUBSTR(ints.i, 3, 1) AND 48 | SUBSTR(ints.i, 3, 1) <= SUBSTR(ints.i, 4, 1) AND 49 | SUBSTR(ints.i, 4, 1) <= SUBSTR(ints.i, 5, 1) AND 50 | SUBSTR(ints.i, 5, 1) <= SUBSTR(ints.i, 6, 1) 51 | ) 52 | ; 53 | -------------------------------------------------------------------------------- /day05/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "day05" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /day05/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day05" 3 | version = "0.1.0" 4 | authors = ["Anthony Sottile "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | -------------------------------------------------------------------------------- /day05/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day05/__init__.py -------------------------------------------------------------------------------- /day05/input.txt: -------------------------------------------------------------------------------- 1 | 3,225,1,225,6,6,1100,1,238,225,104,0,1101,61,45,225,102,94,66,224,101,-3854,224,224,4,224,102,8,223,223,1001,224,7,224,1,223,224,223,1101,31,30,225,1102,39,44,224,1001,224,-1716,224,4,224,102,8,223,223,1001,224,7,224,1,224,223,223,1101,92,41,225,101,90,40,224,1001,224,-120,224,4,224,102,8,223,223,1001,224,1,224,1,223,224,223,1101,51,78,224,101,-129,224,224,4,224,1002,223,8,223,1001,224,6,224,1,224,223,223,1,170,13,224,101,-140,224,224,4,224,102,8,223,223,1001,224,4,224,1,223,224,223,1101,14,58,225,1102,58,29,225,1102,68,70,225,1002,217,87,224,101,-783,224,224,4,224,102,8,223,223,101,2,224,224,1,224,223,223,1101,19,79,225,1001,135,42,224,1001,224,-56,224,4,224,102,8,223,223,1001,224,6,224,1,224,223,223,2,139,144,224,1001,224,-4060,224,4,224,102,8,223,223,101,1,224,224,1,223,224,223,1102,9,51,225,4,223,99,0,0,0,677,0,0,0,0,0,0,0,0,0,0,0,1105,0,99999,1105,227,247,1105,1,99999,1005,227,99999,1005,0,256,1105,1,99999,1106,227,99999,1106,0,265,1105,1,99999,1006,0,99999,1006,227,274,1105,1,99999,1105,1,280,1105,1,99999,1,225,225,225,1101,294,0,0,105,1,0,1105,1,99999,1106,0,300,1105,1,99999,1,225,225,225,1101,314,0,0,106,0,0,1105,1,99999,1008,677,226,224,102,2,223,223,1006,224,329,101,1,223,223,108,677,677,224,102,2,223,223,1005,224,344,101,1,223,223,107,677,677,224,1002,223,2,223,1005,224,359,101,1,223,223,1107,226,677,224,1002,223,2,223,1005,224,374,1001,223,1,223,1008,677,677,224,102,2,223,223,1006,224,389,1001,223,1,223,1007,677,677,224,1002,223,2,223,1006,224,404,1001,223,1,223,8,677,226,224,102,2,223,223,1005,224,419,1001,223,1,223,8,226,226,224,102,2,223,223,1006,224,434,101,1,223,223,1107,226,226,224,1002,223,2,223,1006,224,449,101,1,223,223,1107,677,226,224,102,2,223,223,1005,224,464,101,1,223,223,1108,226,226,224,102,2,223,223,1006,224,479,1001,223,1,223,7,677,677,224,1002,223,2,223,1006,224,494,101,1,223,223,7,677,226,224,102,2,223,223,1005,224,509,101,1,223,223,1108,226,677,224,1002,223,2,223,1006,224,524,101,1,223,223,8,226,677,224,1002,223,2,223,1005,224,539,101,1,223,223,1007,226,226,224,102,2,223,223,1006,224,554,1001,223,1,223,108,226,226,224,1002,223,2,223,1006,224,569,1001,223,1,223,1108,677,226,224,102,2,223,223,1005,224,584,101,1,223,223,108,226,677,224,102,2,223,223,1005,224,599,101,1,223,223,1007,226,677,224,102,2,223,223,1006,224,614,1001,223,1,223,1008,226,226,224,1002,223,2,223,1006,224,629,1001,223,1,223,107,226,226,224,1002,223,2,223,1006,224,644,101,1,223,223,7,226,677,224,102,2,223,223,1005,224,659,1001,223,1,223,107,677,226,224,102,2,223,223,1005,224,674,1001,223,1,223,4,223,99,226 2 | -------------------------------------------------------------------------------- /day05/input2.txt: -------------------------------------------------------------------------------- 1 | 1101,100,-1,4,0 2 | -------------------------------------------------------------------------------- /day05/part1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog, fn) { 12 | function parameter(instr, n) { 13 | const mode = Math.trunc(instr / (10 ** (n + 1))) % 10; 14 | switch (mode) { 15 | case 0: 16 | return prog[prog[pc + n]]; 17 | case 1: 18 | return prog[pc + n]; 19 | default: 20 | throw new Error('OHNOSE!!!'); 21 | } 22 | } 23 | 24 | let pc = 0; 25 | for (;;) { 26 | const instr = prog[pc]; 27 | const opc = instr % 100; 28 | switch (opc) { 29 | case 99: 30 | return prog[0]; 31 | case 1: 32 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2); 33 | pc += 4; 34 | break; 35 | case 2: 36 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2); 37 | pc += 4; 38 | break; 39 | case 3: 40 | prog[prog[pc + 1]] = 1; // hardcoded value from puzzle 41 | pc += 2; 42 | break; 43 | case 4: 44 | fn(parameter(instr, 1)); 45 | pc += 2; 46 | break; 47 | default: 48 | throw new Error('OHNOES!'); 49 | } 50 | } 51 | } 52 | 53 | const prog = 54 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 55 | .trim() 56 | .split(/,/) 57 | .map((s) => parseInt(s, 10)); 58 | 59 | compute(prog, console.log); 60 | -------------------------------------------------------------------------------- /day05/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import Callable 3 | from typing import List 4 | 5 | from support import timing 6 | 7 | 8 | def run(prog: List[int], fn: Callable[[int], None]) -> List[int]: 9 | def parameter(instr: int, n: int) -> int: 10 | mode = instr // (10 ** (n + 1)) % 10 11 | if mode == 0: 12 | return prog[prog[pc + n]] 13 | elif mode == 1: 14 | return prog[pc + n] 15 | else: 16 | raise NotImplementedError(mode) 17 | 18 | pc = 0 19 | while pc < len(prog): 20 | instr = prog[pc] 21 | opc = instr % 100 22 | if opc == 99: 23 | return prog 24 | elif opc == 1: 25 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2) 26 | pc += 4 27 | elif opc == 2: 28 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2) 29 | pc += 4 30 | elif opc == 3: 31 | prog[prog[pc + 1]] = 1 # 1 is the hardcoded input 32 | pc += 2 33 | elif opc == 4: 34 | fn(parameter(instr, 1)) 35 | pc += 2 36 | else: 37 | raise AssertionError(f'unreachable? {prog} {pc}') 38 | raise AssertionError(f'unreachable? {prog} {pc}') 39 | 40 | 41 | def compute(s: str) -> None: 42 | prog = [int(part) for part in s.strip().split(',')] 43 | run(prog, print) 44 | 45 | 46 | def main() -> int: 47 | parser = argparse.ArgumentParser() 48 | parser.add_argument('data_file') 49 | args = parser.parse_args() 50 | 51 | with open(args.data_file) as f, timing(): 52 | compute(f.read()) 53 | 54 | return 0 55 | 56 | 57 | if __name__ == '__main__': 58 | exit(main()) 59 | -------------------------------------------------------------------------------- /day05/part2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog, fn) { 12 | function parameter(instr, n) { 13 | const mode = Math.trunc(instr / (10 ** (n + 1))) % 10; 14 | switch (mode) { 15 | case 0: 16 | return prog[prog[pc + n]]; 17 | case 1: 18 | return prog[pc + n]; 19 | default: 20 | throw new Error('OHNOSE!!!'); 21 | } 22 | } 23 | 24 | let pc = 0; 25 | for (;;) { 26 | const instr = prog[pc]; 27 | const opc = instr % 100; 28 | switch (opc) { 29 | case 99: 30 | return prog[0]; 31 | case 1: 32 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2); 33 | pc += 4; 34 | break; 35 | case 2: 36 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2); 37 | pc += 4; 38 | break; 39 | case 3: 40 | prog[prog[pc + 1]] = 5; // hardcoded value from puzzle 41 | pc += 2; 42 | break; 43 | case 4: 44 | fn(parameter(instr, 1)); 45 | pc += 2; 46 | break; 47 | case 5: 48 | if (parameter(instr, 1)) { 49 | pc = parameter(instr, 2); 50 | } else { 51 | pc += 3; 52 | } 53 | break; 54 | case 6: 55 | if (!parameter(instr, 1)) { 56 | pc = parameter(instr, 2); 57 | } else { 58 | pc += 3; 59 | } 60 | break; 61 | case 7: 62 | if (parameter(instr, 1) < parameter(instr, 2)) { 63 | prog[prog[pc + 3]] = 1; 64 | } else { 65 | prog[prog[pc + 3]] = 0; 66 | } 67 | pc += 4; 68 | break; 69 | case 8: 70 | if (parameter(instr, 1) === parameter(instr, 2)) { 71 | prog[prog[pc + 3]] = 1; 72 | } else { 73 | prog[prog[pc + 3]] = 0; 74 | } 75 | pc += 4; 76 | break; 77 | default: 78 | throw new Error(`OHNOES! ${pc} ${prog}`); 79 | } 80 | } 81 | } 82 | 83 | const prog = 84 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 85 | .trim() 86 | .split(/,/) 87 | .map((s) => parseInt(s, 10)); 88 | 89 | compute(prog, console.log); 90 | -------------------------------------------------------------------------------- /day05/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import Callable 3 | from typing import List 4 | 5 | from support import timing 6 | 7 | 8 | def run(prog: List[int], fn: Callable[[int], None]) -> List[int]: 9 | def parameter(instr: int, n: int) -> int: 10 | mode = instr // (10 ** (n + 1)) % 10 11 | if mode == 0: 12 | return prog[prog[pc + n]] 13 | elif mode == 1: 14 | return prog[pc + n] 15 | else: 16 | raise NotImplementedError(mode) 17 | 18 | pc = 0 19 | while pc < len(prog): 20 | instr = prog[pc] 21 | opc = instr % 100 22 | if opc == 99: 23 | return prog 24 | elif opc == 1: 25 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2) 26 | pc += 4 27 | elif opc == 2: 28 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2) 29 | pc += 4 30 | elif opc == 3: 31 | prog[prog[pc + 1]] = 5 # 5 is the hardcoded input 32 | pc += 2 33 | elif opc == 4: 34 | fn(parameter(instr, 1)) 35 | pc += 2 36 | elif opc == 5: 37 | if parameter(instr, 1): 38 | pc = parameter(instr, 2) 39 | else: 40 | pc += 3 41 | elif opc == 6: 42 | if not parameter(instr, 1): 43 | pc = parameter(instr, 2) 44 | else: 45 | pc += 3 46 | elif opc == 7: 47 | if parameter(instr, 1) < parameter(instr, 2): 48 | prog[prog[pc + 3]] = 1 49 | else: 50 | prog[prog[pc + 3]] = 0 51 | pc += 4 52 | elif opc == 8: 53 | if parameter(instr, 1) == parameter(instr, 2): 54 | prog[prog[pc + 3]] = 1 55 | else: 56 | prog[prog[pc + 3]] = 0 57 | pc += 4 58 | else: 59 | raise AssertionError(f'unreachable? {prog} {pc}') 60 | raise AssertionError(f'unreachable? {prog} {pc}') 61 | 62 | 63 | def compute(s: str) -> None: 64 | prog = [int(part) for part in s.strip().split(',')] 65 | run(prog, print) 66 | 67 | 68 | def main() -> int: 69 | parser = argparse.ArgumentParser() 70 | parser.add_argument('data_file') 71 | args = parser.parse_args() 72 | 73 | with open(args.data_file) as f, timing(): 74 | compute(f.read()) 75 | 76 | return 0 77 | 78 | 79 | if __name__ == '__main__': 80 | exit(main()) 81 | -------------------------------------------------------------------------------- /day05/src/bin/part1.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::process; 4 | 5 | fn parameter(instr: i64, n: u32, prog: &Vec, pc: usize) -> i64 { 6 | let mode = instr / (10_i64.pow(n + 1)) % 10; 7 | match mode { 8 | 0 => return prog[prog[pc + n as usize] as usize], 9 | 1 => return prog[pc + n as usize], 10 | _ => panic!("unknown mode {}", mode), 11 | } 12 | } 13 | 14 | fn main() { 15 | let args: Vec = env::args().collect(); 16 | if args.len() != 2 { 17 | println!("usage {} INPUT", args[0]); 18 | process::exit(1); 19 | } 20 | 21 | let mut prog = fs::read_to_string(&args[1]) 22 | .expect("file error") 23 | .trim() 24 | .split(',') 25 | .map(|s| s.parse::().expect("cound not parse int")) 26 | .collect::>(); 27 | 28 | let mut pc = 0; 29 | while pc < prog.len() { 30 | let instr = prog[pc]; 31 | let opc = instr % 100; 32 | match opc { 33 | 99 => return, 34 | 1 => { 35 | let idx = prog[pc + 3] as usize; 36 | prog[idx] = 37 | parameter(instr, 1, &prog, pc) + 38 | parameter(instr, 2, &prog, pc); 39 | pc += 4; 40 | }, 41 | 2 => { 42 | let idx = prog[pc + 3] as usize; 43 | prog[idx] = 44 | parameter(instr, 1, &prog, pc) * 45 | parameter(instr, 2, &prog, pc); 46 | pc += 4; 47 | }, 48 | 3 => { 49 | let idx = prog[pc + 1] as usize; 50 | prog[idx] = 1; // hard coded value 51 | pc += 2; 52 | }, 53 | 4 => { 54 | println!("output: {}", parameter(instr, 1, &prog, pc)); 55 | pc += 2; 56 | }, 57 | _ => panic!("unknown opc {}, {}, {:?}", opc, pc, prog), 58 | } 59 | } 60 | 61 | panic!("unreachable"); 62 | } 63 | -------------------------------------------------------------------------------- /day05/src/bin/part2.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::fs; 3 | use std::process; 4 | 5 | fn parameter(instr: i64, n: u32, prog: &Vec, pc: usize) -> i64 { 6 | let mode = instr / (10_i64.pow(n + 1)) % 10; 7 | match mode { 8 | 0 => return prog[prog[pc + n as usize] as usize], 9 | 1 => return prog[pc + n as usize], 10 | _ => panic!("unknown mode {}", mode), 11 | } 12 | } 13 | 14 | fn main() { 15 | let args: Vec = env::args().collect(); 16 | if args.len() != 2 { 17 | println!("usage {} INPUT", args[0]); 18 | process::exit(1); 19 | } 20 | 21 | let mut prog = fs::read_to_string(&args[1]) 22 | .expect("file error") 23 | .trim() 24 | .split(',') 25 | .map(|s| s.parse::().expect("cound not parse int")) 26 | .collect::>(); 27 | 28 | let mut pc = 0; 29 | while pc < prog.len() { 30 | let instr = prog[pc]; 31 | let opc = instr % 100; 32 | match opc { 33 | 99 => return, 34 | 1 => { 35 | let idx = prog[pc + 3] as usize; 36 | prog[idx] = 37 | parameter(instr, 1, &prog, pc) + 38 | parameter(instr, 2, &prog, pc); 39 | pc += 4; 40 | }, 41 | 2 => { 42 | let idx = prog[pc + 3] as usize; 43 | prog[idx] = 44 | parameter(instr, 1, &prog, pc) * 45 | parameter(instr, 2, &prog, pc); 46 | pc += 4; 47 | }, 48 | 3 => { 49 | let idx = prog[pc + 1] as usize; 50 | prog[idx] = 5; // hard coded value 51 | pc += 2; 52 | }, 53 | 4 => { 54 | println!("output: {}", parameter(instr, 1, &prog, pc)); 55 | pc += 2; 56 | }, 57 | 5 => { 58 | if parameter(instr, 1, &prog, pc) != 0 { 59 | pc = parameter(instr, 2, &prog, pc) as usize; 60 | } else { 61 | pc += 3; 62 | } 63 | }, 64 | 6 => { 65 | if parameter(instr, 1, &prog, pc) == 0 { 66 | pc = parameter(instr, 2, &prog, pc) as usize; 67 | } else { 68 | pc += 3; 69 | } 70 | }, 71 | 7 => { 72 | let idx = prog[pc + 3] as usize; 73 | prog[idx] = 74 | ( 75 | parameter(instr, 1, &prog, pc) < 76 | parameter(instr, 2, &prog, pc) 77 | ) as i64; 78 | pc += 4 79 | }, 80 | 8 => { 81 | let idx = prog[pc + 3] as usize; 82 | prog[idx] = 83 | ( 84 | parameter(instr, 1, &prog, pc) == 85 | parameter(instr, 2, &prog, pc) 86 | ) as i64; 87 | pc += 4 88 | }, 89 | _ => panic!("unknown opc {}, {}, {:?}", opc, pc, prog), 90 | } 91 | } 92 | 93 | panic!("unreachable"); 94 | } 95 | -------------------------------------------------------------------------------- /day06/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day06/__init__.py -------------------------------------------------------------------------------- /day06/part1.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | 4 | if ($#ARGV != 0) { 5 | print "usage: $0 FILENAME\n"; 6 | exit 1; 7 | } 8 | 9 | open(my $fh, '<:encoding(UTF-8)', $ARGV[0]) 10 | or die "Could not open file '$ARGV[0]' $!"; 11 | 12 | my %nodes = ("COM" => ""); 13 | while (my $row = <$fh>) { 14 | chomp $row; 15 | my @spl = split('\)', $row); 16 | $nodes{$spl[1]} = $spl[0]; 17 | } 18 | 19 | my $total = 0; 20 | foreach (values %nodes) { 21 | my $node = $_; 22 | while ( "" ne $node ) { 23 | $total += 1; 24 | $node = $nodes{$node}; 25 | } 26 | } 27 | print "$total\n"; 28 | -------------------------------------------------------------------------------- /day06/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import functools 3 | from typing import Callable 4 | 5 | import pytest 6 | 7 | from support import timing 8 | 9 | 10 | def compute_orig(s: str) -> int: 11 | nodes = {'COM': ''} 12 | for line in s.splitlines(): 13 | parent, name = line.split(')') 14 | nodes[name] = parent 15 | 16 | total = 0 17 | for node in nodes.values(): 18 | while node != '': 19 | total += 1 20 | node = nodes[node] 21 | return total 22 | 23 | 24 | def compute_memoized(s: str) -> int: 25 | nodes = {'COM': ''} 26 | for line in s.splitlines(): 27 | parent, name = line.split(')') 28 | nodes[name] = parent 29 | 30 | @functools.lru_cache(maxsize=None) 31 | def size(node: str) -> int: 32 | if node == '': 33 | return 0 34 | else: 35 | return 1 + size(nodes[node]) 36 | 37 | total = 0 38 | for node in nodes.values(): 39 | total += size(node) 40 | return total 41 | 42 | 43 | INPUT = '''\ 44 | COM)B 45 | B)C 46 | C)D 47 | D)E 48 | E)F 49 | B)G 50 | G)H 51 | D)I 52 | E)J 53 | J)K 54 | K)L 55 | ''' 56 | 57 | 58 | @pytest.mark.parametrize( 59 | ('input_s', 'expected'), 60 | ( 61 | (INPUT, 42), 62 | ), 63 | ) 64 | @pytest.mark.parametrize('fn', (compute_orig, compute_memoized)) 65 | def test(input_s: str, expected: int, fn: Callable[[str], int]) -> None: 66 | assert fn(input_s) == expected 67 | 68 | 69 | def main() -> int: 70 | parser = argparse.ArgumentParser() 71 | parser.add_argument('data_file') 72 | args = parser.parse_args() 73 | 74 | with open(args.data_file) as f, timing('original'): 75 | print(compute_orig(f.read())) 76 | 77 | with open(args.data_file) as f, timing('memoized'): 78 | print(compute_memoized(f.read())) 79 | 80 | return 0 81 | 82 | 83 | if __name__ == '__main__': 84 | exit(main()) 85 | -------------------------------------------------------------------------------- /day06/part2.pl: -------------------------------------------------------------------------------- 1 | use strict; 2 | use warnings; 3 | use List::Util qw( min ); 4 | 5 | if ($#ARGV != 0) { 6 | print "usage: $0 FILENAME\n"; 7 | exit 1; 8 | } 9 | 10 | open(my $fh, '<:encoding(UTF-8)', $ARGV[0]) 11 | or die "Could not open file '$ARGV[0]' $!"; 12 | 13 | my %nodes = ("COM" => ""); 14 | while (my $row = <$fh>) { 15 | chomp $row; 16 | my @spl = split('\)', $row); 17 | $nodes{$spl[1]} = $spl[0]; 18 | } 19 | 20 | my %you_hops; 21 | my $you_node = $nodes{"YOU"}; 22 | my $you_i = 0; 23 | while ( $you_node ne "COM" ) { 24 | $you_hops{$you_node} = $you_i; 25 | $you_i += 1; 26 | $you_node = $nodes{$you_node}; 27 | } 28 | 29 | my %san_hops; 30 | my $san_node = $nodes{"SAN"}; 31 | my $san_i = 0; 32 | while ( $san_node ne "COM" ) { 33 | $san_hops{$san_node} = $san_i; 34 | $san_i += 1; 35 | $san_node = $nodes{$san_node}; 36 | } 37 | 38 | my @distances = (); 39 | foreach (keys %you_hops) { 40 | if (exists($san_hops{$_})) { 41 | push @distances, $you_hops{$_} + $san_hops{$_}; 42 | } 43 | } 44 | my $answer = min @distances; 45 | print "$answer\n"; 46 | -------------------------------------------------------------------------------- /day06/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import Dict 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | def compute(s: str) -> int: 10 | nodes = {'COM': ''} 11 | for line in s.splitlines(): 12 | parent, name = line.split(')') 13 | nodes[name] = parent 14 | 15 | you_hops: Dict[str, int] = {} 16 | node = nodes['YOU'] 17 | i = 0 18 | while node != 'COM': 19 | you_hops[node] = i 20 | i += 1 21 | node = nodes[node] 22 | 23 | san_hops: Dict[str, int] = {} 24 | node = nodes['SAN'] 25 | i = 0 26 | while node != 'COM': 27 | san_hops[node] = i 28 | i += 1 29 | node = nodes[node] 30 | 31 | candidates = [ 32 | (you_hops[k] + san_hops[k], k) 33 | for k in you_hops.keys() & san_hops.keys() 34 | ] 35 | 36 | n, el = min(candidates) 37 | return n 38 | 39 | 40 | INPUT = '''\ 41 | COM)B 42 | B)C 43 | C)D 44 | D)E 45 | E)F 46 | B)G 47 | G)H 48 | D)I 49 | E)J 50 | J)K 51 | K)L 52 | K)YOU 53 | I)SAN 54 | ''' 55 | 56 | 57 | @pytest.mark.parametrize( 58 | ('input_s', 'expected'), 59 | ( 60 | (INPUT, 4), 61 | ), 62 | ) 63 | def test(input_s: str, expected: int) -> None: 64 | assert compute(input_s) == expected 65 | 66 | 67 | def main() -> int: 68 | parser = argparse.ArgumentParser() 69 | parser.add_argument('data_file') 70 | args = parser.parse_args() 71 | 72 | with open(args.data_file) as f, timing(): 73 | print(compute(f.read())) 74 | 75 | return 0 76 | 77 | 78 | if __name__ == '__main__': 79 | exit(main()) 80 | -------------------------------------------------------------------------------- /day07/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day07/__init__.py -------------------------------------------------------------------------------- /day07/input.txt: -------------------------------------------------------------------------------- 1 | 3,8,1001,8,10,8,105,1,0,0,21,38,59,76,89,106,187,268,349,430,99999,3,9,1002,9,3,9,101,2,9,9,1002,9,4,9,4,9,99,3,9,1001,9,5,9,1002,9,5,9,1001,9,2,9,1002,9,3,9,4,9,99,3,9,1001,9,4,9,102,4,9,9,1001,9,3,9,4,9,99,3,9,101,4,9,9,1002,9,5,9,4,9,99,3,9,1002,9,3,9,101,5,9,9,1002,9,3,9,4,9,99,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,99,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,2,9,4,9,3,9,102,2,9,9,4,9,99 2 | -------------------------------------------------------------------------------- /day07/part1.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog, inCallback, outCallback) { 12 | function parameter(instr, n) { 13 | const mode = Math.trunc(instr / (10 ** (n + 1))) % 10; 14 | switch (mode) { 15 | case 0: 16 | return prog[prog[pc + n]]; 17 | case 1: 18 | return prog[pc + n]; 19 | default: 20 | throw new Error('OHNOSE!!!'); 21 | } 22 | } 23 | 24 | let pc = 0; 25 | for (;;) { 26 | const instr = prog[pc]; 27 | const opc = instr % 100; 28 | switch (opc) { 29 | case 99: 30 | return prog[0]; 31 | case 1: 32 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2); 33 | pc += 4; 34 | break; 35 | case 2: 36 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2); 37 | pc += 4; 38 | break; 39 | case 3: 40 | prog[prog[pc + 1]] = inCallback(); 41 | pc += 2; 42 | break; 43 | case 4: 44 | outCallback(parameter(instr, 1)); 45 | pc += 2; 46 | break; 47 | case 5: 48 | if (parameter(instr, 1)) { 49 | pc = parameter(instr, 2); 50 | } else { 51 | pc += 3; 52 | } 53 | break; 54 | case 6: 55 | if (!parameter(instr, 1)) { 56 | pc = parameter(instr, 2); 57 | } else { 58 | pc += 3; 59 | } 60 | break; 61 | case 7: 62 | if (parameter(instr, 1) < parameter(instr, 2)) { 63 | prog[prog[pc + 3]] = 1; 64 | } else { 65 | prog[prog[pc + 3]] = 0; 66 | } 67 | pc += 4; 68 | break; 69 | case 8: 70 | if (parameter(instr, 1) === parameter(instr, 2)) { 71 | prog[prog[pc + 3]] = 1; 72 | } else { 73 | prog[prog[pc + 3]] = 0; 74 | } 75 | pc += 4; 76 | break; 77 | default: 78 | throw new Error('OHNOES!'); 79 | } 80 | } 81 | } 82 | 83 | function permutations(inputArr) { 84 | // siq copy pasta from https://stackoverflow.com/a/20871714/812183 85 | let result = []; 86 | 87 | const permute = (arr, m = []) => { 88 | if (arr.length === 0) { 89 | result.push(m) 90 | } else { 91 | for (let i = 0; i < arr.length; i++) { 92 | let curr = arr.slice(); 93 | let next = curr.splice(i, 1); 94 | permute(curr.slice(), m.concat(next)) 95 | } 96 | } 97 | } 98 | 99 | permute(inputArr) 100 | 101 | return result; 102 | } 103 | 104 | const prog = 105 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 106 | .trim() 107 | .split(/,/) 108 | .map((s) => parseInt(s, 10)); 109 | 110 | let outputs = []; 111 | let permuted = permutations([...Array(5).keys()]); 112 | for (const permutation of permuted) { 113 | let val = 0; 114 | 115 | for (const n of permutation) { 116 | let vals = [val, n]; 117 | compute(prog.slice(), () => vals.pop(), (n) => (val = n)); 118 | } 119 | outputs.push(val); 120 | } 121 | console.log(Math.max.apply(null, outputs)); 122 | -------------------------------------------------------------------------------- /day07/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import itertools 3 | from typing import Callable 4 | from typing import List 5 | 6 | import pytest 7 | 8 | from support import timing 9 | 10 | 11 | def run( 12 | prog: List[int], 13 | in_fn: Callable[[], int], 14 | fn: Callable[[int], None], 15 | ) -> List[int]: 16 | def parameter(instr: int, n: int) -> int: 17 | mode = instr // (10 ** (n + 1)) % 10 18 | if mode == 0: 19 | return prog[prog[pc + n]] 20 | elif mode == 1: 21 | return prog[pc + n] 22 | else: 23 | raise NotImplementedError(mode) 24 | 25 | pc = 0 26 | while pc < len(prog): 27 | instr = prog[pc] 28 | opc = instr % 100 29 | if opc == 99: 30 | return prog 31 | elif opc == 1: 32 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2) 33 | pc += 4 34 | elif opc == 2: 35 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2) 36 | pc += 4 37 | elif opc == 3: 38 | prog[prog[pc + 1]] = in_fn() 39 | pc += 2 40 | elif opc == 4: 41 | fn(parameter(instr, 1)) 42 | pc += 2 43 | elif opc == 5: 44 | if parameter(instr, 1): 45 | pc = parameter(instr, 2) 46 | else: 47 | pc += 3 48 | elif opc == 6: 49 | if not parameter(instr, 1): 50 | pc = parameter(instr, 2) 51 | else: 52 | pc += 3 53 | elif opc == 7: 54 | if parameter(instr, 1) < parameter(instr, 2): 55 | prog[prog[pc + 3]] = 1 56 | else: 57 | prog[prog[pc + 3]] = 0 58 | pc += 4 59 | elif opc == 8: 60 | if parameter(instr, 1) == parameter(instr, 2): 61 | prog[prog[pc + 3]] = 1 62 | else: 63 | prog[prog[pc + 3]] = 0 64 | pc += 4 65 | else: 66 | raise AssertionError(f'unreachable? {prog} {pc}') 67 | raise AssertionError(f'unreachable? {prog} {pc}') 68 | 69 | 70 | def compute(s: str) -> int: 71 | prog = [int(part) for part in s.strip().split(',')] 72 | outputs = [] 73 | for permutation in itertools.permutations(range(5)): 74 | val = 0 75 | for n in permutation: 76 | vals = iter((n, val)) 77 | 78 | def get() -> int: 79 | return next(vals) 80 | 81 | def put(n: int) -> None: 82 | nonlocal val 83 | val = n 84 | 85 | run(prog[:], get, put) 86 | outputs.append(val) 87 | return max(outputs) 88 | 89 | 90 | @pytest.mark.parametrize( 91 | ('input_s', 'expected'), 92 | ( 93 | ('3,15,3,16,1002,16,10,16,1,16,15,15,4,15,99,0,0', 43210), 94 | ), 95 | ) 96 | def test(input_s: str, expected: int) -> None: 97 | assert compute(input_s) == expected 98 | 99 | 100 | def main() -> int: 101 | parser = argparse.ArgumentParser() 102 | parser.add_argument('data_file') 103 | args = parser.parse_args() 104 | 105 | with open(args.data_file) as f, timing(): 106 | print(compute(f.read())) 107 | 108 | return 0 109 | 110 | 111 | if __name__ == '__main__': 112 | exit(main()) 113 | -------------------------------------------------------------------------------- /day07/part2.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | 5 | if (process.argv.length !== 3) { 6 | console.error(`usage: node ${process.argv[1]} FILENAME`); 7 | process.exit(1) 8 | } 9 | 10 | 11 | function compute(prog, pc, val) { 12 | pc = pc || 0; 13 | 14 | function parameter(instr, n) { 15 | const mode = Math.trunc(instr / (10 ** (n + 1))) % 10; 16 | switch (mode) { 17 | case 0: 18 | return prog[prog[pc + n]]; 19 | case 1: 20 | return prog[pc + n]; 21 | default: 22 | throw new Error('OHNOSE!!!'); 23 | } 24 | } 25 | 26 | for (;;) { 27 | const instr = prog[pc]; 28 | const opc = instr % 100; 29 | switch (opc) { 30 | case 99: 31 | return [undefined, undefined, undefined, true]; 32 | case 1: 33 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2); 34 | pc += 4; 35 | break; 36 | case 2: 37 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2); 38 | pc += 4; 39 | break; 40 | case 3: 41 | if (val === undefined) { 42 | return [prog, pc, undefined, false]; 43 | } else { 44 | prog[prog[pc + 1]] = val; 45 | val = undefined; 46 | pc += 2; 47 | } 48 | break; 49 | case 4: 50 | return [prog, pc + 2, parameter(instr, 1), false]; 51 | case 5: 52 | if (parameter(instr, 1)) { 53 | pc = parameter(instr, 2); 54 | } else { 55 | pc += 3; 56 | } 57 | break; 58 | case 6: 59 | if (!parameter(instr, 1)) { 60 | pc = parameter(instr, 2); 61 | } else { 62 | pc += 3; 63 | } 64 | break; 65 | case 7: 66 | if (parameter(instr, 1) < parameter(instr, 2)) { 67 | prog[prog[pc + 3]] = 1; 68 | } else { 69 | prog[prog[pc + 3]] = 0; 70 | } 71 | pc += 4; 72 | break; 73 | case 8: 74 | if (parameter(instr, 1) === parameter(instr, 2)) { 75 | prog[prog[pc + 3]] = 1; 76 | } else { 77 | prog[prog[pc + 3]] = 0; 78 | } 79 | pc += 4; 80 | break; 81 | default: 82 | throw new Error(`OHNOES! ${pc} wat`); 83 | } 84 | } 85 | } 86 | 87 | function permutations(inputArr) { 88 | // siq copy pasta from https://stackoverflow.com/a/20871714/812183 89 | let result = []; 90 | 91 | const permute = (arr, m = []) => { 92 | if (arr.length === 0) { 93 | result.push(m) 94 | } else { 95 | for (let i = 0; i < arr.length; i++) { 96 | let curr = arr.slice(); 97 | let next = curr.splice(i, 1); 98 | permute(curr.slice(), m.concat(next)) 99 | } 100 | } 101 | } 102 | 103 | permute(inputArr) 104 | 105 | return result; 106 | } 107 | 108 | const prog = 109 | fs.readFileSync(process.argv[2], {encoding: 'UTF-8'}) 110 | .trim() 111 | .split(/,/) 112 | .map((s) => parseInt(s, 10)); 113 | 114 | let outputs = []; 115 | let permuted = permutations([...Array(10).keys()].slice(5)); 116 | for (const permutation of permuted) { 117 | const progs = permutation.map((n) => compute(prog.slice(), 0, n)); 118 | let i = 0; 119 | 120 | let val = 0; 121 | for (;;) { 122 | let [prog, pc, val2, halted] = progs[i]; 123 | [prog, pc, val2, halted] = compute(prog, pc, val); 124 | if (halted) { 125 | break; 126 | } else { 127 | val = val2; 128 | } 129 | progs[i] = [prog, pc, val, halted]; 130 | 131 | i += 1; 132 | i %= progs.length; 133 | } 134 | outputs.push(val); 135 | } 136 | console.log(Math.max.apply(null, outputs)); 137 | -------------------------------------------------------------------------------- /day07/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import itertools 3 | from typing import Generator 4 | from typing import List 5 | from typing import Optional 6 | 7 | import pytest 8 | 9 | from support import timing 10 | 11 | 12 | def run(prog: List[int], n: int) -> Generator[Optional[int], int, None]: 13 | read_n = False 14 | 15 | def parameter(instr: int, n: int) -> int: 16 | mode = instr // (10 ** (n + 1)) % 10 17 | if mode == 0: 18 | return prog[prog[pc + n]] 19 | elif mode == 1: 20 | return prog[pc + n] 21 | else: 22 | raise NotImplementedError(mode) 23 | 24 | pc = 0 25 | while pc < len(prog): 26 | instr = prog[pc] 27 | opc = instr % 100 28 | if opc == 99: 29 | return 30 | elif opc == 1: 31 | prog[prog[pc + 3]] = parameter(instr, 1) + parameter(instr, 2) 32 | pc += 4 33 | elif opc == 2: 34 | prog[prog[pc + 3]] = parameter(instr, 1) * parameter(instr, 2) 35 | pc += 4 36 | elif opc == 3: 37 | if not read_n: 38 | prog[prog[pc + 1]] = n 39 | read_n = True 40 | else: 41 | prog[prog[pc + 1]] = yield None 42 | pc += 2 43 | elif opc == 4: 44 | yield parameter(instr, 1) 45 | pc += 2 46 | elif opc == 5: 47 | if parameter(instr, 1): 48 | pc = parameter(instr, 2) 49 | else: 50 | pc += 3 51 | elif opc == 6: 52 | if not parameter(instr, 1): 53 | pc = parameter(instr, 2) 54 | else: 55 | pc += 3 56 | elif opc == 7: 57 | if parameter(instr, 1) < parameter(instr, 2): 58 | prog[prog[pc + 3]] = 1 59 | else: 60 | prog[prog[pc + 3]] = 0 61 | pc += 4 62 | elif opc == 8: 63 | if parameter(instr, 1) == parameter(instr, 2): 64 | prog[prog[pc + 3]] = 1 65 | else: 66 | prog[prog[pc + 3]] = 0 67 | pc += 4 68 | else: 69 | raise AssertionError(f'unreachable? {prog} {pc}') 70 | raise AssertionError(f'unreachable? {prog} {pc}') 71 | 72 | 73 | def compute(s: str) -> int: 74 | prog = [int(part) for part in s.strip().split(',')] 75 | outputs = [] 76 | for permutation in itertools.permutations(range(5, 10)): 77 | progs = [run(prog[:], n) for n in permutation] 78 | progs_cycle = itertools.cycle(progs) 79 | 80 | val = 0 81 | try: 82 | while True: 83 | prog_gen = next(progs_cycle) 84 | retrieved = next(prog_gen) 85 | assert retrieved is None 86 | retrieved = prog_gen.send(val) 87 | assert isinstance(retrieved, int) 88 | val = retrieved 89 | except StopIteration: 90 | outputs.append(val) 91 | 92 | outputs.append(val) 93 | 94 | return max(outputs) 95 | 96 | 97 | @pytest.mark.parametrize( 98 | ('input_s', 'expected'), 99 | ( 100 | ( 101 | '3,26,1001,26,-4,26,3,27,1002,27,2,27,1,27,26,27,4,27,1001,28,-1,' 102 | '28,1005,28,6,99,0,0,5', 103 | 139629729, 104 | ), 105 | ), 106 | ) 107 | def test(input_s: str, expected: int) -> None: 108 | assert compute(input_s) == expected 109 | 110 | 111 | def main() -> int: 112 | parser = argparse.ArgumentParser() 113 | parser.add_argument('data_file') 114 | args = parser.parse_args() 115 | 116 | with open(args.data_file) as f, timing(): 117 | print(compute(f.read())) 118 | 119 | return 0 120 | 121 | 122 | if __name__ == '__main__': 123 | exit(main()) 124 | -------------------------------------------------------------------------------- /day08/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day08/__init__.py -------------------------------------------------------------------------------- /day08/make_intcode_program.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import List 3 | from typing import Union 4 | 5 | """\ 6 | loop: 7 | if input[pos] == 0 8 | increment counter 9 | elif input[pos] == 1 10 | increment 1s counter 11 | else: # elif input[pos] == 2 12 | increment 2s counter 13 | 14 | increment i 15 | increment pos 16 | if i == 25 * 6: 17 | if min_zeros > counter: 18 | answer = 1s counter * 2s counter 19 | min_zeros = counter 20 | i = 0 21 | counter = 0 22 | 1s counter = 0 23 | 2s counter = 0 24 | 25 | if pos == 25 * 6 * 100: 26 | output answer 27 | halt 28 | 29 | goto loop 30 | """ 31 | 32 | """\ 33 | jit $prog[pos] +7 34 | add $counter_0 1 $counter_0 35 | jit 1 +18 36 | eq $prog[pos] 1 $comp_check 37 | jif $comp_check +7 38 | add $counter_1 1 $counter_1 39 | jit 1 +4 40 | add $counter_2 1 $counter_2 41 | 42 | add $i 1 $i 43 | add $1 1 $1 44 | add $11 1 $11 45 | 46 | eq $i 25 * 6 $comp_check 47 | jif $comp_check 0 48 | lt $counter $min_zero $comp_check 49 | jif $comp_check +8 50 | mult $counter_1 $counter_2 $answer 51 | add $counter 0 $min_zeros 52 | 53 | add 0 0 $i 54 | add 0 0 $counter 55 | add 0 0 $counter_1 56 | add 0 0 $counter_2 57 | 58 | eq $pos 25 * 6 * 100 $comp_check 59 | jif $comp_check 0 60 | 61 | output $answer 62 | halt 63 | """ 64 | 65 | 66 | def main() -> int: 67 | prog: List[Union[int, str]] = [ 68 | 1005, '$pos', '+7', # $pos here is @1 69 | 1001, '$counter_0', 1, '$counter_0', 70 | 1105, 1, '+18', 71 | 1008, '$pos', 1, '$comp_check', # $pos here is @11 72 | 1006, '$comp_check', '+7', 73 | 1001, '$counter_1', 1, '$counter_1', 74 | 1105, 1, '+4', 75 | 1001, '$counter_2', 1, '$counter_2', 76 | 77 | 1001, '$i', 1, '$i', 78 | 1001, 1, 1, 1, # progress $pos above 79 | 1001, 11, 1, 11, # progress $pos above 80 | 81 | 1008, '$i', 25 * 6, '$comp_check', 82 | 1006, '$comp_check', 0, 83 | 7, '$counter_0', '$min_zero', '$comp_check', 84 | 1006, '$comp_check', '+8', 85 | 2, '$counter_1', '$counter_2', '$answer', 86 | 1001, '$counter_0', 0, '$min_zero', 87 | 88 | 1101, 0, 0, '$i', 89 | 1101, 0, 0, '$counter_0', 90 | 1101, 0, 0, '$counter_1', 91 | 1101, 0, 0, '$counter_2', 92 | 1001, '$n', 1, '$n', 93 | 94 | 1008, '$n', 100, '$comp_check', 95 | 1006, '$comp_check', 0, 96 | 97 | 4, '$answer', 98 | 99, 99 | ] 100 | 101 | def add_variable(s: str, val: int) -> None: 102 | variable_pos[s] = len(prog) + len(variables) 103 | variables.append(val) 104 | 105 | variables: List[int] = [] 106 | variable_pos = {} 107 | add_variable('$comp_check', 0) 108 | add_variable('$i', 0) 109 | add_variable('$n', 0) 110 | add_variable('$min_zero', 25 * 6 + 1) 111 | add_variable('$answer', 0) 112 | add_variable('$counter_0', 0) 113 | add_variable('$counter_1', 0) 114 | add_variable('$counter_2', 0) 115 | 116 | variable_pos['$pos'] = len(prog) + len(variables) 117 | 118 | with open('input.txt') as f: 119 | data = [int(p) for p in list(f.read().strip())] 120 | 121 | for i, v in enumerate(prog): 122 | if isinstance(v, str) and v.startswith('+'): 123 | prog[i] = i + int(v) + 1 124 | 125 | final_prog = [ 126 | p if isinstance(p, int) else variable_pos[p] 127 | for p in prog 128 | ] + variables + data 129 | 130 | for k, v in variable_pos.items(): 131 | print(f'{k} => {v}', file=sys.stderr) 132 | 133 | # helpful for debugging 134 | print(str(final_prog[:100]), file=sys.stderr) 135 | print(','.join(str(p) for p in final_prog)) 136 | return 0 137 | 138 | 139 | if __name__ == '__main__': 140 | exit(main()) 141 | -------------------------------------------------------------------------------- /day08/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import pytest 4 | 5 | from support import timing 6 | 7 | 8 | def compute(s: str) -> int: 9 | layer_size = 25 * 6 10 | s = s.strip() 11 | n_layers = len(s) // layer_size 12 | layers = [ 13 | s[i * layer_size:layer_size + i * layer_size] 14 | for i in range(n_layers) 15 | ] 16 | min_layer = min(layers, key=lambda s: s.count('0')) 17 | return min_layer.count('1') * min_layer.count('2') 18 | 19 | 20 | @pytest.mark.parametrize( 21 | ('input_s', 'expected'), 22 | ( 23 | # put given test cases here 24 | ), 25 | ) 26 | def test(input_s: str, expected: int) -> None: 27 | assert compute(input_s) == expected 28 | 29 | 30 | def main() -> int: 31 | parser = argparse.ArgumentParser() 32 | parser.add_argument('data_file') 33 | args = parser.parse_args() 34 | 35 | with open(args.data_file) as f, timing(): 36 | print(compute(f.read())) 37 | 38 | return 0 39 | 40 | 41 | if __name__ == '__main__': 42 | exit(main()) 43 | -------------------------------------------------------------------------------- /day08/part2.lua: -------------------------------------------------------------------------------- 1 | if #arg ~= 1 then 2 | print(string.format("usage: %s FILENAME", arg[0])) 3 | os.exit(1) 4 | end 5 | 6 | local fh = assert(io.open(arg[1], "r")) 7 | local contents = assert(fh:read("a")) 8 | fh:close() 9 | 10 | for y=0,5 do 11 | for x=0,24 do 12 | for layer=0,99 do 13 | local i = 1 + (6 * 25 * layer) + (25 * y) + x 14 | local c = contents:sub(i,i) 15 | if c ~= "2" then 16 | c = c:gsub("0", " ") 17 | io.write(c) 18 | break 19 | end 20 | end 21 | end 22 | io.write("\n") 23 | end 24 | -------------------------------------------------------------------------------- /day08/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | from support import timing 4 | 5 | 6 | def compute(s: str) -> None: 7 | layer_size = 25 * 6 8 | s = s.strip() 9 | n_layers = len(s) // layer_size 10 | layers = [ 11 | s[i * layer_size:layer_size + i * layer_size] 12 | for i in range(n_layers) 13 | ] 14 | 15 | for y in range(6): 16 | for x in range(25): 17 | for layer in layers: 18 | if layer[y * 25 + x] != '2': 19 | print(layer[y * 25 + x].replace('0', ' '), end='') 20 | break 21 | else: 22 | print('?', end='') 23 | print() 24 | 25 | 26 | def main() -> int: 27 | parser = argparse.ArgumentParser() 28 | parser.add_argument('data_file') 29 | args = parser.parse_args() 30 | 31 | with open(args.data_file) as f, timing(): 32 | compute(f.read()) 33 | 34 | return 0 35 | 36 | 37 | if __name__ == '__main__': 38 | exit(main()) 39 | -------------------------------------------------------------------------------- /day09/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day09/__init__.py -------------------------------------------------------------------------------- /day09/input.txt: -------------------------------------------------------------------------------- 1 | 1102,34463338,34463338,63,1007,63,34463338,63,1005,63,53,1101,3,0,1000,109,988,209,12,9,1000,209,6,209,3,203,0,1008,1000,1,63,1005,63,65,1008,1000,2,63,1005,63,904,1008,1000,0,63,1005,63,58,4,25,104,0,99,4,0,104,0,99,4,17,104,0,99,0,0,1101,35,0,1007,1102,30,1,1013,1102,37,1,1017,1101,23,0,1006,1101,0,32,1008,1102,1,29,1000,1101,0,38,1010,1101,0,24,1002,1101,33,0,1003,1101,1,0,1021,1102,31,1,1019,1101,27,0,1014,1102,20,1,1005,1101,0,0,1020,1102,1,892,1027,1101,895,0,1026,1102,39,1,1015,1102,1,370,1029,1102,1,28,1001,1102,34,1,1012,1101,25,0,1016,1101,0,375,1028,1101,36,0,1018,1101,0,21,1004,1102,1,26,1009,1101,0,249,1022,1101,0,660,1025,1101,0,665,1024,1102,1,22,1011,1102,242,1,1023,109,5,2102,1,3,63,1008,63,31,63,1005,63,205,1001,64,1,64,1105,1,207,4,187,1002,64,2,64,109,8,21102,40,1,5,1008,1018,37,63,1005,63,227,1105,1,233,4,213,1001,64,1,64,1002,64,2,64,109,7,2105,1,3,1001,64,1,64,1106,0,251,4,239,1002,64,2,64,109,-7,1201,-7,0,63,1008,63,20,63,1005,63,271,1106,0,277,4,257,1001,64,1,64,1002,64,2,64,109,-10,1208,0,33,63,1005,63,295,4,283,1106,0,299,1001,64,1,64,1002,64,2,64,109,-6,1207,4,27,63,1005,63,319,1001,64,1,64,1105,1,321,4,305,1002,64,2,64,109,12,1207,-1,33,63,1005,63,339,4,327,1105,1,343,1001,64,1,64,1002,64,2,64,109,6,1206,6,355,1106,0,361,4,349,1001,64,1,64,1002,64,2,64,109,21,2106,0,-8,4,367,1106,0,379,1001,64,1,64,1002,64,2,64,109,-29,1202,0,1,63,1008,63,36,63,1005,63,403,1001,64,1,64,1105,1,405,4,385,1002,64,2,64,109,11,21107,41,40,-6,1005,1012,421,1105,1,427,4,411,1001,64,1,64,1002,64,2,64,109,-11,2101,0,-4,63,1008,63,33,63,1005,63,453,4,433,1001,64,1,64,1106,0,453,1002,64,2,64,109,-7,21108,42,40,10,1005,1010,469,1105,1,475,4,459,1001,64,1,64,1002,64,2,64,109,1,1201,4,0,63,1008,63,20,63,1005,63,497,4,481,1105,1,501,1001,64,1,64,1002,64,2,64,109,5,21107,43,44,5,1005,1011,523,4,507,1001,64,1,64,1106,0,523,1002,64,2,64,109,20,21108,44,44,-7,1005,1019,541,4,529,1106,0,545,1001,64,1,64,1002,64,2,64,109,2,1205,-8,561,1001,64,1,64,1106,0,563,4,551,1002,64,2,64,109,-23,2108,22,0,63,1005,63,583,1001,64,1,64,1105,1,585,4,569,1002,64,2,64,109,-6,2107,30,1,63,1005,63,605,1001,64,1,64,1105,1,607,4,591,1002,64,2,64,109,23,1205,-1,621,4,613,1105,1,625,1001,64,1,64,1002,64,2,64,109,-19,2102,1,-3,63,1008,63,29,63,1005,63,647,4,631,1106,0,651,1001,64,1,64,1002,64,2,64,109,28,2105,1,-7,4,657,1106,0,669,1001,64,1,64,1002,64,2,64,109,-17,1206,6,687,4,675,1001,64,1,64,1105,1,687,1002,64,2,64,109,2,21101,45,0,1,1008,1017,42,63,1005,63,707,1106,0,713,4,693,1001,64,1,64,1002,64,2,64,109,-6,2101,0,-3,63,1008,63,34,63,1005,63,733,1105,1,739,4,719,1001,64,1,64,1002,64,2,64,109,3,21101,46,0,1,1008,1014,46,63,1005,63,761,4,745,1106,0,765,1001,64,1,64,1002,64,2,64,109,5,21102,47,1,-7,1008,1011,47,63,1005,63,787,4,771,1105,1,791,1001,64,1,64,1002,64,2,64,109,-24,2108,24,8,63,1005,63,813,4,797,1001,64,1,64,1106,0,813,1002,64,2,64,109,5,1208,10,29,63,1005,63,829,1105,1,835,4,819,1001,64,1,64,1002,64,2,64,109,7,2107,23,-4,63,1005,63,853,4,841,1105,1,857,1001,64,1,64,1002,64,2,64,109,-2,1202,0,1,63,1008,63,21,63,1005,63,879,4,863,1105,1,883,1001,64,1,64,1002,64,2,64,109,15,2106,0,8,1106,0,901,4,889,1001,64,1,64,4,64,99,21102,1,27,1,21102,915,1,0,1105,1,922,21201,1,51839,1,204,1,99,109,3,1207,-2,3,63,1005,63,964,21201,-2,-1,1,21101,942,0,0,1106,0,922,21201,1,0,-1,21201,-2,-3,1,21101,957,0,0,1105,1,922,22201,1,-1,-2,1105,1,968,21201,-2,0,-2,109,-3,2106,0,0 2 | -------------------------------------------------------------------------------- /day09/intdis.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import NamedTuple 3 | 4 | 5 | class Op(NamedTuple): 6 | fmt: str 7 | param_count: int 8 | 9 | 10 | OPS = { 11 | 99: Op('(hal)', 0), 12 | 1: Op('(add) {s3:4} = ({p1:4} + {p2})', 3), 13 | 2: Op('(mul) {s3:4} = ({p1:4} * {p2})', 3), 14 | 3: Op('(inp) {s1:4} = input()', 1), 15 | 4: Op('(out) output({p1})', 1), 16 | 5: Op('(jit) if {p1}: goto {p2}', 2), 17 | 6: Op('(jif) if not {p1}: goto {p2}', 2), 18 | 7: Op('(lt_) {s3:4} = ({p1:4} < {p2})', 3), 19 | 8: Op('(eq_) {s3:4} = ({p1:4} == {p2})', 3), 20 | 9: Op('(rel) rb += {p1:4}', 1), 21 | } 22 | D = Op('(___) {d1} {d2} {d3} {d4} {d5} {d6} {d7} {d8} {d9} {d10}', 10) 23 | 24 | 25 | def main() -> int: 26 | parser = argparse.ArgumentParser() 27 | parser.add_argument('filename') 28 | args = parser.parse_args() 29 | 30 | with open(args.filename) as f: 31 | prog = dict(enumerate(int(p) for p in f.read().strip().split(','))) 32 | 33 | def param(instr: int, n: int) -> str: 34 | if i + n not in prog: 35 | return '_' 36 | 37 | mode = instr // (10 ** (n + 1)) % 10 38 | if mode == 0: 39 | return f'${prog[i + n]}' 40 | elif mode == 1: 41 | return str(prog[i + n]) 42 | elif mode == 2: 43 | return f'r${prog[i + n]}' 44 | else: 45 | return '_' 46 | 47 | def store(instr: int, n: int) -> str: 48 | if i + n not in prog: 49 | return '_' 50 | 51 | mode = instr // (10 ** (n + 1)) % 10 52 | if mode == 0: 53 | return f'${prog[i + n]}' 54 | elif mode == 2: 55 | return f'r${prog[i + n]}' 56 | else: 57 | return '_' 58 | 59 | def d(n: int) -> str: 60 | if i + n in prog: 61 | return str(prog[i + n]) 62 | else: 63 | return '_' 64 | 65 | data = False 66 | i = 0 67 | while i < len(prog): 68 | instr = prog[i] 69 | opc = instr % 100 70 | if data or opc not in OPS: 71 | data = True 72 | op = D 73 | else: 74 | op = OPS[opc] 75 | 76 | params = { 77 | 'p1': param(instr, 1), 78 | 'p2': param(instr, 2), 79 | 's1': store(instr, 1), 80 | 's3': store(instr, 3), 81 | **{f'd{j}': d(j) for j in range(1, 11)}, 82 | } 83 | print(f'{i:{len(str(len(prog))) + 1}}| {op.fmt.format(**params)}') 84 | i += 1 + op.param_count 85 | 86 | return 0 87 | 88 | 89 | if __name__ == '__main__': 90 | exit(main()) 91 | -------------------------------------------------------------------------------- /day09/part1.nim: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import sequtils 4 | import strutils 5 | import sugar 6 | import system 7 | import tables 8 | 9 | if paramCount() != 1: 10 | stderr.writeLine("usage: ", paramStr(0), " FILENAME") 11 | quit(1) 12 | 13 | let progL = readFile(os.paramStr(1)).strip().split(',').map(x => parseInt(x)) 14 | var prog = initTable[int, int]() 15 | for i, val in progL: 16 | prog[i] = val 17 | var pc = 0 18 | var rb = 0 19 | 20 | proc parameter(instr: int, n: int): int= 21 | result = case int(instr / (int(10) ^ int(n + 1))) mod 10: 22 | of 0: 23 | prog.getOrDefault(prog.getOrDefault(pc + n, 0), 0) 24 | of 1: 25 | prog.getOrDefault(pc + n) 26 | of 2: 27 | prog.getOrDefault(rb + prog.getOrDefault(pc + n)) 28 | else: 29 | raiseAssert("unreachable") 30 | 31 | proc store(instr: int, n: int): int= 32 | result = case int(instr / (int(10) ^ int(n + 1))) mod 10: 33 | of 0: 34 | prog.getOrDefault(pc + n, 0) 35 | of 2: 36 | rb + prog.getOrDefault(pc + n) 37 | else: 38 | raiseAssert("unreachable") 39 | 40 | while true: 41 | let instr = prog[pc] 42 | let opc = instr mod 100 43 | case opc: 44 | of 99: 45 | quit(0) 46 | of 1: 47 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 48 | pc += 4 49 | of 2: 50 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 51 | pc += 4 52 | of 3: 53 | prog[store(instr, 1)] = 1 # the hardcoded input 54 | pc += 2 55 | of 4: 56 | echo parameter(instr, 1) 57 | pc += 2 58 | of 5: 59 | if parameter(instr, 1) != 0: 60 | pc = parameter(instr, 2) 61 | else: 62 | pc += 3 63 | of 6: 64 | if parameter(instr, 1) == 0: 65 | pc = parameter(instr, 2) 66 | else: 67 | pc += 3 68 | of 7: 69 | if parameter(instr, 1) < parameter(instr, 2): 70 | prog[store(instr, 3)] = 1 71 | else: 72 | prog[store(instr, 3)] = 0 73 | pc += 4 74 | of 8: 75 | if parameter(instr, 1) == parameter(instr, 2): 76 | prog[store(instr, 3)] = 1 77 | else: 78 | prog[store(instr, 3)] = 0 79 | pc += 4 80 | of 9: 81 | rb += parameter(instr, 1) 82 | pc += 2 83 | else: 84 | raiseAssert("unreachable") 85 | -------------------------------------------------------------------------------- /day09/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | from support import timing 7 | 8 | 9 | def run(prog: Dict[int, int], fn: Callable[[int], None]) -> None: 10 | def parameter(instr: int, n: int) -> int: 11 | mode = instr // (10 ** (n + 1)) % 10 12 | if mode == 0: 13 | return prog[prog[pc + n]] 14 | elif mode == 1: 15 | return prog[pc + n] 16 | elif mode == 2: 17 | return prog[rb + prog[pc + n]] 18 | else: 19 | raise NotImplementedError(mode) 20 | 21 | def store(instr: int, n: int) -> int: 22 | mode = instr // (10 ** (n + 1)) % 10 23 | if mode == 0: 24 | return prog[pc + n] 25 | elif mode == 2: 26 | return rb + prog[pc + n] 27 | else: 28 | raise NotImplementedError(mode) 29 | 30 | rb = 0 31 | pc = 0 32 | while pc < len(prog): 33 | instr = prog[pc] 34 | opc = instr % 100 35 | if opc == 99: 36 | return 37 | elif opc == 1: 38 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 39 | pc += 4 40 | elif opc == 2: 41 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 42 | pc += 4 43 | elif opc == 3: 44 | prog[store(instr, 1)] = 1 # the hardcoded input 45 | pc += 2 46 | elif opc == 4: 47 | fn(parameter(instr, 1)) 48 | pc += 2 49 | elif opc == 5: 50 | if parameter(instr, 1): 51 | pc = parameter(instr, 2) 52 | else: 53 | pc += 3 54 | elif opc == 6: 55 | if not parameter(instr, 1): 56 | pc = parameter(instr, 2) 57 | else: 58 | pc += 3 59 | elif opc == 7: 60 | if parameter(instr, 1) < parameter(instr, 2): 61 | prog[store(instr, 3)] = 1 62 | else: 63 | prog[store(instr, 3)] = 0 64 | pc += 4 65 | elif opc == 8: 66 | if parameter(instr, 1) == parameter(instr, 2): 67 | prog[store(instr, 3)] = 1 68 | else: 69 | prog[store(instr, 3)] = 0 70 | pc += 4 71 | elif opc == 9: 72 | rb += parameter(instr, 1) 73 | pc += 2 74 | else: 75 | raise AssertionError(f'unreachable? {prog} {pc}') 76 | raise AssertionError(f'unreachable? {prog} {pc}') 77 | 78 | 79 | def compute(s: str) -> None: 80 | prog = [int(part) for part in s.strip().split(',')] 81 | prog_d = collections.defaultdict(int, enumerate(prog)) 82 | run(prog_d, print) 83 | 84 | 85 | def main() -> int: 86 | parser = argparse.ArgumentParser() 87 | parser.add_argument('data_file') 88 | args = parser.parse_args() 89 | 90 | with open(args.data_file) as f, timing(): 91 | compute(f.read()) 92 | 93 | return 0 94 | 95 | 96 | if __name__ == '__main__': 97 | exit(main()) 98 | -------------------------------------------------------------------------------- /day09/part2.nim: -------------------------------------------------------------------------------- 1 | import math 2 | import os 3 | import sequtils 4 | import strutils 5 | import sugar 6 | import system 7 | import tables 8 | 9 | if paramCount() != 1: 10 | stderr.writeLine("usage: ", paramStr(0), " FILENAME") 11 | quit(1) 12 | 13 | let progL = readFile(os.paramStr(1)).strip().split(',').map(x => parseInt(x)) 14 | var prog = initTable[int, int]() 15 | for i, val in progL: 16 | prog[i] = val 17 | var pc = 0 18 | var rb = 0 19 | 20 | proc parameter(instr: int, n: int): int= 21 | result = case int(instr / (int(10) ^ int(n + 1))) mod 10: 22 | of 0: 23 | prog.getOrDefault(prog.getOrDefault(pc + n, 0), 0) 24 | of 1: 25 | prog.getOrDefault(pc + n) 26 | of 2: 27 | prog.getOrDefault(rb + prog.getOrDefault(pc + n)) 28 | else: 29 | raiseAssert("unreachable") 30 | 31 | proc store(instr: int, n: int): int= 32 | result = case int(instr / (int(10) ^ int(n + 1))) mod 10: 33 | of 0: 34 | prog.getOrDefault(pc + n, 0) 35 | of 2: 36 | rb + prog.getOrDefault(pc + n) 37 | else: 38 | raiseAssert("unreachable") 39 | 40 | while true: 41 | let instr = prog[pc] 42 | let opc = instr mod 100 43 | case opc: 44 | of 99: 45 | quit(0) 46 | of 1: 47 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 48 | pc += 4 49 | of 2: 50 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 51 | pc += 4 52 | of 3: 53 | prog[store(instr, 1)] = 2 # the hardcoded input 54 | pc += 2 55 | of 4: 56 | echo parameter(instr, 1) 57 | pc += 2 58 | of 5: 59 | if parameter(instr, 1) != 0: 60 | pc = parameter(instr, 2) 61 | else: 62 | pc += 3 63 | of 6: 64 | if parameter(instr, 1) == 0: 65 | pc = parameter(instr, 2) 66 | else: 67 | pc += 3 68 | of 7: 69 | if parameter(instr, 1) < parameter(instr, 2): 70 | prog[store(instr, 3)] = 1 71 | else: 72 | prog[store(instr, 3)] = 0 73 | pc += 4 74 | of 8: 75 | if parameter(instr, 1) == parameter(instr, 2): 76 | prog[store(instr, 3)] = 1 77 | else: 78 | prog[store(instr, 3)] = 0 79 | pc += 4 80 | of 9: 81 | rb += parameter(instr, 1) 82 | pc += 2 83 | else: 84 | raiseAssert("unreachable") 85 | -------------------------------------------------------------------------------- /day09/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | from support import timing 7 | 8 | 9 | def run( 10 | prog: Dict[int, int], 11 | in_fn: Callable[[], int], 12 | out_fn: Callable[[int], None], 13 | ) -> None: 14 | def parameter(instr: int, n: int) -> int: 15 | mode = instr // (10 ** (n + 1)) % 10 16 | if mode == 0: 17 | return prog[prog[pc + n]] 18 | elif mode == 1: 19 | return prog[pc + n] 20 | elif mode == 2: 21 | return prog[rb + prog[pc + n]] 22 | else: 23 | raise NotImplementedError(mode) 24 | 25 | def store(instr: int, n: int) -> int: 26 | mode = instr // (10 ** (n + 1)) % 10 27 | if mode == 0: 28 | return prog[pc + n] 29 | elif mode == 2: 30 | return rb + prog[pc + n] 31 | else: 32 | raise NotImplementedError(mode) 33 | 34 | rb = 0 35 | pc = 0 36 | while pc < len(prog): 37 | instr = prog[pc] 38 | opc = instr % 100 39 | if opc == 99: 40 | return 41 | elif opc == 1: 42 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 43 | pc += 4 44 | elif opc == 2: 45 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 46 | pc += 4 47 | elif opc == 3: 48 | prog[store(instr, 1)] = in_fn() 49 | pc += 2 50 | elif opc == 4: 51 | out_fn(parameter(instr, 1)) 52 | pc += 2 53 | elif opc == 5: 54 | if parameter(instr, 1): 55 | pc = parameter(instr, 2) 56 | else: 57 | pc += 3 58 | elif opc == 6: 59 | if not parameter(instr, 1): 60 | pc = parameter(instr, 2) 61 | else: 62 | pc += 3 63 | elif opc == 7: 64 | if parameter(instr, 1) < parameter(instr, 2): 65 | prog[store(instr, 3)] = 1 66 | else: 67 | prog[store(instr, 3)] = 0 68 | pc += 4 69 | elif opc == 8: 70 | if parameter(instr, 1) == parameter(instr, 2): 71 | prog[store(instr, 3)] = 1 72 | else: 73 | prog[store(instr, 3)] = 0 74 | pc += 4 75 | elif opc == 9: 76 | rb += parameter(instr, 1) 77 | pc += 2 78 | else: 79 | raise AssertionError(f'unreachable? {prog} {pc}') 80 | raise AssertionError(f'unreachable? {prog} {pc}') 81 | 82 | 83 | def compute(s: str) -> None: 84 | prog = [int(part) for part in s.strip().split(',')] 85 | prog_d = collections.defaultdict(int, enumerate(prog)) 86 | 87 | def in_fn() -> int: 88 | return 2 89 | 90 | def out_fn(n: int) -> None: 91 | print(n) 92 | 93 | run(prog_d, in_fn, out_fn) 94 | 95 | 96 | def main() -> int: 97 | parser = argparse.ArgumentParser() 98 | parser.add_argument('data_file') 99 | args = parser.parse_args() 100 | 101 | with open(args.data_file) as f, timing(): 102 | compute(f.read()) 103 | 104 | return 0 105 | 106 | 107 | if __name__ == '__main__': 108 | exit(main()) 109 | -------------------------------------------------------------------------------- /day10/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day10/__init__.py -------------------------------------------------------------------------------- /day10/input.txt: -------------------------------------------------------------------------------- 1 | #....#.....#...#.#.....#.#..#....# 2 | #..#..##...#......#.....#..###.#.# 3 | #......#.#.#.....##....#.#.....#.. 4 | ..#.#...#.......#.##..#........... 5 | .##..#...##......##.#.#........... 6 | .....#.#..##...#..##.....#...#.##. 7 | ....#.##.##.#....###.#........#### 8 | ..#....#..####........##.........# 9 | ..#...#......#.#..#..#.#.##......# 10 | .............#.#....##.......#...# 11 | .#.#..##.#.#.#.#.......#.....#.... 12 | .....##.###..#.....#.#..###.....## 13 | .....#...#.#.#......#.#....##..... 14 | ##.#.....#...#....#...#..#....#.#. 15 | ..#.............###.#.##....#.#... 16 | ..##.#.........#.##.####.........# 17 | ##.#...###....#..#...###..##..#..# 18 | .........#.#.....#........#....... 19 | #.......#..#.#.#..##.....#.#.....# 20 | ..#....#....#.#.##......#..#.###.. 21 | ......##.##.##...#...##.#...###... 22 | .#.....#...#........#....#.###.... 23 | .#.#.#..#............#..........#. 24 | ..##.....#....#....##..#.#.......# 25 | ..##.....#.#...................... 26 | .#..#...#....#.#.....#.........#.. 27 | ........#.............#.#......... 28 | #...#.#......#.##....#...#.#.#...# 29 | .#.....#.#.....#.....#.#.##......# 30 | ..##....#.....#.....#....#.##..#.. 31 | #..###.#.#....#......#...#........ 32 | ..#......#..#....##...#.#.#...#..# 33 | .#.##.#.#.....#..#..#........##... 34 | ....#...##.##.##......#..#..##.... 35 | -------------------------------------------------------------------------------- /day10/part1.ps1: -------------------------------------------------------------------------------- 1 | if ($args.Length -ne 1) { 2 | Write-Error "usage: FILENAME" 3 | Exit 1 4 | } 5 | 6 | function Get-GCD ($x, $y) { 7 | if ($y -eq 0) { $x } else { Get-GCD $y ($x%$y) } 8 | } 9 | 10 | $coords = @() 11 | $lines = Get-Content $args[0] 12 | for ($y=0; $y -lt $lines.Length; $y++) { 13 | for ($x=0; $x -lt $lines[$y].Length; $x++) { 14 | if ($lines[$y][$x] -eq '#') { 15 | $coords += , @($x,$y) 16 | } 17 | } 18 | } 19 | 20 | $counts = @() 21 | foreach ($coord in $coords) { 22 | $count = 0 23 | foreach ($other_coord in $coords) { 24 | if ("$coord" -eq "$other_coord") { 25 | continue 26 | } 27 | $x_diff = $other_coord[0] - $coord[0] 28 | if ($x_diff -ge 0) { 29 | $x_sign = 1 30 | } else { 31 | $x_sign = -1 32 | } 33 | $y_diff = $other_coord[1] - $coord[1] 34 | if ($y_diff -ge 0) { 35 | $y_sign = 1 36 | } else { 37 | $y_sign = -1 38 | } 39 | if ($x_diff -eq 0) { 40 | $y_diff = $y_sign * 1 41 | } 42 | if ($y_diff -eq 0) { 43 | $x_diff = $x_sign * 1 44 | } 45 | $gcd = [math]::abs($(Get-GCD $x_diff $y_diff)) 46 | 47 | $x_diff /= $gcd 48 | $y_diff /= $gcd 49 | 50 | $broken = $false 51 | $new_x = $coord[0] + $x_diff 52 | $new_y = $coord[1] + $y_diff 53 | while ($new_x -ne $other_coord[0] -or $new_y -ne $other_coord[1]) { 54 | $found = $false 55 | foreach ($other_coord2 in $coords) { 56 | if ($new_x -eq $other_coord2[0] -and $new_y -eq $other_coord2[1]) { 57 | $found = $true 58 | break 59 | } 60 | } 61 | if ($found) { 62 | $broken = $true 63 | break 64 | } 65 | $new_x += $x_diff 66 | $new_y += $y_diff 67 | } 68 | if (!$broken) { 69 | $count++ 70 | } 71 | } 72 | $counts += , @($count,$coord) 73 | } 74 | 75 | $max = 0 76 | foreach ($count in $counts) { 77 | $max = [math]::max($max, $count[0]) 78 | } 79 | 80 | "$max" 81 | -------------------------------------------------------------------------------- /day10/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import math 3 | from typing import NamedTuple 4 | 5 | import pytest 6 | 7 | from support import timing 8 | 9 | 10 | class Coord(NamedTuple): 11 | x: int 12 | y: int 13 | 14 | 15 | def compute(s: str) -> int: 16 | coords = set() 17 | for y, line in enumerate(s.strip().splitlines()): 18 | for x, c in enumerate(line): 19 | if c == '#': 20 | coords.add(Coord(x, y)) 21 | 22 | counts = [] 23 | for coord in coords: 24 | count = 0 25 | for other_coord in coords: 26 | if coord == other_coord: 27 | continue 28 | x_diff = other_coord.x - coord.x 29 | x_sign = 1 if x_diff >= 0 else -1 30 | y_diff = other_coord.y - coord.y 31 | y_sign = 1 if y_diff >= 0 else -1 32 | if x_diff == 0: 33 | y_diff = y_sign * 1 34 | if y_diff == 0: 35 | x_diff = x_sign * 1 36 | gcd = math.gcd(x_diff, y_diff) 37 | slope = Coord(x_diff // gcd, y_diff // gcd) 38 | 39 | new = Coord(coord.x + slope.x, coord.y + slope.y) 40 | while new != other_coord: 41 | if new in coords: 42 | break 43 | new = Coord(new.x + slope.x, new.y + slope.y) 44 | else: 45 | count += 1 46 | counts.append((count, coord)) 47 | 48 | max_val, _ = max(counts) 49 | return max_val 50 | 51 | 52 | INPUT1 = '''\ 53 | ......#.#. 54 | #..#.#.... 55 | ..#######. 56 | .#.#.###.. 57 | .#..#..... 58 | ..#....#.# 59 | #..#....#. 60 | .##.#..### 61 | ##...#..#. 62 | .#....#### 63 | ''' 64 | 65 | 66 | @pytest.mark.parametrize( 67 | ('input_s', 'expected'), 68 | ( 69 | (INPUT1, 33), 70 | ), 71 | ) 72 | def test(input_s: str, expected: int) -> None: 73 | assert compute(input_s) == expected 74 | 75 | 76 | def main() -> int: 77 | parser = argparse.ArgumentParser() 78 | parser.add_argument('data_file') 79 | args = parser.parse_args() 80 | 81 | with open(args.data_file) as f, timing(): 82 | print(compute(f.read())) 83 | 84 | return 0 85 | 86 | 87 | if __name__ == '__main__': 88 | exit(main()) 89 | -------------------------------------------------------------------------------- /day10/part2.ps1: -------------------------------------------------------------------------------- 1 | if ($args.Length -ne 1) { 2 | Write-Error "usage: FILENAME" 3 | Exit 1 4 | } 5 | 6 | function Get-GCD ($x, $y) { 7 | if ($y -eq 0) { $x } else { Get-GCD $y ($x%$y) } 8 | } 9 | 10 | function Get-Slope-Key($x, $y) { 11 | if ($y -lt 0 -and $x -eq 0) { 12 | 10000 13 | } elseif ($y -lt 0 -and $x -gt 0) { 14 | 9000 + [math]::abs($y / $x) 15 | } elseif ($x -gt 0 -and $y -eq 0) { 16 | 8000 17 | } elseif ($x -gt 0 -and $y -gt 0) { 18 | 7000 + [math]::abs($x / $y) 19 | } elseif ($y -gt 0 -and $x -eq 0) { 20 | 6000 21 | } elseif ($y -gt 0 -and $x -lt 0) { 22 | 5000 + $y / $x 23 | } elseif ($y -eq 0 -and $x -lt 0) { 24 | 4000 25 | } else { 26 | 3000 + [math]::abs($x / $y) 27 | } 28 | } 29 | 30 | 31 | $coords = @{} 32 | $lines = Get-Content $args[0] 33 | for ($y=0; $y -lt $lines.Length; $y++) { 34 | for ($x=0; $x -lt $lines[$y].Length; $x++) { 35 | if ($lines[$y][$x] -eq '#') { 36 | if (!$coords.Contains($x)) { 37 | $coords[$x] = @{} 38 | } 39 | $coords[$x][$y] = 1 40 | } 41 | } 42 | } 43 | 44 | $coord_x = 26 45 | $coord_y = 28 46 | 47 | $slopes=@{} 48 | for ($x_i = 0; $x_i -lt $x; $x_i++) { 49 | for ($y_i = 0; $y_i -lt $y; $y_i++) { 50 | if ($x_i -eq $coord_x -and $y_i -eq $coord_y) { 51 | continue 52 | } 53 | $x_diff = $x_i - $coord_x 54 | if ($x_diff -ge 0) { 55 | $x_sign = 1 56 | } else { 57 | $x_sign = -1 58 | } 59 | $y_diff = $y_i - $coord_y 60 | if ($y_diff -ge 0) { 61 | $y_sign = 1 62 | } else { 63 | $y_sign = -1 64 | } 65 | if ($x_diff -eq 0) { 66 | $y_diff = $y_sign * 1 67 | } 68 | if ($y_diff -eq 0) { 69 | $x_diff = $x_sign * 1 70 | } 71 | $gcd = [math]::abs($(Get-GCD $x_diff $y_diff)) 72 | 73 | $x_diff /= $gcd 74 | $y_diff /= $gcd 75 | 76 | if (!$slopes.Contains($x_diff)) { 77 | $slopes[$x_diff] = @{} 78 | } 79 | $slopes[$x_diff][$y_diff] = 1 80 | } 81 | } 82 | 83 | $slopes_sorted_k = @() 84 | $slopes_sorted = @() 85 | 86 | foreach ($key_x in $slopes.Keys) { 87 | foreach ($key_y in $slopes[$key_x].Keys) { 88 | $slope_key = Get-Slope-Key $key_x $key_y 89 | for ($i = 0; $i -lt $slopes_sorted_k.Length; $i++) { 90 | if ($slopes_sorted_k[$i] -gt $slope_key) { 91 | break 92 | } 93 | } 94 | if ($i -eq 0) { 95 | $slopes_sorted_k = @($slope_key) + $slopes_sorted_k 96 | $slopes_sorted = , @(@($key_x,$key_y)) + $slopes_sorted 97 | } else { 98 | $slopes_sorted_k = $slopes_sorted_k[0..$($i-1)] + $slope_key + $slopes_sorted_k[$i..$slopes_sorted_k.Length] 99 | $slopes_sorted = $slopes_sorted[0..$($i-1)] + , @(@($key_x,$key_y)) + $slopes_sorted[$i..$slopes_sorted.Length] 100 | } 101 | } 102 | } 103 | 104 | $count = 0 105 | while ($true) { 106 | for ($i = $slopes_sorted.Length - 1; $i -ge 0; $i--) { 107 | $slope = $slopes_sorted[$i] 108 | $new_x = $coord_x + $slope[0] 109 | $new_y = $coord_y + $slope[1] 110 | 111 | while ( 112 | 0 -le $new_x -and $new_x -lt $x -and 113 | 0 -le $new_y -and $new_y -lt $y 114 | ) { 115 | if ($coords.ContainsKey($new_x) -and $coords[$new_x].ContainsKey($new_y)) { 116 | $count++ 117 | $coords[$new_x].Remove($new_y) 118 | if ($count -eq 200) { 119 | 100 * $new_x + $new_y 120 | Exit 0 121 | } 122 | break 123 | } 124 | $new_x += $slope[0] 125 | $new_y += $slope[1] 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /day10/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import math 3 | from typing import NamedTuple 4 | from typing import Tuple 5 | 6 | from support import timing 7 | 8 | 9 | class Coord(NamedTuple): 10 | x: int 11 | y: int 12 | 13 | 14 | def slope_key(slope: Coord) -> Tuple[int, float]: 15 | if slope.y < 0 and slope.x == 0: 16 | return (10, 1.) 17 | elif slope.y < 0 and slope.x > 0: 18 | return (9, abs(slope.y / slope.x)) 19 | elif slope.x > 0 and slope.y == 0: 20 | return (8, 1.) 21 | elif slope.x > 0 and slope.y > 0: 22 | return (7, abs(slope.x / slope.y)) 23 | elif slope.y > 0 and slope.x == 0: 24 | return (6, 1.) 25 | elif slope.y > 0 and slope.x < 0: 26 | return (5, slope.y / slope.x) 27 | elif slope.y == 0 and slope.x < 0: 28 | return (4, 1.) 29 | else: 30 | return (3, abs(slope.x / slope.y)) 31 | 32 | 33 | def compute(s: str) -> int: 34 | coords = set() 35 | lines = 0 36 | for y, line in enumerate(s.strip().splitlines()): 37 | width = len(line) 38 | lines += 1 39 | for x, c in enumerate(line): 40 | if c == '#': 41 | coords.add(Coord(x, y)) 42 | 43 | coord = Coord(x=26, y=28) 44 | 45 | slopes_set = set() 46 | for x in range(width): 47 | for y in range(lines): 48 | if (x, y) == coord: 49 | continue 50 | x_diff = x - coord.x 51 | x_sign = 1 if x_diff >= 0 else -1 52 | y_diff = y - coord.y 53 | y_sign = 1 if y_diff >= 0 else -1 54 | if x_diff == 0: 55 | y_diff = y_sign * 1 56 | if y_diff == 0: 57 | x_diff = x_sign * 1 58 | gcd = math.gcd(x_diff, y_diff) 59 | slopes_set.add(Coord(x_diff // gcd, y_diff // gcd)) 60 | 61 | slopes = sorted(slopes_set, key=slope_key, reverse=True) 62 | 63 | count = 0 64 | while True: 65 | for slope in slopes: 66 | new_coord = Coord(coord.x + slope.x, coord.y + slope.y) 67 | while 0 <= new_coord.x < width and 0 <= new_coord.y < lines: 68 | if new_coord in coords: 69 | count += 1 70 | coords.remove(new_coord) 71 | if count == 200: 72 | return 100 * new_coord.x + new_coord.y 73 | break 74 | new_coord = Coord(new_coord.x + slope.x, new_coord.y + slope.y) 75 | 76 | assert False, 'unreachable' 77 | 78 | 79 | def main() -> int: 80 | parser = argparse.ArgumentParser() 81 | parser.add_argument('data_file') 82 | args = parser.parse_args() 83 | 84 | with open(args.data_file) as f, timing(): 85 | print(compute(f.read())) 86 | 87 | return 0 88 | 89 | 90 | if __name__ == '__main__': 91 | exit(main()) 92 | -------------------------------------------------------------------------------- /day11/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day11/__init__.py -------------------------------------------------------------------------------- /day11/input.txt: -------------------------------------------------------------------------------- 1 | 3,8,1005,8,330,1106,0,11,0,0,0,104,1,104,0,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,29,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,1001,8,0,51,1006,0,78,2,107,9,10,1006,0,87,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,82,2,1103,5,10,1,101,8,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,112,1006,0,23,1006,0,20,1,2,11,10,1,1007,12,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,1,8,10,4,10,101,0,8,148,3,8,102,-1,8,10,101,1,10,10,4,10,108,1,8,10,4,10,1002,8,1,170,2,101,12,10,2,5,7,10,1,102,10,10,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,1001,8,0,205,1,1004,10,10,2,6,13,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,1001,8,0,235,2,102,4,10,1006,0,16,1006,0,84,1006,0,96,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,1001,8,0,269,1006,0,49,2,1003,6,10,2,1104,14,10,1006,0,66,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,1002,8,1,305,2,1,11,10,101,1,9,9,1007,9,1020,10,1005,10,15,99,109,652,104,0,104,1,21102,838479487744,1,1,21102,1,347,0,1106,0,451,21101,666567967640,0,1,21101,358,0,0,1106,0,451,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21101,28994219048,0,1,21102,405,1,0,1105,1,451,21102,3375459559,1,1,21101,0,416,0,1106,0,451,3,10,104,0,104,0,3,10,104,0,104,0,21102,838433665892,1,1,21102,1,439,0,1106,0,451,21102,988669698816,1,1,21102,450,1,0,1105,1,451,99,109,2,21201,-1,0,1,21102,1,40,2,21101,482,0,3,21102,472,1,0,1105,1,515,109,-2,2105,1,0,0,1,0,0,1,109,2,3,10,204,-1,1001,477,478,493,4,0,1001,477,1,477,108,4,477,10,1006,10,509,1101,0,0,477,109,-2,2105,1,0,0,109,4,1201,-1,0,514,1207,-3,0,10,1006,10,532,21101,0,0,-3,22102,1,-3,1,21201,-2,0,2,21102,1,1,3,21101,551,0,0,1106,0,556,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,579,2207,-4,-2,10,1006,10,579,21201,-4,0,-4,1105,1,647,21201,-4,0,1,21201,-3,-1,2,21202,-2,2,3,21101,0,598,0,1106,0,556,21202,1,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,617,21102,0,1,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,639,22102,1,-1,1,21101,0,639,0,106,0,514,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0 2 | -------------------------------------------------------------------------------- /day11/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | import enum 4 | from typing import Callable 5 | from typing import Dict 6 | from typing import Tuple 7 | 8 | from support import timing 9 | 10 | 11 | def run( 12 | prog: Dict[int, int], 13 | in_fn: Callable[[], int], 14 | out_fn: Callable[[int], None], 15 | ) -> None: 16 | def parameter(instr: int, n: int) -> int: 17 | mode = instr // (10 ** (n + 1)) % 10 18 | if mode == 0: 19 | return prog[prog[pc + n]] 20 | elif mode == 1: 21 | return prog[pc + n] 22 | elif mode == 2: 23 | return prog[rb + prog[pc + n]] 24 | else: 25 | raise NotImplementedError(mode) 26 | 27 | def store(instr: int, n: int) -> int: 28 | mode = instr // (10 ** (n + 1)) % 10 29 | if mode == 0: 30 | return prog[pc + n] 31 | elif mode == 2: 32 | return rb + prog[pc + n] 33 | else: 34 | raise NotImplementedError(mode) 35 | 36 | rb = 0 37 | pc = 0 38 | while pc < len(prog): 39 | instr = prog[pc] 40 | opc = instr % 100 41 | 42 | if opc == 99: 43 | return 44 | elif opc == 1: 45 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 46 | pc += 4 47 | elif opc == 2: 48 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 49 | pc += 4 50 | elif opc == 3: 51 | prog[store(instr, 1)] = in_fn() 52 | pc += 2 53 | elif opc == 4: 54 | out_fn(parameter(instr, 1)) 55 | pc += 2 56 | elif opc == 5: 57 | if parameter(instr, 1): 58 | pc = parameter(instr, 2) 59 | else: 60 | pc += 3 61 | elif opc == 6: 62 | if not parameter(instr, 1): 63 | pc = parameter(instr, 2) 64 | else: 65 | pc += 3 66 | elif opc == 7: 67 | if parameter(instr, 1) < parameter(instr, 2): 68 | prog[store(instr, 3)] = 1 69 | else: 70 | prog[store(instr, 3)] = 0 71 | pc += 4 72 | elif opc == 8: 73 | if parameter(instr, 1) == parameter(instr, 2): 74 | prog[store(instr, 3)] = 1 75 | else: 76 | prog[store(instr, 3)] = 0 77 | pc += 4 78 | elif opc == 9: 79 | rb += parameter(instr, 1) 80 | pc += 2 81 | else: 82 | raise AssertionError(f'unreachable? {prog} {pc}') 83 | raise AssertionError(f'unreachable? {prog} {pc}') 84 | 85 | 86 | Dir = enum.Enum('Dir', 'UP DOWN LEFT RIGHT') 87 | RIGHT = (Dir.UP, Dir.RIGHT, Dir.DOWN, Dir.LEFT) 88 | LEFT = tuple(reversed(RIGHT)) 89 | 90 | 91 | def compute(s: str) -> int: 92 | prog = [int(part) for part in s.strip().split(',')] 93 | prog_d = collections.defaultdict(int, enumerate(prog)) 94 | 95 | painted: Dict[Tuple[int, int], int] = collections.defaultdict(int) 96 | 97 | direction = Dir.UP 98 | coord_x = 0 99 | coord_y = 0 100 | color_mode = True 101 | 102 | def in_fn() -> int: 103 | return painted[(coord_x, coord_y)] 104 | 105 | def out_fn(n: int) -> None: 106 | nonlocal color_mode 107 | nonlocal direction 108 | nonlocal coord_x 109 | nonlocal coord_y 110 | if color_mode: 111 | painted[(coord_x, coord_y)] = n 112 | else: 113 | if n == 0: 114 | direction = LEFT[(LEFT.index(direction) + 1) % 4] 115 | else: 116 | direction = RIGHT[(RIGHT.index(direction) + 1) % 4] 117 | 118 | if direction == Dir.LEFT: 119 | coord_x -= 1 120 | elif direction == Dir.RIGHT: 121 | coord_x += 1 122 | elif direction == Dir.UP: 123 | coord_y -= 1 124 | else: 125 | coord_y += 1 126 | 127 | color_mode = not color_mode 128 | 129 | run(prog_d, in_fn, out_fn) 130 | return len(painted) 131 | 132 | 133 | def main() -> int: 134 | parser = argparse.ArgumentParser() 135 | parser.add_argument('data_file') 136 | args = parser.parse_args() 137 | 138 | with open(args.data_file) as f, timing(): 139 | print(compute(f.read())) 140 | 141 | return 0 142 | 143 | 144 | if __name__ == '__main__': 145 | exit(main()) 146 | -------------------------------------------------------------------------------- /day12/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day12/__init__.py -------------------------------------------------------------------------------- /day12/input.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /day12/part1.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import kotlin.collections.MutableList 3 | import kotlin.math.abs 4 | import kotlin.system.exitProcess 5 | import kotlin.text.Regex 6 | 7 | data class Vector(val x: Int, val y: Int, val z: Int) { 8 | fun add(other: Vector): Vector { 9 | return Vector(this.x + other.x, this.y + other.y, this.z + other.z) 10 | } 11 | 12 | fun value(): Int { 13 | return abs(this.x) + abs(this.y) + abs(this.z) 14 | } 15 | 16 | override fun toString(): String { 17 | return "Vector(x=${this.x}, y=${this.y}, z=${this.z})" 18 | } 19 | } 20 | 21 | class Moon(val pos: Vector, val vel: Vector) { 22 | override fun toString(): String { 23 | return "Moon(pos=${this.pos}, vel=${this.vel})" 24 | } 25 | 26 | fun value(): Int { 27 | return this.pos.value() * this.vel.value() 28 | } 29 | 30 | companion object { 31 | val reg = Regex("") 32 | fun parse(s: String): Moon { 33 | val parsed = reg.matchEntire(s)!! 34 | return Moon( 35 | Vector( 36 | parsed.groupValues[1].toInt(), 37 | parsed.groupValues[2].toInt(), 38 | parsed.groupValues[3].toInt() 39 | ), 40 | Vector(0, 0, 0) 41 | ) 42 | } 43 | } 44 | } 45 | 46 | fun main(args: Array) { 47 | if (args.count() != 1) { 48 | println("error: supply a filename!") 49 | exitProcess(1) 50 | } 51 | 52 | var moons: MutableList = ArrayList() 53 | File(args[0]).forEachLine { moons.add(Moon.parse(it)) } 54 | 55 | for (q in 0 until 1000) { 56 | for ((i, moon) in moons.withIndex()) { 57 | var (vX, vY, vZ) = moon.vel 58 | for (o_moon in moons) { 59 | if (o_moon == moon) { 60 | continue 61 | } 62 | if (o_moon.pos.x > moon.pos.x) { 63 | vX += 1 64 | } else if (o_moon.pos.x < moon.pos.x) { 65 | vX -= 1 66 | } 67 | if (o_moon.pos.y > moon.pos.y) { 68 | vY += 1 69 | } else if (o_moon.pos.y < moon.pos.y) { 70 | vY -= 1 71 | } 72 | if (o_moon.pos.z > moon.pos.z) { 73 | vZ += 1 74 | } else if (o_moon.pos.z < moon.pos.z) { 75 | vZ -= 1 76 | } 77 | } 78 | moons[i] = Moon(moon.pos, Vector(vX, vY, vZ)) 79 | } 80 | 81 | for ((i, moon) in moons.withIndex()) { 82 | moons[i] = Moon(moon.pos.add(moon.vel), moon.vel) 83 | } 84 | } 85 | 86 | println(moons.map { it.value() }.sum()) 87 | } 88 | -------------------------------------------------------------------------------- /day12/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import re 3 | from typing import NamedTuple 4 | 5 | from support import timing 6 | 7 | INT_RE = re.compile(r'-?\d+') 8 | 9 | 10 | class Vector(NamedTuple): 11 | x: int 12 | y: int 13 | z: int 14 | 15 | def add(self, other: 'Vector') -> 'Vector': 16 | return self._replace( 17 | x=self.x + other.x, 18 | y=self.y + other.y, 19 | z=self.z + other.z, 20 | ) 21 | 22 | 23 | class Moon(NamedTuple): 24 | pos: Vector 25 | vel: Vector 26 | 27 | @classmethod 28 | def parse(cls, s: str) -> 'Moon': 29 | x_s, y_s, z_s = INT_RE.findall(s) 30 | return cls( 31 | pos=Vector(int(x_s), int(y_s), int(z_s)), 32 | vel=Vector(0, 0, 0), 33 | ) 34 | 35 | 36 | def compute(s: str) -> int: 37 | moons = [Moon.parse(line) for line in s.splitlines()] 38 | 39 | for _ in range(1000): 40 | for i, moon in enumerate(moons): 41 | v_x, v_y, v_z = moon.vel 42 | for o_moon in moons: 43 | if o_moon is moon: 44 | continue 45 | if o_moon.pos.x > moon.pos.x: 46 | v_x += 1 47 | elif o_moon.pos.x < moon.pos.x: 48 | v_x -= 1 49 | if o_moon.pos.y > moon.pos.y: 50 | v_y += 1 51 | elif o_moon.pos.y < moon.pos.y: 52 | v_y -= 1 53 | if o_moon.pos.z > moon.pos.z: 54 | v_z += 1 55 | elif o_moon.pos.z < moon.pos.z: 56 | v_z -= 1 57 | moons[i] = moon._replace(vel=Vector(v_x, v_y, v_z)) 58 | 59 | for i, moon in enumerate(moons): 60 | moons[i] = moon._replace(pos=moon.pos.add(moon.vel)) 61 | 62 | return sum( 63 | sum(abs(p) for p in moon.pos) * 64 | sum(abs(p) for p in moon.vel) 65 | for moon in moons 66 | ) 67 | 68 | 69 | def main() -> int: 70 | parser = argparse.ArgumentParser() 71 | parser.add_argument('data_file') 72 | args = parser.parse_args() 73 | 74 | with open(args.data_file) as f, timing(): 75 | print(compute(f.read())) 76 | 77 | return 0 78 | 79 | 80 | if __name__ == '__main__': 81 | exit(main()) 82 | -------------------------------------------------------------------------------- /day12/part2.kt: -------------------------------------------------------------------------------- 1 | import java.io.File 2 | import kotlin.collections.MutableList 3 | import kotlin.math.abs 4 | import kotlin.system.exitProcess 5 | import kotlin.text.Regex 6 | 7 | data class Vector(val x: Long, val y: Long, val z: Long) { 8 | fun add(other: Vector): Vector { 9 | return Vector(this.x + other.x, this.y + other.y, this.z + other.z) 10 | } 11 | 12 | fun value(): Long { 13 | return abs(this.x) + abs(this.y) + abs(this.z) 14 | } 15 | 16 | override fun toString(): String { 17 | return "Vector(x=${this.x}, y=${this.y}, z=${this.z})" 18 | } 19 | } 20 | 21 | data class Moon(val pos: Vector, val vel: Vector) { 22 | override fun toString(): String { 23 | return "Moon(pos=${this.pos}, vel=${this.vel})" 24 | } 25 | 26 | fun value(): Long { 27 | return this.pos.value() * this.vel.value() 28 | } 29 | 30 | fun xs(): Pair { 31 | return Pair(this.pos.x, this.vel.x) 32 | } 33 | 34 | fun ys(): Pair { 35 | return Pair(this.pos.y, this.vel.y) 36 | } 37 | 38 | fun zs(): Pair { 39 | return Pair(this.pos.z, this.vel.z) 40 | } 41 | 42 | companion object { 43 | val reg = Regex("") 44 | fun parse(s: String): Moon { 45 | val parsed = reg.matchEntire(s)!! 46 | return Moon( 47 | Vector( 48 | parsed.groupValues[1].toLong(), 49 | parsed.groupValues[2].toLong(), 50 | parsed.groupValues[3].toLong() 51 | ), 52 | Vector(0, 0, 0) 53 | ) 54 | } 55 | } 56 | } 57 | 58 | fun gcd(a: Long, b: Long): Long = if (b == 0L) a else gcd(b, a % b) 59 | 60 | fun lcm(x: Long, y: Long): Long = x * y / gcd(x, y) 61 | 62 | fun main(args: Array) { 63 | if (args.count() != 1) { 64 | println("error: supply a filename!") 65 | exitProcess(1) 66 | } 67 | 68 | var moons: MutableList = ArrayList() 69 | File(args[0]).forEachLine { moons.add(Moon.parse(it)) } 70 | 71 | val origXs = moons.map { it.xs() } 72 | var xPeriod: Long? = null 73 | val origYs = moons.map { it.ys() } 74 | var yPeriod: Long? = null 75 | val origZs = moons.map { it.zs() } 76 | var zPeriod: Long? = null 77 | 78 | var q: Long = 0 79 | while (xPeriod == null || yPeriod == null || zPeriod == null) { 80 | q += 1 81 | for ((i, moon) in moons.withIndex()) { 82 | var (v_x, v_y, v_z) = moon.vel 83 | for (o_moon in moons) { 84 | if (o_moon == moon) { 85 | continue 86 | } 87 | if (o_moon.pos.x > moon.pos.x) { 88 | v_x += 1 89 | } else if (o_moon.pos.x < moon.pos.x) { 90 | v_x -= 1 91 | } 92 | if (o_moon.pos.y > moon.pos.y) { 93 | v_y += 1 94 | } else if (o_moon.pos.y < moon.pos.y) { 95 | v_y -= 1 96 | } 97 | if (o_moon.pos.z > moon.pos.z) { 98 | v_z += 1 99 | } else if (o_moon.pos.z < moon.pos.z) { 100 | v_z -= 1 101 | } 102 | } 103 | moons[i] = Moon(moon.pos, Vector(v_x, v_y, v_z)) 104 | } 105 | 106 | for ((i, moon) in moons.withIndex()) { 107 | moons[i] = Moon(moon.pos.add(moon.vel), moon.vel) 108 | } 109 | 110 | if (xPeriod == null && moons.map { it.xs() } == origXs) { 111 | xPeriod = q 112 | } 113 | if (yPeriod == null && moons.map { it.ys() } == origYs) { 114 | yPeriod = q 115 | } 116 | if (zPeriod == null && moons.map { it.zs() } == origZs) { 117 | zPeriod = q 118 | } 119 | } 120 | 121 | println(xPeriod) 122 | println(yPeriod) 123 | println(zPeriod) 124 | println(lcm(lcm(xPeriod, yPeriod), zPeriod)) 125 | } 126 | -------------------------------------------------------------------------------- /day12/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import math 3 | import re 4 | from typing import Dict 5 | from typing import List 6 | from typing import NamedTuple 7 | from typing import Tuple 8 | 9 | from support import timing 10 | 11 | INT_RE = re.compile(r'-?\d+') 12 | 13 | 14 | class Vector(NamedTuple): 15 | x: int 16 | y: int 17 | z: int 18 | 19 | def add(self, other: 'Vector') -> 'Vector': 20 | return self._replace( 21 | x=self.x + other.x, 22 | y=self.y + other.y, 23 | z=self.z + other.z, 24 | ) 25 | 26 | 27 | class Moon(NamedTuple): 28 | pos: Vector 29 | vel: Vector 30 | 31 | @classmethod 32 | def parse(cls, s: str) -> 'Moon': 33 | x_s, y_s, z_s = INT_RE.findall(s) 34 | return cls( 35 | pos=Vector(int(x_s), int(y_s), int(z_s)), 36 | vel=Vector(0, 0, 0), 37 | ) 38 | 39 | 40 | def lcm(x: int, y: int) -> int: 41 | return x * y // math.gcd(x, y) 42 | 43 | 44 | def axis(moons: List[Moon], axis: str) -> List[Tuple[int, int]]: 45 | return [(getattr(m.pos, axis), getattr(m.vel, axis)) for m in moons] 46 | 47 | 48 | def compute(s: str) -> int: 49 | moons = [Moon.parse(line) for line in s.splitlines()] 50 | periods: Dict[str, int] = {} 51 | axes = {k: axis(moons, k) for k in ('x', 'y', 'z')} 52 | 53 | q = 0 54 | while len(periods) < 3: 55 | q += 1 56 | for i, moon in enumerate(moons): 57 | v_x, v_y, v_z = moon.vel 58 | for o_moon in moons: 59 | if o_moon is moon: 60 | continue 61 | if o_moon.pos.x > moon.pos.x: 62 | v_x += 1 63 | elif o_moon.pos.x < moon.pos.x: 64 | v_x -= 1 65 | if o_moon.pos.y > moon.pos.y: 66 | v_y += 1 67 | elif o_moon.pos.y < moon.pos.y: 68 | v_y -= 1 69 | if o_moon.pos.z > moon.pos.z: 70 | v_z += 1 71 | elif o_moon.pos.z < moon.pos.z: 72 | v_z -= 1 73 | moons[i] = moon._replace(vel=Vector(v_x, v_y, v_z)) 74 | 75 | for i, moon in enumerate(moons): 76 | moons[i] = moon._replace(pos=moon.pos.add(moon.vel)) 77 | 78 | for k, v in axes.items(): 79 | if k not in periods: 80 | if axis(moons, k) == v: 81 | periods[k] = q 82 | 83 | return lcm(lcm(periods['x'], periods['y']), periods['z']) 84 | 85 | 86 | def main() -> int: 87 | parser = argparse.ArgumentParser() 88 | parser.add_argument('data_file') 89 | args = parser.parse_args() 90 | 91 | with open(args.data_file) as f, timing(): 92 | print(compute(f.read())) 93 | 94 | return 0 95 | 96 | 97 | if __name__ == '__main__': 98 | exit(main()) 99 | -------------------------------------------------------------------------------- /day13/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day13/__init__.py -------------------------------------------------------------------------------- /day13/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | from typing import List 6 | from typing import Tuple 7 | 8 | from support import timing 9 | 10 | 11 | def run( 12 | prog: Dict[int, int], 13 | in_fn: Callable[[], int], 14 | out_fn: Callable[[int], None], 15 | ) -> None: 16 | def parameter(instr: int, n: int) -> int: 17 | mode = instr // (10 ** (n + 1)) % 10 18 | if mode == 0: 19 | return prog[prog[pc + n]] 20 | elif mode == 1: 21 | return prog[pc + n] 22 | elif mode == 2: 23 | return prog[rb + prog[pc + n]] 24 | else: 25 | raise NotImplementedError(mode) 26 | 27 | def store(instr: int, n: int) -> int: 28 | mode = instr // (10 ** (n + 1)) % 10 29 | if mode == 0: 30 | return prog[pc + n] 31 | elif mode == 2: 32 | return rb + prog[pc + n] 33 | else: 34 | raise NotImplementedError(mode) 35 | 36 | rb = 0 37 | pc = 0 38 | while pc < len(prog): 39 | instr = prog[pc] 40 | opc = instr % 100 41 | if opc == 99: 42 | return 43 | elif opc == 1: 44 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 45 | pc += 4 46 | elif opc == 2: 47 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 48 | pc += 4 49 | elif opc == 3: 50 | prog[store(instr, 1)] = in_fn() 51 | pc += 2 52 | elif opc == 4: 53 | out_fn(parameter(instr, 1)) 54 | pc += 2 55 | elif opc == 5: 56 | if parameter(instr, 1): 57 | pc = parameter(instr, 2) 58 | else: 59 | pc += 3 60 | elif opc == 6: 61 | if not parameter(instr, 1): 62 | pc = parameter(instr, 2) 63 | else: 64 | pc += 3 65 | elif opc == 7: 66 | if parameter(instr, 1) < parameter(instr, 2): 67 | prog[store(instr, 3)] = 1 68 | else: 69 | prog[store(instr, 3)] = 0 70 | pc += 4 71 | elif opc == 8: 72 | if parameter(instr, 1) == parameter(instr, 2): 73 | prog[store(instr, 3)] = 1 74 | else: 75 | prog[store(instr, 3)] = 0 76 | pc += 4 77 | elif opc == 9: 78 | rb += parameter(instr, 1) 79 | pc += 2 80 | else: 81 | raise AssertionError(f'unreachable? {prog} {pc}') 82 | raise AssertionError(f'unreachable? {prog} {pc}') 83 | 84 | 85 | def compute(s: str) -> int: 86 | prog = [int(part) for part in s.strip().split(',')] 87 | prog_d = collections.defaultdict(int, enumerate(prog)) 88 | 89 | board: Dict[Tuple[int, int], int] = collections.defaultdict(int) 90 | read_queue: List[int] = [] 91 | 92 | def in_fn() -> int: 93 | raise AssertionError('unreachable') 94 | 95 | def out_fn(n: int) -> None: 96 | read_queue.append(n) 97 | if len(read_queue) == 3: 98 | x, y, tile = read_queue 99 | board[x, y] = tile 100 | read_queue.clear() 101 | 102 | run(prog_d, in_fn, out_fn) 103 | return collections.Counter(board.values())[2] 104 | 105 | 106 | def main() -> int: 107 | parser = argparse.ArgumentParser() 108 | parser.add_argument('data_file') 109 | args = parser.parse_args() 110 | 111 | with open(args.data_file) as f, timing(): 112 | print(compute(f.read())) 113 | 114 | return 0 115 | 116 | 117 | if __name__ == '__main__': 118 | exit(main()) 119 | -------------------------------------------------------------------------------- /day13/part1.rb: -------------------------------------------------------------------------------- 1 | if ARGV.empty? 2 | puts "usage: ruby #{__FILE__} FILENAME" 3 | exit 1 4 | end 5 | 6 | prog_l = File.read(ARGV[0]).strip.split(',').map(&:to_i) 7 | prog = Hash.new { |h, k| h[k] = 0 } 8 | prog_l.each_with_index do |v, i| 9 | prog[i] = v 10 | end 11 | rb = pc = 0 12 | 13 | def param(prog, pc, rb, instr, n) 14 | mode = instr / (10**(n + 1)) % 10 15 | case mode 16 | when 0 17 | prog[prog[pc + n]] 18 | when 1 19 | prog[pc + n] 20 | when 2 21 | prog[rb + prog[pc + n]] 22 | else 23 | raise StandardError, 'unreachable!' 24 | end 25 | end 26 | 27 | def store(prog, pc, rb, instr, n) 28 | mode = instr / (10**(n + 1)) % 10 29 | case mode 30 | when 0 31 | prog[pc + n] 32 | when 2 33 | rb + prog[pc + n] 34 | else 35 | raise StandardError, 'unreachable!' 36 | end 37 | end 38 | 39 | board = Hash.new { |h, k| h[k] = 0 } 40 | read_queue = [] 41 | 42 | loop do 43 | instr = prog[pc] 44 | opc = instr % 100 45 | case opc 46 | when 99 47 | total = 0 48 | board.each_value do |v| 49 | total += 1 if v == 2 50 | end 51 | puts total 52 | exit 0 53 | when 1 54 | prog[store(prog, pc, rb, instr, 3)] = ( 55 | param(prog, pc, rb, instr, 1) + param(prog, pc, rb, instr, 2) 56 | ) 57 | pc += 4 58 | when 2 59 | prog[store(prog, pc, rb, instr, 3)] = ( 60 | param(prog, pc, rb, instr, 1) * param(prog, pc, rb, instr, 2) 61 | ) 62 | pc += 4 63 | when 3 64 | # TODO: input function 65 | prog[store(prog, pc, rb, instr, 1)] = 1 66 | pc += 2 67 | when 4 68 | read_queue.push(param(prog, pc, rb, instr, 1)) 69 | if read_queue.length == 3 70 | x, y, tile = read_queue 71 | board[[x, y]] = tile 72 | read_queue.clear 73 | end 74 | pc += 2 75 | when 5 76 | if param(prog, pc, rb, instr, 1) != 0 77 | pc = param(prog, pc, rb, instr, 2) 78 | else 79 | pc += 3 80 | end 81 | when 6 82 | if param(prog, pc, rb, instr, 1).zero? 83 | pc = param(prog, pc, rb, instr, 2) 84 | else 85 | pc += 3 86 | end 87 | when 7 88 | if param(prog, pc, rb, instr, 1) < param(prog, pc, rb, instr, 2) 89 | prog[store(prog, pc, rb, instr, 3)] = 1 90 | else 91 | prog[store(prog, pc, rb, instr, 3)] = 0 92 | end 93 | pc += 4 94 | when 8 95 | if param(prog, pc, rb, instr, 1) == param(prog, pc, rb, instr, 2) 96 | prog[store(prog, pc, rb, instr, 3)] = 1 97 | else 98 | prog[store(prog, pc, rb, instr, 3)] = 0 99 | end 100 | pc += 4 101 | when 9 102 | rb += param(prog, pc, rb, instr, 1) 103 | pc += 2 104 | else 105 | raise StandardError, 'unreachable!' 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /day13/part2.rb: -------------------------------------------------------------------------------- 1 | if ARGV.empty? 2 | puts "usage: ruby #{__FILE__} FILENAME" 3 | exit 1 4 | end 5 | 6 | prog_l = File.read(ARGV[0]).strip.split(',').map(&:to_i) 7 | prog = Hash.new { |h, k| h[k] = 0 } 8 | prog_l.each_with_index do |v, i| 9 | prog[i] = v 10 | end 11 | rb = pc = 0 12 | 13 | def param(prog, pc, rb, instr, n) 14 | mode = instr / (10**(n + 1)) % 10 15 | case mode 16 | when 0 17 | prog[prog[pc + n]] 18 | when 1 19 | prog[pc + n] 20 | when 2 21 | prog[rb + prog[pc + n]] 22 | else 23 | raise StandardError, 'unreachable!' 24 | end 25 | end 26 | 27 | def store(prog, pc, rb, instr, n) 28 | mode = instr / (10**(n + 1)) % 10 29 | case mode 30 | when 0 31 | prog[pc + n] 32 | when 2 33 | rb + prog[pc + n] 34 | else 35 | raise StandardError, 'unreachable!' 36 | end 37 | end 38 | 39 | TILES = { 0 => ' ', 1 => '█', 2 => '▦', 3 => '▂', 4 => '○' }.freeze 40 | board = Hash.new { |h, k| h[k] = 0 } 41 | max_x = 0 42 | max_y = 0 43 | read_queue = [] 44 | score = 0 45 | ball_pos = 0 46 | paddle_pos = 0 47 | 48 | prog[0] = 2 49 | 50 | loop do 51 | instr = prog[pc] 52 | opc = instr % 100 53 | case opc 54 | when 99 55 | puts score 56 | exit 0 57 | when 1 58 | prog[store(prog, pc, rb, instr, 3)] = ( 59 | param(prog, pc, rb, instr, 1) + param(prog, pc, rb, instr, 2) 60 | ) 61 | pc += 4 62 | when 2 63 | prog[store(prog, pc, rb, instr, 3)] = ( 64 | param(prog, pc, rb, instr, 1) * param(prog, pc, rb, instr, 2) 65 | ) 66 | pc += 4 67 | when 3 68 | if ENV['DRAW'] 69 | (0..max_y).each do |y| 70 | (0..max_x).each do |x| 71 | print(TILES[board[[x, y]]]) 72 | end 73 | puts 74 | end 75 | end 76 | val = if ball_pos > paddle_pos 77 | 1 78 | elsif ball_pos == paddle_pos 79 | 0 80 | else 81 | -1 82 | end 83 | prog[store(prog, pc, rb, instr, 1)] = val 84 | pc += 2 85 | when 4 86 | read_queue.push(param(prog, pc, rb, instr, 1)) 87 | if read_queue.length == 3 88 | x, y, val = read_queue 89 | if x == -1 && y.zero? 90 | score = val 91 | else 92 | board[[x, y]] = val 93 | if val == 4 94 | ball_pos = x 95 | elsif val == 3 96 | paddle_pos = x 97 | end 98 | end 99 | max_x = [max_x, x].max 100 | max_y = [max_y, y].max 101 | read_queue.clear 102 | end 103 | pc += 2 104 | when 5 105 | if param(prog, pc, rb, instr, 1) != 0 106 | pc = param(prog, pc, rb, instr, 2) 107 | else 108 | pc += 3 109 | end 110 | when 6 111 | if param(prog, pc, rb, instr, 1).zero? 112 | pc = param(prog, pc, rb, instr, 2) 113 | else 114 | pc += 3 115 | end 116 | when 7 117 | if param(prog, pc, rb, instr, 1) < param(prog, pc, rb, instr, 2) 118 | prog[store(prog, pc, rb, instr, 3)] = 1 119 | else 120 | prog[store(prog, pc, rb, instr, 3)] = 0 121 | end 122 | pc += 4 123 | when 8 124 | if param(prog, pc, rb, instr, 1) == param(prog, pc, rb, instr, 2) 125 | prog[store(prog, pc, rb, instr, 3)] = 1 126 | else 127 | prog[store(prog, pc, rb, instr, 3)] = 0 128 | end 129 | pc += 4 130 | when 9 131 | rb += param(prog, pc, rb, instr, 1) 132 | pc += 2 133 | else 134 | raise StandardError, 'unreachable!' 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /day14/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day14/__init__.py -------------------------------------------------------------------------------- /day14/input.txt: -------------------------------------------------------------------------------- 1 | 11 RVCS => 8 CBMDT 2 | 29 QXPB, 8 QRGRH => 8 LGMKD 3 | 3 VPRVD => 6 PMFZG 4 | 1 CNWNQ, 11 MJVXS => 6 SPLM 5 | 13 SPDRZ, 13 PMFZG => 2 BLFM 6 | 8 QWPFN => 7 LWVB 7 | 1 SPLM => 8 TKWQ 8 | 2 QRGRH, 6 CNWNQ => 7 DTZW 9 | 2 DMLT, 1 SPLM, 1 TMDK => 9 NKNS 10 | 1 MJVXS, 1 HLBV => 7 PQCQH 11 | 1 JZHZP, 9 LWVB => 7 MJSCQ 12 | 29 DGFR => 7 QRGRH 13 | 14 XFLKQ, 2 NKNS, 4 KMNJF, 3 MLZGQ, 7 TKWQ, 24 WTDW, 11 CBMDT => 4 GJKX 14 | 4 TKWQ, 1 WLCFR => 4 PDKGT 15 | 2 NKNS => 4 GDKL 16 | 4 WRZST => 9 XFLKQ 17 | 19 DGFR => 4 VPRVD 18 | 10 MJSCQ, 4 QWPFN, 4 QXPB => 2 MLZGQ 19 | 1 JZHZP => 7 QWPFN 20 | 1 XFLKQ => 9 FQGVL 21 | 3 GQGXC => 9 VHGP 22 | 3 NQZTV, 1 JZHZP => 2 NVZWL 23 | 38 WLCFR, 15 GJKX, 44 LGMKD, 2 CBVXG, 2 GDKL, 77 FQGVL, 10 MKRCZ, 29 WJQD, 33 BWXGC, 19 PQCQH, 24 BKXD => 1 FUEL 24 | 102 ORE => 5 DGFR 25 | 17 NWKLB, 1 SBPLK => 5 HRQM 26 | 3 BWXGC => 8 TQDP 27 | 1 TQDP => 2 PSZDZ 28 | 2 MJVXS => 9 WNXG 29 | 2 NBTW, 1 HRQM => 2 SVHBH 30 | 8 CNWNQ, 1 DTZW => 4 RVCS 31 | 4 VHGP, 20 WNXG, 2 SVHBH => 3 SPDRZ 32 | 110 ORE => 5 TXMC 33 | 10 QRGRH => 5 NWKLB 34 | 1 SBPLK => 3 MJVXS 35 | 9 DGFR => 5 RFSRL 36 | 5 LBTV => 3 DMLT 37 | 1 NWKLB, 1 KMNJF, 1 HDQXB, 6 LBTV, 2 PSZDZ, 34 PMFZG, 2 SVHBH => 2 WJQD 38 | 1 RVCS => 5 MKRCZ 39 | 14 NQZTV, 3 FPLT, 1 SJMS => 2 GQGXC 40 | 18 RFSRL, 13 VHGP, 23 NBTW => 5 WTDW 41 | 1 VHGP, 6 TKWQ => 7 QXPB 42 | 1 JZHZP, 1 CNWNQ => 5 KMNJF 43 | 109 ORE => 9 BWXGC 44 | 2 CNWNQ, 1 PDKGT, 2 KMNJF => 5 HDQXB 45 | 1 PDKGT, 18 WRZST, 9 MJSCQ, 3 VHGP, 1 BLFM, 1 LGMKD, 7 WLCFR => 2 BKXD 46 | 11 MLJK => 6 FPLT 47 | 8 DGFR, 2 TXMC, 3 WJRC => 9 SJMS 48 | 2 SBPLK => 1 LBTV 49 | 22 QWPFN => 4 WRZST 50 | 5 WRZST, 22 WNXG, 1 VHGP => 7 NBTW 51 | 7 RVCS => 9 TMDK 52 | 1 DGFR, 14 TXMC => 5 JZHZP 53 | 2 JZHZP => 3 SBPLK 54 | 19 PDKGT => 8 HLBV 55 | 195 ORE => 6 WJRC 56 | 6 GQGXC => 8 CNWNQ 57 | 1 NVZWL, 4 GQGXC => 2 CBVXG 58 | 1 NVZWL, 1 KMNJF => 8 WLCFR 59 | 153 ORE => 4 MLJK 60 | 1 BWXGC => 6 NQZTV 61 | -------------------------------------------------------------------------------- /day14/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | import math 4 | from typing import Dict 5 | from typing import NamedTuple 6 | from typing import Tuple 7 | 8 | import pytest 9 | 10 | from support import timing 11 | 12 | 13 | class Reagent(NamedTuple): 14 | chemical: str 15 | quantity: int 16 | 17 | @classmethod 18 | def parse(cls, s: str) -> 'Reagent': 19 | n, s = s.strip().split() 20 | return cls(s, int(n)) 21 | 22 | 23 | class Reaction(NamedTuple): 24 | chemical: str 25 | quantity: int 26 | reagents: Tuple[Reagent, ...] 27 | 28 | @classmethod 29 | def parse(cls, s: str) -> 'Reaction': 30 | reagents_s, _, result_s = s.partition(' => ') 31 | reagents = tuple(Reagent.parse(s) for s in reagents_s.split(', ')) 32 | result = Reagent.parse(result_s) 33 | return cls(result.chemical, result.quantity, reagents) 34 | 35 | 36 | def parse_chemical(s: str) -> Reagent: 37 | n, s = s.strip().split() 38 | return Reagent(s, int(n)) 39 | 40 | 41 | def compute(s: str) -> int: 42 | all_reactions = [Reaction.parse(line) for line in s.splitlines()] 43 | reactions = {reaction.chemical: reaction for reaction in all_reactions} 44 | 45 | ore = 0 46 | excess: Dict[str, int] = collections.defaultdict(int) 47 | needed = [Reagent('FUEL', 1)] 48 | 49 | while needed: 50 | s, n = needed.pop() 51 | if s == 'ORE': 52 | ore += n 53 | else: 54 | if excess[s] >= n: 55 | excess[s] -= n 56 | continue 57 | elif excess[s]: 58 | n -= excess[s] 59 | excess[s] = 0 60 | 61 | reaction = reactions[s] 62 | mult = math.ceil(n / reaction.quantity) 63 | excess[s] += reaction.quantity * mult - n 64 | for reagent in reaction.reagents: 65 | new = reagent._replace(quantity=mult * reagent.quantity) 66 | needed.append(new) 67 | return ore 68 | 69 | 70 | SAMPLE1 = """\ 71 | 10 ORE => 10 A 72 | 1 ORE => 1 B 73 | 7 A, 1 B => 1 C 74 | 7 A, 1 C => 1 D 75 | 7 A, 1 D => 1 E 76 | 7 A, 1 E => 1 FUEL 77 | """ 78 | 79 | 80 | @pytest.mark.parametrize( 81 | ('input_s', 'expected'), 82 | ( 83 | (SAMPLE1, 31), 84 | ), 85 | ) 86 | def test(input_s: str, expected: int) -> None: 87 | assert compute(input_s) == expected 88 | 89 | 90 | def main() -> int: 91 | parser = argparse.ArgumentParser() 92 | parser.add_argument('data_file') 93 | args = parser.parse_args() 94 | 95 | with open(args.data_file) as f, timing(): 96 | print(compute(f.read())) 97 | 98 | return 0 99 | 100 | 101 | if __name__ == '__main__': 102 | exit(main()) 103 | -------------------------------------------------------------------------------- /day14/part1.py0.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | 4 | 5 | def parseint(s): # could use string.atoi, but this is good enough 6 | ret = 0 7 | for c in s: 8 | ret = ret * 10 9 | ret = ret + ord(c) - ord('0') 10 | return ret 11 | 12 | 13 | def char_eq(s1, s2): 14 | o1 = ord(s1) 15 | o2 = ord(s2) 16 | return o1 <= o2 and o1 >= o2 17 | 18 | 19 | def string_index(s, other): 20 | i = 0 21 | while i < len(s) - len(other) + 1: 22 | j = 0 23 | matched = 1 24 | while j < len(other): 25 | if not char_eq(s[i + j], other[j]): 26 | matched = 0 27 | j = j + 1 28 | if matched: 29 | return i 30 | i = i + 1 31 | return -1 32 | 33 | 34 | def string_isspace(c): 35 | return char_eq(c, ' ') or char_eq(c, '\n') 36 | 37 | 38 | def string_strip(s): 39 | i = 0 40 | while string_isspace(s[i]): 41 | i = i + 1 42 | j = len(s) - 1 43 | while string_isspace(s[j]): 44 | j = j - 1 45 | return s[i:j + 1] 46 | 47 | 48 | def string_contains(s, delim): 49 | return string_index(s, delim) >= 0 50 | 51 | 52 | def string_equal(s, other): # can't use equality `=` or `<>` 53 | len_s = len(s) 54 | len_o = len(other) 55 | return len_s <= len_o and len_s >= len_o and string_contains(s, other) 56 | 57 | 58 | def parse_reagent(s): 59 | parts = split_by_delim(string_strip(s), ' ') 60 | ret = {} 61 | ret['n'] = parseint(parts[0]) 62 | ret['s'] = parts[1] 63 | return ret 64 | 65 | 66 | def split_by_delim(s, delim): 67 | pos = string_index(s, delim) 68 | before = s[:pos] 69 | after = s[pos + len(delim):] 70 | return before, after 71 | 72 | 73 | def parse_reaction(s): 74 | before, after = split_by_delim(string_strip(s), ' => ') 75 | ret = {} 76 | result = parse_reagent(after) 77 | ret['s'] = result['s'] 78 | ret['n'] = result['n'] 79 | ret['reagents'] = [] 80 | while string_contains(before, ', '): 81 | reagent_s, before = split_by_delim(before, ', ') 82 | ret['reagents'].append(parse_reagent(reagent_s)) 83 | ret['reagents'].append(parse_reagent(before)) 84 | return ret 85 | 86 | 87 | # avoid python 0's equality which is a single `=` or `<>` 88 | if len(sys.argv) > 2 or len(sys.argv) < 2: 89 | print('usage ' + sys.argv[0] + ' FILENAME') 90 | sys.exit(1) 91 | 92 | # use len(...) here as python 0 doesn't have negative indices 93 | f = open(sys.argv[len(sys.argv) - 1], 'r') 94 | try: 95 | reactions = {} 96 | line = f.readline() 97 | while line: 98 | parsed = parse_reaction(line) 99 | reactions[parsed['s']] = parsed 100 | line = f.readline() 101 | finally: 102 | f.close() 103 | 104 | ore = 0 105 | excess = {} 106 | for k in reactions.keys(): 107 | excess[k] = 0 108 | needed = [('FUEL', 1)] 109 | while needed: 110 | s, n = needed[len(needed) - 1] 111 | # no .pop()! 112 | needed = needed[:-1] 113 | if string_equal(s, 'ORE'): 114 | ore = ore + n 115 | else: 116 | if excess[s] >= n: 117 | excess[s] = excess[s] - n 118 | else: 119 | if excess[s] > 0: 120 | n = n - excess[s] 121 | excess[s] = 0 122 | reaction = reactions[s] 123 | mult = int(math.ceil(float(n) / float(reaction['n']))) 124 | excess[s] = excess[s] + reaction['n'] * mult - n 125 | for reagent in reaction['reagents']: 126 | needed.append((reagent['s'], mult * reagent['n'])) 127 | 128 | print(ore) 129 | -------------------------------------------------------------------------------- /day14/part2.py0.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sys 3 | 4 | 5 | def parseint(s): # could use string.atoi, but this is good enough 6 | ret = 0 7 | for c in s: 8 | ret = ret * 10 9 | ret = ret + ord(c) - ord('0') 10 | return ret 11 | 12 | 13 | def char_eq(s1, s2): 14 | o1 = ord(s1) 15 | o2 = ord(s2) 16 | return o1 <= o2 and o1 >= o2 17 | 18 | 19 | def string_index(s, other): 20 | i = 0 21 | while i < len(s) - len(other) + 1: 22 | j = 0 23 | matched = 1 24 | while j < len(other): 25 | if not char_eq(s[i + j], other[j]): 26 | matched = 0 27 | j = j + 1 28 | if matched: 29 | return i 30 | i = i + 1 31 | return -1 32 | 33 | 34 | def string_isspace(c): 35 | return char_eq(c, ' ') or char_eq(c, '\n') 36 | 37 | 38 | def string_strip(s): 39 | i = 0 40 | while string_isspace(s[i]): 41 | i = i + 1 42 | j = len(s) - 1 43 | while string_isspace(s[j]): 44 | j = j - 1 45 | return s[i:j + 1] 46 | 47 | 48 | def string_contains(s, delim): 49 | return string_index(s, delim) >= 0 50 | 51 | 52 | def string_equal(s, other): # can't use equality `=` or `<>` 53 | len_s = len(s) 54 | len_o = len(other) 55 | return len_s <= len_o and len_s >= len_o and string_contains(s, other) 56 | 57 | 58 | def parse_reagent(s): 59 | parts = split_by_delim(string_strip(s), ' ') 60 | ret = {} 61 | ret['n'] = parseint(parts[0]) 62 | ret['s'] = parts[1] 63 | return ret 64 | 65 | 66 | def split_by_delim(s, delim): 67 | pos = string_index(s, delim) 68 | before = s[:pos] 69 | after = s[pos + len(delim):] 70 | return before, after 71 | 72 | 73 | def parse_reaction(s): 74 | before, after = split_by_delim(string_strip(s), ' => ') 75 | ret = {} 76 | result = parse_reagent(after) 77 | ret['s'] = result['s'] 78 | ret['n'] = result['n'] 79 | ret['reagents'] = [] 80 | while string_contains(before, ', '): 81 | reagent_s, before = split_by_delim(before, ', ') 82 | ret['reagents'].append(parse_reagent(reagent_s)) 83 | ret['reagents'].append(parse_reagent(before)) 84 | return ret 85 | 86 | 87 | def require(n, reactions): 88 | ore = 0 89 | excess = {} 90 | for k in reactions.keys(): 91 | excess[k] = 0 92 | needed = [('FUEL', n)] 93 | while needed: 94 | s, n = needed[len(needed) - 1] 95 | # no .pop()! 96 | needed = needed[:-1] 97 | if string_equal(s, 'ORE'): 98 | ore = ore + n 99 | else: 100 | if excess[s] >= n: 101 | excess[s] = excess[s] - n 102 | else: 103 | if excess[s] > 0: 104 | n = n - excess[s] 105 | excess[s] = 0 106 | reaction = reactions[s] 107 | mult = int(math.ceil(float(n) / float(reaction['n']))) 108 | excess[s] = excess[s] + reaction['n'] * mult - n 109 | for reagent in reaction['reagents']: 110 | needed.append((reagent['s'], mult * reagent['n'])) 111 | return ore 112 | 113 | 114 | # avoid python 0's equality which is a single `=` or `<>` 115 | if len(sys.argv) > 2 or len(sys.argv) < 2: 116 | print('usage ' + sys.argv[0] + ' FILENAME') 117 | sys.exit(1) 118 | 119 | # use len(...) here as python 0 doesn't have negative indices 120 | f = open(sys.argv[len(sys.argv) - 1], 'r') 121 | try: 122 | reactions = {} 123 | line = f.readline() 124 | while line: 125 | parsed = parse_reaction(line) 126 | reactions[parsed['s']] = parsed 127 | line = f.readline() 128 | finally: 129 | f.close() 130 | 131 | ore = 1000000000000 132 | fuel = 1 133 | inc = 1 134 | while 1: 135 | ore_required = require(fuel + inc, reactions) 136 | if ore_required > ore: 137 | if inc >= 1 and inc <= 1: 138 | break 139 | else: 140 | inc = 1 141 | else: 142 | fuel = fuel + inc 143 | inc = inc * 2 144 | print(fuel) 145 | -------------------------------------------------------------------------------- /day15/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day15/__init__.py -------------------------------------------------------------------------------- /day15/input.txt: -------------------------------------------------------------------------------- 1 | 3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,102,1,1034,1039,101,0,1036,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1105,1,124,1001,1034,0,1039,102,1,1036,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1106,0,124,1001,1034,-1,1039,1008,1036,0,1041,102,1,1035,1040,1002,1038,1,1043,101,0,1037,1042,1106,0,124,1001,1034,1,1039,1008,1036,0,1041,1002,1035,1,1040,102,1,1038,1043,101,0,1037,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,37,1032,1006,1032,165,1008,1040,39,1032,1006,1032,165,1102,2,1,1044,1106,0,224,2,1041,1043,1032,1006,1032,179,1101,0,1,1044,1105,1,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,74,1044,1106,0,224,1102,0,1,1044,1106,0,224,1006,1044,247,1002,1039,1,1034,102,1,1040,1035,1002,1041,1,1036,102,1,1043,1038,1001,1042,0,1037,4,1044,1106,0,0,4,35,96,8,87,44,67,40,80,25,91,53,86,23,96,7,76,76,10,30,90,46,47,40,93,75,3,17,1,19,89,7,92,47,95,3,92,39,72,69,6,18,86,94,19,82,98,9,7,91,42,86,29,83,65,43,91,71,92,16,96,82,5,81,6,92,93,76,71,17,91,91,73,64,33,27,89,4,99,81,80,6,57,87,9,42,99,97,13,42,81,82,72,68,35,93,2,99,6,6,94,2,39,39,86,43,97,77,86,21,56,75,61,91,82,56,94,32,47,90,33,72,93,13,87,12,42,68,99,71,34,97,79,87,99,79,25,42,95,97,51,93,80,33,71,68,89,50,49,78,77,24,93,70,13,11,56,29,18,77,77,94,60,80,75,84,42,87,90,58,84,27,78,3,80,70,85,79,4,36,94,65,79,93,94,13,97,75,49,92,15,84,5,85,35,67,96,87,64,32,83,97,20,89,64,18,93,32,46,91,57,53,75,56,7,56,92,99,36,22,93,19,25,29,48,86,94,68,18,95,79,87,97,55,75,44,65,82,99,31,94,42,53,81,72,85,70,93,47,40,77,60,85,87,11,60,98,25,90,88,93,93,85,64,43,88,96,36,83,14,98,40,48,11,18,80,97,49,23,2,91,85,50,88,94,41,75,99,84,15,45,9,81,83,96,51,56,58,76,72,50,94,59,76,87,10,25,88,73,99,20,95,46,93,88,2,50,89,86,26,18,85,72,85,75,66,83,25,97,96,25,94,14,34,94,89,57,88,78,17,92,59,40,29,84,87,55,61,81,9,82,93,17,33,81,81,58,43,91,68,86,80,61,83,23,46,78,60,14,94,79,28,91,57,79,83,48,92,5,49,97,81,56,53,84,42,58,93,20,71,29,29,89,88,34,31,87,92,78,62,78,72,93,3,54,97,82,38,32,89,86,88,38,19,84,51,99,60,90,95,14,78,11,82,89,12,87,98,70,79,33,76,44,97,79,33,19,34,83,58,4,89,21,88,78,46,78,76,66,61,92,91,38,86,27,61,86,46,52,97,44,80,89,53,55,47,83,34,44,97,37,41,92,28,70,95,82,91,76,8,99,2,80,1,66,96,71,94,1,44,89,29,13,99,35,80,89,31,91,19,77,46,85,77,93,61,31,62,14,92,82,73,94,86,20,31,94,72,73,44,61,91,79,40,88,69,85,6,83,96,49,12,77,39,83,91,24,70,13,81,57,39,88,38,23,80,43,92,67,46,87,25,80,93,82,68,98,93,63,85,29,18,78,94,27,89,85,20,63,89,93,96,99,50,71,97,15,28,53,78,85,78,82,64,67,14,94,47,96,65,58,81,20,91,36,82,55,11,85,87,59,84,6,67,87,69,88,81,68,38,84,52,33,79,97,69,89,89,34,96,18,78,67,87,36,93,57,77,77,21,47,99,27,26,79,7,88,37,90,33,25,96,66,83,24,30,82,84,16,82,85,15,55,92,20,80,92,38,20,34,87,67,11,84,28,42,93,26,54,89,85,78,82,60,14,9,76,85,10,80,80,50,85,29,86,20,61,81,80,51,32,88,91,92,34,56,79,58,76,41,47,89,24,40,90,85,88,30,48,91,42,2,91,95,98,60,79,40,86,61,79,81,23,91,91,12,21,78,54,75,61,11,79,89,73,84,13,95,81,6,52,92,37,76,65,82,84,87,40,94,70,78,71,83,46,94,2,79,57,80,35,99,21,83,81,93,64,81,78,99,57,87,49,87,41,92,83,82,58,92,0,0,21,21,1,10,1,0,0,0,0,0,0 2 | -------------------------------------------------------------------------------- /day15/part1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | import "io/ioutil" 5 | import "math" 6 | import "os" 7 | import "strconv" 8 | import "strings" 9 | 10 | type Prog struct { 11 | Prog map[int]int 12 | Pc int 13 | Value int 14 | } 15 | 16 | type Point struct { 17 | X int 18 | Y int 19 | } 20 | 21 | func run(prog map[int]int, pc int, value int) Prog { 22 | rb := 0 23 | 24 | param := func(instr int, n int) int { 25 | mode := instr / int(math.Pow10(n+1)) % 10 26 | switch mode { 27 | case 0: 28 | return prog[prog[pc+n]] 29 | case 1: 30 | return prog[pc+n] 31 | case 2: 32 | return prog[rb+prog[pc+n]] 33 | default: 34 | panic(mode) 35 | } 36 | } 37 | store := func(instr int, n int) int { 38 | mode := instr / int(math.Pow10(n+1)) % 10 39 | switch mode { 40 | case 0: 41 | return prog[pc+n] 42 | case 2: 43 | return rb + prog[pc+n] 44 | default: 45 | panic(mode) 46 | } 47 | } 48 | 49 | for { 50 | instr := prog[pc] 51 | switch opc := instr % 100; opc { 52 | case 99: 53 | panic(opc) 54 | case 1: 55 | prog[store(instr, 3)] = param(instr, 1) + param(instr, 2) 56 | pc += 4 57 | case 2: 58 | prog[store(instr, 3)] = param(instr, 1) * param(instr, 2) 59 | pc += 4 60 | case 3: 61 | prog[store(instr, 1)] = value 62 | pc += 2 63 | case 4: 64 | return Prog{Prog: prog, Pc: pc + 2, Value: param(instr, 1)} 65 | case 5: 66 | if param(instr, 1) != 0 { 67 | pc = param(instr, 2) 68 | } else { 69 | pc += 3 70 | } 71 | case 6: 72 | if param(instr, 1) == 0 { 73 | pc = param(instr, 2) 74 | } else { 75 | pc += 3 76 | } 77 | case 7: 78 | if param(instr, 1) < param(instr, 2) { 79 | prog[store(instr, 3)] = 1 80 | } else { 81 | prog[store(instr, 3)] = 0 82 | } 83 | pc += 4 84 | case 8: 85 | if param(instr, 1) == param(instr, 2) { 86 | prog[store(instr, 3)] = 1 87 | } else { 88 | prog[store(instr, 3)] = 0 89 | } 90 | pc += 4 91 | case 9: 92 | rb += param(instr, 1) 93 | pc += 2 94 | default: 95 | panic(opc) 96 | } 97 | } 98 | } 99 | 100 | func addPosition(p Point, dir int) Point { 101 | x := p.X 102 | y := p.Y 103 | switch dir { 104 | case 1: 105 | y -= 1 106 | case 2: 107 | y += 1 108 | case 3: 109 | x -= 1 110 | case 4: 111 | x += 1 112 | default: 113 | panic(dir) 114 | } 115 | return Point{X: x, Y: y} 116 | } 117 | 118 | func copyMap(m map[int]int) map[int]int { 119 | ret := make(map[int]int) 120 | for k, v := range m { 121 | ret[k] = v 122 | } 123 | return ret 124 | } 125 | 126 | type Item struct { 127 | Prog Prog 128 | Point Point 129 | Depth int 130 | } 131 | 132 | func main() { 133 | if len(os.Args) != 2 { 134 | fmt.Fprintf(os.Stderr, "usage: %v FILENAME\n", os.Args[0]) 135 | os.Exit(1) 136 | } 137 | file, err := os.Open(os.Args[1]) 138 | if err != nil { 139 | fmt.Fprintf(os.Stderr, "error opening file: %v\n", err) 140 | os.Exit(1) 141 | } 142 | contentsBytes, err := ioutil.ReadAll(file) 143 | if err != nil { 144 | fmt.Fprintf(os.Stderr, "error reading file: %v\n", err) 145 | os.Exit(1) 146 | } 147 | prog := make(map[int]int) 148 | contents := strings.TrimSpace(string(contentsBytes)) 149 | for i, s := range strings.Split(contents, ",") { 150 | v, err := strconv.Atoi(s) 151 | if err != nil { 152 | fmt.Fprintf(os.Stderr, "error parsing value: %v", err) 153 | os.Exit(1) 154 | } 155 | prog[i] = v 156 | } 157 | 158 | programs := make([]Item, 0) 159 | for _, dir := range []int{1, 2, 3, 4} { 160 | programs = append( 161 | programs, 162 | Item{ 163 | Prog: run(copyMap(prog), 0, dir), 164 | Point: addPosition(Point{X: 0, Y: 0}, dir), 165 | Depth: 1, 166 | }, 167 | ) 168 | } 169 | 170 | area := make(map[Point]rune) 171 | for len(programs) > 0 { 172 | next := make([]Item, 0) 173 | for _, item := range programs { 174 | switch item.Prog.Value { 175 | case 0: 176 | area[item.Point] = '#' 177 | case 1: 178 | area[item.Point] = '.' 179 | for _, dir := range []int{1, 2, 3, 4} { 180 | newPoint := addPosition(item.Point, dir) 181 | if _, ok := area[newPoint]; ok { 182 | continue 183 | } else { 184 | next = append( 185 | next, 186 | Item{ 187 | Prog: run( 188 | copyMap(item.Prog.Prog), 189 | item.Prog.Pc, 190 | dir, 191 | ), 192 | Point: newPoint, 193 | Depth: item.Depth + 1, 194 | }, 195 | ) 196 | } 197 | } 198 | case 2: 199 | fmt.Println(item.Depth) 200 | os.Exit(0) 201 | } 202 | } 203 | programs = next 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /day16/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day16/__init__.py -------------------------------------------------------------------------------- /day16/input.txt: -------------------------------------------------------------------------------- 1 | 59773775883217736423759431065821647306353420453506194937477909478357279250527959717515453593953697526882172199401149893789300695782513381578519607082690498042922082853622468730359031443128253024761190886248093463388723595869794965934425375464188783095560858698959059899665146868388800302242666479679987279787144346712262803568907779621609528347260035619258134850854741360515089631116920328622677237196915348865412336221562817250057035898092525020837239100456855355496177944747496249192354750965666437121797601987523473707071258599440525572142300549600825381432815592726865051526418740875442413571535945830954724825314675166862626566783107780527347044 2 | -------------------------------------------------------------------------------- /day16/part1.cr: -------------------------------------------------------------------------------- 1 | if ARGV.empty? 2 | puts "usage: #{__FILE__} FILENAME" 3 | exit 1 4 | end 5 | 6 | ints = File.read(ARGV[0]).strip.chars.map(&.to_i) 7 | 8 | PATTERN = [0, 1, 0, -1] 9 | 10 | 100.times do 11 | next_ints = [] of Int32 12 | 13 | (0...ints.size).each do |idx| 14 | pattern = [] of Int32 15 | [0, 1, 0, -1].each do |i| 16 | (0..idx).each do 17 | pattern << i 18 | end 19 | end 20 | 21 | sum = 0 22 | pattern.cycle.skip(1).each_with_index do |y,i| 23 | if i >= ints.size 24 | break 25 | end 26 | sum += y * ints[i] 27 | end 28 | next_ints << sum.abs % 10 29 | end 30 | ints = next_ints 31 | end 32 | 33 | puts ints.first(8).join("") 34 | -------------------------------------------------------------------------------- /day16/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import itertools 3 | from typing import Generator 4 | from typing import Sequence 5 | 6 | import pytest 7 | 8 | from support import timing 9 | 10 | 11 | def repeat(pattern: Sequence[int], n: int) -> Generator[int, None, None]: 12 | for val in pattern: 13 | for _ in range(n): 14 | yield val 15 | 16 | 17 | def compute(s: str) -> str: 18 | ints = [int(c) for c in s.strip()] 19 | 20 | for _ in range(100): 21 | next_lst = [] 22 | for idx in range(len(ints)): 23 | pattern = itertools.cycle(repeat((0, 1, 0, -1), idx + 1)) 24 | next(pattern) 25 | val = sum(x * y for x, y in zip(ints, pattern)) 26 | next_lst.append(abs(val) % 10) 27 | ints = next_lst 28 | 29 | return ''.join(str(n) for n in ints[:8]) 30 | 31 | 32 | def compute_jump1(s: str) -> str: 33 | ints = [int(c) for c in s.strip()] 34 | 35 | for _ in range(100): 36 | next_lst = [] 37 | for idx in range((len(ints) + 1) // 2): 38 | pattern = itertools.cycle(repeat((0, 1, 0, -1), idx + 1)) 39 | next(pattern) 40 | val = sum(x * y for x, y in zip(ints, pattern)) 41 | next_lst.append(abs(val) % 10) 42 | for idx in range((len(ints) + 1) // 2, len(ints)): 43 | val = sum(ints[idx:]) % 10 44 | next_lst.append(val) 45 | assert len(ints) == len(next_lst) 46 | ints = next_lst 47 | 48 | return ''.join(str(n) for n in ints[:8]) 49 | 50 | 51 | def compute_jump2(s: str) -> str: 52 | ints = [int(c) for c in s.strip()] 53 | 54 | for _ in range(100): 55 | next_lst = [] 56 | for idx in range((len(ints) + 1) // 2): 57 | pattern = itertools.cycle(repeat((1, 0, -1, 0), idx + 1)) 58 | val = sum(x * y for x, y in zip(ints[idx:], pattern)) 59 | next_lst.append(abs(val) % 10) 60 | for idx in range((len(ints) + 1) // 2, len(ints)): 61 | val = sum(ints[idx:]) % 10 62 | next_lst.append(val) 63 | assert len(ints) == len(next_lst) 64 | ints = next_lst 65 | 66 | return ''.join(str(n) for n in ints[:8]) 67 | 68 | 69 | def compute_jump3(s: str) -> str: 70 | ints = [int(c) for c in s.strip()] 71 | 72 | for _ in range(100): 73 | next_lst = [] 74 | for idx in range((len(ints) + 1) // 2): 75 | pattern = itertools.cycle(repeat((1, 0, -1, 0), idx + 1)) 76 | val = sum(x * y for x, y in zip(ints[idx:], pattern)) 77 | next_lst.append(abs(val) % 10) 78 | 79 | restsum = sum(ints[len(ints) + 1:]) 80 | for num in ints[len(ints) + 1:]: 81 | next_lst.append(abs(restsum) % 10) 82 | restsum -= num 83 | assert len(ints) == len(next_lst) 84 | ints = next_lst 85 | 86 | return ''.join(str(n) for n in ints[:8]) 87 | 88 | 89 | @pytest.mark.parametrize( 90 | ('input_s', 'expected'), 91 | ( 92 | ('80871224585914546619083218645595', '24176176'), 93 | 94 | # put given test cases here 95 | ), 96 | ) 97 | def test(input_s: str, expected: str) -> None: 98 | assert compute(input_s) == expected 99 | 100 | 101 | def main() -> int: 102 | parser = argparse.ArgumentParser() 103 | parser.add_argument('data_file') 104 | args = parser.parse_args() 105 | 106 | with open(args.data_file) as f, timing('original'): 107 | print(compute(f.read())) 108 | 109 | with open(args.data_file) as f, timing('optimization 1'): 110 | print(compute_jump1(f.read())) 111 | 112 | with open(args.data_file) as f, timing('optimization 2'): 113 | print(compute_jump2(f.read())) 114 | 115 | with open(args.data_file) as f, timing('optimization 3'): 116 | print(compute_jump2(f.read())) 117 | 118 | return 0 119 | 120 | 121 | if __name__ == '__main__': 122 | exit(main()) 123 | -------------------------------------------------------------------------------- /day16/part2.cr: -------------------------------------------------------------------------------- 1 | if ARGV.empty? 2 | puts "usage: #{__FILE__} FILENAME" 3 | exit 1 4 | end 5 | 6 | contents = File.read(ARGV[0]).strip 7 | ints = contents.chars.map(&.to_i) * 10000 8 | offset = contents[0...7].to_i 9 | ints = ints[offset..] 10 | 11 | 100.times do 12 | restsum = ints.sum 13 | ints.each_with_index do |num,i| 14 | ints[i] = restsum.abs % 10 15 | restsum -= num 16 | end 17 | end 18 | 19 | puts ints[...8].join("") 20 | -------------------------------------------------------------------------------- /day16/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | import pytest 4 | 5 | from support import timing 6 | 7 | 8 | def compute(s: str) -> str: 9 | ints = [int(c) for c in s.strip()] * 10000 10 | offset = int(s[:7]) 11 | assert offset > len(ints) // 2, (offset, len(ints)) 12 | ints = ints[offset:] 13 | 14 | for iteration in range(100): 15 | restsum = sum(ints) 16 | for i, n in enumerate(ints): 17 | ints[i] = abs(restsum) % 10 18 | restsum -= n 19 | 20 | return ''.join(str(n) for n in ints[:8]) 21 | 22 | 23 | @pytest.mark.parametrize( 24 | ('input_s', 'expected'), 25 | ( 26 | ('03036732577212944063491565474664', '84462026'), 27 | ('02935109699940807407585447034323', '78725270'), 28 | ('03081770884921959731165446850517', '53553731'), 29 | 30 | # put given test cases here 31 | ), 32 | ) 33 | def test(input_s: str, expected: str) -> None: 34 | assert compute(input_s) == expected 35 | 36 | 37 | def main() -> int: 38 | parser = argparse.ArgumentParser() 39 | parser.add_argument('data_file') 40 | args = parser.parse_args() 41 | 42 | with open(args.data_file) as f, timing(): 43 | print(compute(f.read())) 44 | 45 | return 0 46 | 47 | 48 | if __name__ == '__main__': 49 | exit(main()) 50 | -------------------------------------------------------------------------------- /day17/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day17/__init__.py -------------------------------------------------------------------------------- /day17/part1.php: -------------------------------------------------------------------------------- 1 | 0 && 122 | $x_coord > 0 && 123 | $y_coord < count($grid) - 1 && 124 | $x_coord < count($grid[0]) - 1 && 125 | $grid[$y_coord + 1][$x_coord] === '#' && 126 | $grid[$y_coord - 1][$x_coord] === '#' && 127 | $grid[$y_coord][$x_coord + 1] === '#' && 128 | $grid[$y_coord][$x_coord - 1] === '#' 129 | ); 130 | }; 131 | 132 | $ret = 0; 133 | foreach ($grid as $y_coord => $row) { 134 | foreach ($row as $x_coord => $s) { 135 | if ($is_intersection($grid, $y_coord, $x_coord)) { 136 | $ret += $y_coord * $x_coord; 137 | } 138 | } 139 | } 140 | echo $ret . "\n"; 141 | -------------------------------------------------------------------------------- /day17/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | from support import timing 7 | 8 | 9 | def run( 10 | prog: Dict[int, int], 11 | in_fn: Callable[[], int], 12 | out_fn: Callable[[int], None], 13 | ) -> None: 14 | def parameter(instr: int, n: int) -> int: 15 | mode = instr // (10 ** (n + 1)) % 10 16 | if mode == 0: 17 | return prog[prog[pc + n]] 18 | elif mode == 1: 19 | return prog[pc + n] 20 | elif mode == 2: 21 | return prog[rb + prog[pc + n]] 22 | else: 23 | raise NotImplementedError(mode) 24 | 25 | def store(instr: int, n: int) -> int: 26 | mode = instr // (10 ** (n + 1)) % 10 27 | if mode == 0: 28 | return prog[pc + n] 29 | elif mode == 2: 30 | return rb + prog[pc + n] 31 | else: 32 | raise NotImplementedError(mode) 33 | 34 | rb = 0 35 | pc = 0 36 | while pc < len(prog): 37 | instr = prog[pc] 38 | opc = instr % 100 39 | if opc == 99: 40 | return 41 | elif opc == 1: 42 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 43 | pc += 4 44 | elif opc == 2: 45 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 46 | pc += 4 47 | elif opc == 3: 48 | prog[store(instr, 1)] = in_fn() 49 | pc += 2 50 | elif opc == 4: 51 | out_fn(parameter(instr, 1)) 52 | pc += 2 53 | elif opc == 5: 54 | if parameter(instr, 1): 55 | pc = parameter(instr, 2) 56 | else: 57 | pc += 3 58 | elif opc == 6: 59 | if not parameter(instr, 1): 60 | pc = parameter(instr, 2) 61 | else: 62 | pc += 3 63 | elif opc == 7: 64 | if parameter(instr, 1) < parameter(instr, 2): 65 | prog[store(instr, 3)] = 1 66 | else: 67 | prog[store(instr, 3)] = 0 68 | pc += 4 69 | elif opc == 8: 70 | if parameter(instr, 1) == parameter(instr, 2): 71 | prog[store(instr, 3)] = 1 72 | else: 73 | prog[store(instr, 3)] = 0 74 | pc += 4 75 | elif opc == 9: 76 | rb += parameter(instr, 1) 77 | pc += 2 78 | else: 79 | raise AssertionError(f'unreachable? {prog} {pc}') 80 | raise AssertionError(f'unreachable? {prog} {pc}') 81 | 82 | 83 | def compute(s: str) -> int: 84 | prog = [int(part) for part in s.strip().split(',')] 85 | prog_d = collections.defaultdict(int, enumerate(prog)) 86 | 87 | grid: Dict[int, Dict[int, str]] = collections.defaultdict(lambda: {}) 88 | 89 | def in_fn() -> int: 90 | raise AssertionError('unreachable') 91 | 92 | y = 0 93 | x = 0 94 | 95 | def out_fn(n: int) -> None: 96 | nonlocal x 97 | nonlocal y 98 | 99 | s = chr(n) 100 | if s == '\n': 101 | x = 0 102 | y += 1 103 | else: 104 | grid[y][x] = s 105 | x += 1 106 | 107 | run(prog_d, in_fn, out_fn) 108 | 109 | def is_intersection(y_coord: int, x_coord: int) -> bool: 110 | return ( 111 | grid[y_coord][x_coord] == '#' and 112 | y_coord > 0 and 113 | x_coord > 0 and 114 | y_coord < len(grid) - 1 and 115 | x_coord < len(grid[0]) - 1 and 116 | grid[y_coord - 1][x_coord] == '#' and 117 | grid[y_coord + 1][x_coord] == '#' and 118 | grid[y_coord][x_coord + 1] == '#' and 119 | grid[y_coord][x_coord - 1] == '#' 120 | ) 121 | 122 | ret = 0 123 | for y_coord, row in grid.items(): 124 | for x_coord, s in row.items(): 125 | if is_intersection(y_coord, x_coord): 126 | ret += y_coord * x_coord 127 | 128 | return ret 129 | 130 | 131 | def main() -> int: 132 | parser = argparse.ArgumentParser() 133 | parser.add_argument('data_file') 134 | args = parser.parse_args() 135 | 136 | with open(args.data_file) as f, timing(): 137 | print(compute(f.read())) 138 | 139 | return 0 140 | 141 | 142 | if __name__ == '__main__': 143 | exit(main()) 144 | -------------------------------------------------------------------------------- /day18/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day18/__init__.py -------------------------------------------------------------------------------- /day18/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Dict 4 | from typing import FrozenSet 5 | from typing import Generator 6 | from typing import List 7 | from typing import Set 8 | from typing import Tuple 9 | 10 | import pytest 11 | 12 | from support import timing 13 | 14 | 15 | def next_pos(pos: Tuple[int, int]) -> Generator[Tuple[int, int], None, None]: 16 | x, y = pos 17 | yield x + 1, y 18 | yield x - 1, y 19 | yield x, y + 1 20 | yield x, y - 1 21 | 22 | 23 | def compute(s: str) -> int: 24 | grid: Dict[Tuple[int, int], str] = collections.defaultdict(str) 25 | key_count = 0 26 | for y, line in enumerate(s.splitlines()): 27 | for x, c in enumerate(line): 28 | grid[x, y] = c 29 | if c == '@': 30 | pos = (x, y) 31 | elif c.islower(): 32 | key_count += 1 33 | 34 | visited: Set[Tuple[FrozenSet[str], Tuple[int, int]]] = {(frozenset(), pos)} 35 | 36 | states: List[Tuple[FrozenSet[str], Tuple[int, int], int]] 37 | states = [(frozenset(), pos, 0)] 38 | while True: 39 | states_next = [] 40 | for keys, pos, steps in states: 41 | for cand in next_pos(pos): 42 | c = grid[cand] 43 | if cand not in grid: 44 | continue 45 | elif (keys, cand) in visited: 46 | continue 47 | elif c == '#': 48 | continue 49 | elif c.isupper() and c.lower() not in keys: 50 | continue 51 | 52 | if c.islower() and c not in keys: 53 | cand_keys = keys | {c} 54 | else: 55 | cand_keys = keys 56 | visited.add((keys, cand)) 57 | states_next.append((cand_keys, cand, steps + 1)) 58 | 59 | if len(keys) == key_count: 60 | return steps 61 | 62 | states = states_next 63 | raise AssertionError('unreachable') 64 | 65 | 66 | INPUT1 = '''\ 67 | ######### 68 | #b.A.@.a# 69 | ######### 70 | ''' 71 | INPUT2 = '''\ 72 | ######################## 73 | #f.D.E.e.C.b.A.@.a.B.c.# 74 | ######################.# 75 | #d.....................# 76 | ######################## 77 | ''' 78 | INPUT3 = '''\ 79 | ######################## 80 | #...............b.C.D.f# 81 | #.###################### 82 | #.....@.a.B.c.d.A.e.F.g# 83 | ######################## 84 | ''' 85 | INPUT4 = '''\ 86 | ################# 87 | #i.G..c...e..H.p# 88 | ########.######## 89 | #j.A..b...f..D.o# 90 | ########@######## 91 | #k.E..a...g..B.n# 92 | ########.######## 93 | #l.F..d...h..C.m# 94 | ################# 95 | ''' 96 | 97 | 98 | @pytest.mark.parametrize( 99 | ('input_s', 'expected'), 100 | ( 101 | (INPUT1, 8), 102 | (INPUT2, 86), 103 | (INPUT3, 132), 104 | (INPUT4, 136), 105 | ), 106 | ) 107 | def test(input_s: str, expected: int) -> None: 108 | assert compute(input_s) == expected 109 | 110 | 111 | def main() -> int: 112 | parser = argparse.ArgumentParser() 113 | parser.add_argument('data_file') 114 | args = parser.parse_args() 115 | 116 | with open(args.data_file) as f, timing(): 117 | print(compute(f.read())) 118 | 119 | return 0 120 | 121 | 122 | if __name__ == '__main__': 123 | exit(main()) 124 | -------------------------------------------------------------------------------- /day19/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day19/__init__.py -------------------------------------------------------------------------------- /day19/input.txt: -------------------------------------------------------------------------------- 1 | 109,424,203,1,21102,11,1,0,1106,0,282,21101,18,0,0,1106,0,259,1202,1,1,221,203,1,21101,31,0,0,1105,1,282,21101,0,38,0,1106,0,259,20101,0,23,2,22101,0,1,3,21102,1,1,1,21102,57,1,0,1105,1,303,2102,1,1,222,21002,221,1,3,21001,221,0,2,21101,0,259,1,21101,80,0,0,1106,0,225,21101,0,149,2,21101,91,0,0,1106,0,303,1202,1,1,223,21002,222,1,4,21102,1,259,3,21101,225,0,2,21101,0,225,1,21101,118,0,0,1106,0,225,21001,222,0,3,21102,1,58,2,21102,1,133,0,1106,0,303,21202,1,-1,1,22001,223,1,1,21101,148,0,0,1105,1,259,1201,1,0,223,21002,221,1,4,20102,1,222,3,21101,21,0,2,1001,132,-2,224,1002,224,2,224,1001,224,3,224,1002,132,-1,132,1,224,132,224,21001,224,1,1,21102,195,1,0,105,1,109,20207,1,223,2,20102,1,23,1,21101,0,-1,3,21102,1,214,0,1106,0,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,2101,0,-4,249,22101,0,-3,1,21201,-2,0,2,22101,0,-1,3,21101,250,0,0,1106,0,225,22101,0,1,-4,109,-5,2106,0,0,109,3,22107,0,-2,-1,21202,-1,2,-1,21201,-1,-1,-1,22202,-1,-2,-2,109,-3,2105,1,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,22101,0,-2,-2,109,-3,2106,0,0,109,5,22207,-3,-4,-1,1206,-1,346,22201,-4,-3,-4,21202,-3,-1,-1,22201,-4,-1,2,21202,2,-1,-1,22201,-4,-1,1,22101,0,-2,3,21101,0,343,0,1105,1,303,1105,1,415,22207,-2,-3,-1,1206,-1,387,22201,-3,-2,-3,21202,-2,-1,-1,22201,-3,-1,3,21202,3,-1,-1,22201,-3,-1,2,22102,1,-4,1,21102,1,384,0,1105,1,303,1105,1,415,21202,-4,-1,-4,22201,-4,-3,-4,22202,-3,-2,-2,22202,-2,-4,-4,22202,-3,-2,-3,21202,-4,-1,-2,22201,-3,-2,1,21202,1,1,-4,109,-5,2105,1,0 2 | -------------------------------------------------------------------------------- /day19/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Dict 4 | from typing import List 5 | from typing import NamedTuple 6 | 7 | from support import timing 8 | 9 | 10 | class Prog(NamedTuple): 11 | prog: Dict[int, int] 12 | pc: int 13 | value: int 14 | halted: bool 15 | 16 | 17 | def run( 18 | prog: Dict[int, int], 19 | pc: int, 20 | inputs: List[int], 21 | ) -> Prog: 22 | def parameter(instr: int, n: int) -> int: 23 | mode = instr // (10 ** (n + 1)) % 10 24 | if mode == 0: 25 | return prog[prog[pc + n]] 26 | elif mode == 1: 27 | return prog[pc + n] 28 | elif mode == 2: 29 | return prog[rb + prog[pc + n]] 30 | else: 31 | raise NotImplementedError(mode) 32 | 33 | def store(instr: int, n: int) -> int: 34 | mode = instr // (10 ** (n + 1)) % 10 35 | if mode == 0: 36 | return prog[pc + n] 37 | elif mode == 2: 38 | return rb + prog[pc + n] 39 | else: 40 | raise NotImplementedError(mode) 41 | 42 | rb = 0 43 | pc = 0 44 | while pc < len(prog): 45 | instr = prog[pc] 46 | opc = instr % 100 47 | if opc == 99: 48 | return Prog(prog, pc, -1, True) 49 | elif opc == 1: 50 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 51 | pc += 4 52 | elif opc == 2: 53 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 54 | pc += 4 55 | elif opc == 3: 56 | prog[store(instr, 1)] = inputs.pop() 57 | pc += 2 58 | elif opc == 4: 59 | return Prog(prog, pc + 2, parameter(instr, 1), False) 60 | elif opc == 5: 61 | if parameter(instr, 1): 62 | pc = parameter(instr, 2) 63 | else: 64 | pc += 3 65 | elif opc == 6: 66 | if not parameter(instr, 1): 67 | pc = parameter(instr, 2) 68 | else: 69 | pc += 3 70 | elif opc == 7: 71 | if parameter(instr, 1) < parameter(instr, 2): 72 | prog[store(instr, 3)] = 1 73 | else: 74 | prog[store(instr, 3)] = 0 75 | pc += 4 76 | elif opc == 8: 77 | if parameter(instr, 1) == parameter(instr, 2): 78 | prog[store(instr, 3)] = 1 79 | else: 80 | prog[store(instr, 3)] = 0 81 | pc += 4 82 | elif opc == 9: 83 | rb += parameter(instr, 1) 84 | pc += 2 85 | else: 86 | raise AssertionError(f'unreachable? {prog} {pc}') 87 | raise AssertionError(f'unreachable? {prog} {pc}') 88 | 89 | 90 | def compute(s: str) -> int: 91 | prog_l = [int(part) for part in s.strip().split(',')] 92 | prog_d = collections.defaultdict(int, enumerate(prog_l)) 93 | 94 | n = 0 95 | for y in range(50): 96 | for x in range(50): 97 | if run(prog_d.copy(), 0, [y, x]).value: 98 | n += 1 99 | 100 | return n 101 | 102 | 103 | def main() -> int: 104 | parser = argparse.ArgumentParser() 105 | parser.add_argument('data_file') 106 | args = parser.parse_args() 107 | 108 | with open(args.data_file) as f, timing(): 109 | print(compute(f.read())) 110 | 111 | return 0 112 | 113 | 114 | if __name__ == '__main__': 115 | exit(main()) 116 | -------------------------------------------------------------------------------- /day20/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day20/__init__.py -------------------------------------------------------------------------------- /day21/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day21/__init__.py -------------------------------------------------------------------------------- /day21/main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | """\ 7 | # some manual work I did to figure this out? 8 | ~ (B & ~C & H) 9 | 10 | ~ (~D | (A & (B | (E & I)) & (~B | C | ~H) )) 11 | 12 | OR E J 13 | AND I J 14 | OR B J 15 | AND A J 16 | 17 | NOT C T 18 | AND B T 19 | AND H T 20 | NOT T T 21 | AND T J 22 | 23 | NOT D T 24 | OR T J 25 | 26 | NOT J J 27 | RUN 28 | 29 | D && ( 30 | ~A | 31 | ((implied A) & ~B & (~E | ~I)) | 32 | # ??? 33 | ((implied A) & B & ~C & H) 34 | ) 35 | """ 36 | 37 | 38 | def run( 39 | prog: Dict[int, int], 40 | in_fn: Callable[[], int], 41 | out_fn: Callable[[int], None], 42 | ) -> None: 43 | def parameter(instr: int, n: int) -> int: 44 | mode = instr // (10 ** (n + 1)) % 10 45 | if mode == 0: 46 | return prog[prog[pc + n]] 47 | elif mode == 1: 48 | return prog[pc + n] 49 | elif mode == 2: 50 | return prog[rb + prog[pc + n]] 51 | else: 52 | raise NotImplementedError(mode) 53 | 54 | def store(instr: int, n: int) -> int: 55 | mode = instr // (10 ** (n + 1)) % 10 56 | if mode == 0: 57 | return prog[pc + n] 58 | elif mode == 2: 59 | return rb + prog[pc + n] 60 | else: 61 | raise NotImplementedError(mode) 62 | 63 | rb = 0 64 | pc = 0 65 | while pc < len(prog): 66 | instr = prog[pc] 67 | opc = instr % 100 68 | if opc == 99: 69 | return 70 | elif opc == 1: 71 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 72 | pc += 4 73 | elif opc == 2: 74 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 75 | pc += 4 76 | elif opc == 3: 77 | prog[store(instr, 1)] = in_fn() 78 | pc += 2 79 | elif opc == 4: 80 | out_fn(parameter(instr, 1)) 81 | pc += 2 82 | elif opc == 5: 83 | if parameter(instr, 1): 84 | pc = parameter(instr, 2) 85 | else: 86 | pc += 3 87 | elif opc == 6: 88 | if not parameter(instr, 1): 89 | pc = parameter(instr, 2) 90 | else: 91 | pc += 3 92 | elif opc == 7: 93 | if parameter(instr, 1) < parameter(instr, 2): 94 | prog[store(instr, 3)] = 1 95 | else: 96 | prog[store(instr, 3)] = 0 97 | pc += 4 98 | elif opc == 8: 99 | if parameter(instr, 1) == parameter(instr, 2): 100 | prog[store(instr, 3)] = 1 101 | else: 102 | prog[store(instr, 3)] = 0 103 | pc += 4 104 | elif opc == 9: 105 | rb += parameter(instr, 1) 106 | pc += 2 107 | else: 108 | raise AssertionError(f'unreachable? {prog} {pc}') 109 | raise AssertionError(f'unreachable? {prog} {pc}') 110 | 111 | 112 | def compute(s: str) -> None: 113 | prog = [int(part) for part in s.strip().split(',')] 114 | prog_d = collections.defaultdict(int, enumerate(prog)) 115 | 116 | script = '''\ 117 | OR E J 118 | AND I J 119 | OR B J 120 | AND A J 121 | NOT C T 122 | AND B T 123 | AND H T 124 | NOT T T 125 | AND T J 126 | NOT D T 127 | OR T J 128 | NOT J J 129 | RUN 130 | ''' 131 | script_iter = iter(script) 132 | 133 | def in_fn() -> int: 134 | return ord(next(script_iter)) 135 | 136 | def out_fn(n: int) -> None: 137 | if n < 256: 138 | print(chr(n), end='') 139 | else: 140 | print(n) 141 | 142 | print(','.join(str(n) for n in prog)) 143 | 144 | run(prog_d, in_fn, out_fn) 145 | 146 | 147 | def main() -> int: 148 | parser = argparse.ArgumentParser() 149 | parser.add_argument('data_file') 150 | args = parser.parse_args() 151 | 152 | contents = '' 153 | with open(args.data_file) as f: 154 | for line in f: 155 | line, _, _ = line.partition(';') 156 | line = line.strip() 157 | if line and not line.endswith(','): 158 | line = f'{line.strip()},' 159 | contents += line 160 | contents = contents.rstrip(',\n') 161 | 162 | compute(contents) 163 | 164 | return 0 165 | 166 | 167 | if __name__ == '__main__': 168 | exit(main()) 169 | -------------------------------------------------------------------------------- /day21/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | from support import timing 7 | 8 | 9 | def run( 10 | prog: Dict[int, int], 11 | in_fn: Callable[[], int], 12 | out_fn: Callable[[int], None], 13 | ) -> None: 14 | def parameter(instr: int, n: int) -> int: 15 | mode = instr // (10 ** (n + 1)) % 10 16 | if mode == 0: 17 | return prog[prog[pc + n]] 18 | elif mode == 1: 19 | return prog[pc + n] 20 | elif mode == 2: 21 | return prog[rb + prog[pc + n]] 22 | else: 23 | raise NotImplementedError(mode) 24 | 25 | def store(instr: int, n: int) -> int: 26 | mode = instr // (10 ** (n + 1)) % 10 27 | if mode == 0: 28 | return prog[pc + n] 29 | elif mode == 2: 30 | return rb + prog[pc + n] 31 | else: 32 | raise NotImplementedError(mode) 33 | 34 | rb = 0 35 | pc = 0 36 | while pc < len(prog): 37 | instr = prog[pc] 38 | opc = instr % 100 39 | if opc == 99: 40 | return 41 | elif opc == 1: 42 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 43 | pc += 4 44 | elif opc == 2: 45 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 46 | pc += 4 47 | elif opc == 3: 48 | prog[store(instr, 1)] = in_fn() 49 | pc += 2 50 | elif opc == 4: 51 | out_fn(parameter(instr, 1)) 52 | pc += 2 53 | elif opc == 5: 54 | if parameter(instr, 1): 55 | pc = parameter(instr, 2) 56 | else: 57 | pc += 3 58 | elif opc == 6: 59 | if not parameter(instr, 1): 60 | pc = parameter(instr, 2) 61 | else: 62 | pc += 3 63 | elif opc == 7: 64 | if parameter(instr, 1) < parameter(instr, 2): 65 | prog[store(instr, 3)] = 1 66 | else: 67 | prog[store(instr, 3)] = 0 68 | pc += 4 69 | elif opc == 8: 70 | if parameter(instr, 1) == parameter(instr, 2): 71 | prog[store(instr, 3)] = 1 72 | else: 73 | prog[store(instr, 3)] = 0 74 | pc += 4 75 | elif opc == 9: 76 | rb += parameter(instr, 1) 77 | pc += 2 78 | else: 79 | raise AssertionError(f'unreachable? {prog} {pc}') 80 | raise AssertionError(f'unreachable? {prog} {pc}') 81 | 82 | 83 | def compute(s: str) -> None: 84 | prog = [int(part) for part in s.strip().split(',')] 85 | prog_d = collections.defaultdict(int, enumerate(prog)) 86 | 87 | script = '''\ 88 | NOT A T 89 | OR T J 90 | NOT B T 91 | OR T J 92 | NOT C T 93 | OR T J 94 | NOT D T 95 | NOT T T 96 | AND T J 97 | WALK 98 | ''' 99 | script_iter = iter(script) 100 | 101 | def in_fn() -> int: 102 | return ord(next(script_iter)) 103 | 104 | def out_fn(n: int) -> None: 105 | if n < 256: 106 | print(chr(n), end='') 107 | else: 108 | print(n) 109 | 110 | run(prog_d, in_fn, out_fn) 111 | 112 | 113 | def main() -> int: 114 | parser = argparse.ArgumentParser() 115 | parser.add_argument('data_file') 116 | args = parser.parse_args() 117 | 118 | with open(args.data_file) as f, timing(): 119 | compute(f.read()) 120 | 121 | return 0 122 | 123 | 124 | if __name__ == '__main__': 125 | exit(main()) 126 | -------------------------------------------------------------------------------- /day21/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import collections 3 | from typing import Callable 4 | from typing import Dict 5 | 6 | from support import timing 7 | 8 | """\ 9 | # some manual work I did to figure this out? 10 | ~ (B & ~C & H) 11 | 12 | ~ (~D | (A & (B | (E & I)) & (~B | C | ~H) )) 13 | 14 | OR E J 15 | AND I J 16 | OR B J 17 | AND A J 18 | 19 | NOT C T 20 | AND B T 21 | AND H T 22 | NOT T T 23 | AND T J 24 | 25 | NOT D T 26 | OR T J 27 | 28 | NOT J J 29 | RUN 30 | 31 | D && ( 32 | ~A | 33 | ((implied A) & ~B & (~E | ~I)) | 34 | # ??? 35 | ((implied A) & B & ~C & H) 36 | ) 37 | """ 38 | 39 | 40 | def run( 41 | prog: Dict[int, int], 42 | in_fn: Callable[[], int], 43 | out_fn: Callable[[int], None], 44 | ) -> None: 45 | def parameter(instr: int, n: int) -> int: 46 | mode = instr // (10 ** (n + 1)) % 10 47 | if mode == 0: 48 | return prog[prog[pc + n]] 49 | elif mode == 1: 50 | return prog[pc + n] 51 | elif mode == 2: 52 | return prog[rb + prog[pc + n]] 53 | else: 54 | raise NotImplementedError(mode) 55 | 56 | def store(instr: int, n: int) -> int: 57 | mode = instr // (10 ** (n + 1)) % 10 58 | if mode == 0: 59 | return prog[pc + n] 60 | elif mode == 2: 61 | return rb + prog[pc + n] 62 | else: 63 | raise NotImplementedError(mode) 64 | 65 | rb = 0 66 | pc = 0 67 | while pc < len(prog): 68 | instr = prog[pc] 69 | opc = instr % 100 70 | if opc == 99: 71 | return 72 | elif opc == 1: 73 | prog[store(instr, 3)] = parameter(instr, 1) + parameter(instr, 2) 74 | pc += 4 75 | elif opc == 2: 76 | prog[store(instr, 3)] = parameter(instr, 1) * parameter(instr, 2) 77 | pc += 4 78 | elif opc == 3: 79 | prog[store(instr, 1)] = in_fn() 80 | pc += 2 81 | elif opc == 4: 82 | out_fn(parameter(instr, 1)) 83 | pc += 2 84 | elif opc == 5: 85 | if parameter(instr, 1): 86 | pc = parameter(instr, 2) 87 | else: 88 | pc += 3 89 | elif opc == 6: 90 | if not parameter(instr, 1): 91 | pc = parameter(instr, 2) 92 | else: 93 | pc += 3 94 | elif opc == 7: 95 | if parameter(instr, 1) < parameter(instr, 2): 96 | prog[store(instr, 3)] = 1 97 | else: 98 | prog[store(instr, 3)] = 0 99 | pc += 4 100 | elif opc == 8: 101 | if parameter(instr, 1) == parameter(instr, 2): 102 | prog[store(instr, 3)] = 1 103 | else: 104 | prog[store(instr, 3)] = 0 105 | pc += 4 106 | elif opc == 9: 107 | rb += parameter(instr, 1) 108 | pc += 2 109 | else: 110 | raise AssertionError(f'unreachable? {prog} {pc}') 111 | raise AssertionError(f'unreachable? {prog} {pc}') 112 | 113 | 114 | def compute(s: str) -> None: 115 | prog = [int(part) for part in s.strip().split(',')] 116 | prog_d = collections.defaultdict(int, enumerate(prog)) 117 | 118 | script = '''\ 119 | OR E J 120 | AND I J 121 | OR B J 122 | AND A J 123 | NOT C T 124 | AND B T 125 | AND H T 126 | NOT T T 127 | AND T J 128 | NOT D T 129 | OR T J 130 | NOT J J 131 | RUN 132 | ''' 133 | script_iter = iter(script) 134 | 135 | def in_fn() -> int: 136 | return ord(next(script_iter)) 137 | 138 | def out_fn(n: int) -> None: 139 | if n < 256: 140 | print(chr(n), end='') 141 | else: 142 | print(n) 143 | 144 | run(prog_d, in_fn, out_fn) 145 | 146 | 147 | def main() -> int: 148 | parser = argparse.ArgumentParser() 149 | parser.add_argument('data_file') 150 | args = parser.parse_args() 151 | 152 | with open(args.data_file) as f, timing(): 153 | compute(f.read()) 154 | 155 | return 0 156 | 157 | 158 | if __name__ == '__main__': 159 | exit(main()) 160 | -------------------------------------------------------------------------------- /day22/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day22/__init__.py -------------------------------------------------------------------------------- /day22/input.txt: -------------------------------------------------------------------------------- 1 | deal with increment 65 2 | deal into new stack 3 | deal with increment 25 4 | cut -6735 5 | deal with increment 3 6 | cut 8032 7 | deal with increment 71 8 | cut -4990 9 | deal with increment 66 10 | deal into new stack 11 | cut -8040 12 | deal into new stack 13 | deal with increment 18 14 | cut -8746 15 | deal with increment 42 16 | deal into new stack 17 | deal with increment 17 18 | cut -8840 19 | deal with increment 55 20 | cut -4613 21 | deal with increment 10 22 | cut -5301 23 | deal into new stack 24 | deal with increment 21 25 | cut -5653 26 | deal with increment 2 27 | cut 5364 28 | deal with increment 72 29 | cut -3468 30 | deal into new stack 31 | cut -9661 32 | deal with increment 63 33 | cut 6794 34 | deal with increment 43 35 | cut 2935 36 | deal with increment 66 37 | cut -1700 38 | deal with increment 6 39 | cut 5642 40 | deal with increment 64 41 | deal into new stack 42 | cut -5699 43 | deal with increment 43 44 | cut -9366 45 | deal with increment 42 46 | deal into new stack 47 | cut 2364 48 | deal with increment 13 49 | cut 8080 50 | deal with increment 2 51 | deal into new stack 52 | cut -9602 53 | deal with increment 51 54 | cut 3214 55 | deal into new stack 56 | cut -2995 57 | deal with increment 57 58 | cut -8169 59 | deal into new stack 60 | cut 362 61 | deal with increment 41 62 | cut -4547 63 | deal with increment 56 64 | cut -1815 65 | deal into new stack 66 | cut 1554 67 | deal with increment 71 68 | cut 2884 69 | deal with increment 44 70 | cut -2423 71 | deal with increment 4 72 | deal into new stack 73 | deal with increment 20 74 | cut -2242 75 | deal with increment 48 76 | cut -716 77 | deal with increment 29 78 | cut -6751 79 | deal with increment 45 80 | cut -9511 81 | deal with increment 75 82 | cut -440 83 | deal with increment 35 84 | cut 6861 85 | deal with increment 52 86 | cut -4702 87 | deal into new stack 88 | deal with increment 28 89 | cut 305 90 | deal with increment 16 91 | cut 7094 92 | deal into new stack 93 | cut 5175 94 | deal with increment 30 95 | deal into new stack 96 | deal with increment 61 97 | cut 1831 98 | deal into new stack 99 | deal with increment 25 100 | cut 4043 101 | -------------------------------------------------------------------------------- /day22/part1.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import List 3 | 4 | import pytest 5 | 6 | from support import timing 7 | 8 | 9 | def do_deck(s: str, decksize: int) -> List[int]: 10 | deck = list(range(decksize)) 11 | for line in s.splitlines(): 12 | if line == 'deal into new stack': 13 | deck.reverse() 14 | else: 15 | op_s, _, n_s = line.rpartition(' ') 16 | n = int(n_s) 17 | if op_s == 'cut': 18 | deck = deck[n:] + deck[:n] 19 | else: 20 | new_deck = [0] * decksize 21 | idx = 0 22 | for i in range(decksize): 23 | new_deck[idx] = deck[i] 24 | idx = (idx + n) % decksize 25 | deck = new_deck 26 | return deck 27 | 28 | 29 | def compute(s: str) -> int: 30 | deck = do_deck(s, 10007) 31 | return deck.index(2019) 32 | 33 | 34 | def do_deck_fast(s: str, idx: int, decksize: int) -> int: 35 | for line in s.splitlines(): 36 | if line == 'deal into new stack': 37 | idx = decksize - idx - 1 38 | else: 39 | op_s, _, n_s = line.rpartition(' ') 40 | n = int(n_s) 41 | if op_s == 'cut': 42 | idx = (idx - n) % decksize 43 | else: 44 | idx = (idx * n) % decksize 45 | return idx 46 | 47 | 48 | def compute_fast(s: str) -> int: 49 | return do_deck_fast(s, 2019, 10007) 50 | 51 | 52 | @pytest.mark.parametrize( 53 | ('input_s', 'expected'), 54 | ( 55 | ('deal into new stack', [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]), 56 | ('cut 3', [3, 4, 5, 6, 7, 8, 9, 0, 1, 2]), 57 | ('cut -3', [7, 8, 9, 0, 1, 2, 3, 4, 5, 6]), 58 | ('deal with increment 3', [0, 7, 4, 1, 8, 5, 2, 9, 6, 3]), 59 | ), 60 | ) 61 | def test(input_s: str, expected: int) -> None: 62 | assert do_deck(input_s, 10) == expected 63 | 64 | 65 | def main() -> int: 66 | parser = argparse.ArgumentParser() 67 | parser.add_argument('data_file') 68 | args = parser.parse_args() 69 | 70 | with open(args.data_file) as f, timing('original'): 71 | print(compute(f.read())) 72 | 73 | with open(args.data_file) as f, timing('faster'): 74 | print(compute_fast(f.read())) 75 | 76 | return 0 77 | 78 | 79 | if __name__ == '__main__': 80 | exit(main()) 81 | -------------------------------------------------------------------------------- /day22/part2.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from typing import NamedTuple 3 | 4 | from support import timing 5 | 6 | 7 | class Op(NamedTuple): 8 | s: str 9 | n: int 10 | 11 | 12 | def compute(s: str) -> int: 13 | deck_size = 119315717514047 14 | 15 | instructions = [] 16 | 17 | def _add_instruction(op: str, n: int) -> None: 18 | instructions.append(Op(op, n)) 19 | 20 | for line in s.splitlines(): 21 | if line == 'deal into new stack': 22 | _add_instruction('deal with increment', -1) 23 | # XXX: I don't know why this is negative? (copied) 24 | _add_instruction('cut', -1) 25 | else: 26 | s, _, n_s = line.rpartition(' ') 27 | if s == 'cut': 28 | # XXX: I don't know why this is negative? (copied) 29 | _add_instruction(s, -int(n_s)) 30 | else: 31 | _add_instruction(s, int(n_s)) 32 | 33 | cut_n = 0 34 | deal_n = 1 35 | for op in instructions: 36 | if op.s == 'cut': 37 | cut_n += op.n 38 | elif op.s == 'deal with increment': 39 | cut_n = (cut_n * op.n) % deck_size 40 | deal_n = (deal_n * op.n) % deck_size 41 | 42 | times = 101741582076661 43 | 44 | # cut_n = cut_n * (1 - deal_n ** times) // (1 - deal_n) % deck_size 45 | # [(a mod n)(b−1 mod n)] mod n 46 | 47 | cut_n = ( 48 | (cut_n * pow(deal_n, times, deck_size) - cut_n % deck_size) * 49 | pow(deal_n - 1, -1, deck_size) % deck_size 50 | ) 51 | deal_n = pow(deal_n, times, deck_size) 52 | 53 | # (x * deal_n + cut_n) % deck_size ~= 2020 54 | # => 55 | 56 | return (2020 + deck_size - cut_n) * pow(deal_n, -1, deck_size) % deck_size 57 | 58 | 59 | def main() -> int: 60 | parser = argparse.ArgumentParser() 61 | parser.add_argument('data_file') 62 | args = parser.parse_args() 63 | 64 | with open(args.data_file) as f, timing('original'): 65 | print(compute(f.read())) 66 | 67 | return 0 68 | 69 | 70 | if __name__ == '__main__': 71 | exit(main()) 72 | -------------------------------------------------------------------------------- /day24/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day24/__init__.py -------------------------------------------------------------------------------- /day24/input.txt: -------------------------------------------------------------------------------- 1 | ..#.# 2 | .#.## 3 | ##... 4 | .#.#. 5 | ###.. 6 | -------------------------------------------------------------------------------- /day24/part1.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | 5 | import pytest 6 | 7 | from support import timing 8 | 9 | 10 | def checksum(bugs: set[tuple[int, int]]) -> int: 11 | n = 0 12 | for y, x in bugs: 13 | n |= 1 << (y * 5 + x) 14 | return n 15 | 16 | 17 | def compute(s: str) -> int: 18 | seen = set() 19 | 20 | bugs = set() 21 | for y, line in enumerate(s.splitlines()): 22 | for x, c in enumerate(line): 23 | if c == '#': 24 | bugs.add((y, x)) 25 | 26 | seen = {checksum(bugs)} 27 | 28 | while True: 29 | newbugs = set() 30 | for y in range(5): 31 | for x in range(5): 32 | n_bugs = 0 33 | cs = ((y, x + 1), (y, x - 1), (y + 1, x), (y - 1, x)) 34 | for c_y, c_x in cs: 35 | if (c_y, c_x) in bugs: 36 | n_bugs += 1 37 | 38 | if (y, x) in bugs and n_bugs == 1: 39 | newbugs.add((y, x)) 40 | elif (y, x) not in bugs and n_bugs in (1, 2): 41 | newbugs.add((y, x)) 42 | 43 | new_cs = checksum(newbugs) 44 | if new_cs in seen: 45 | return new_cs 46 | else: 47 | seen.add(new_cs) 48 | bugs = newbugs 49 | 50 | 51 | INPUT_S = '''\ 52 | ....# 53 | #..#. 54 | #..## 55 | ..#.. 56 | #.... 57 | ''' 58 | 59 | 60 | @pytest.mark.parametrize( 61 | ('input_s', 'expected'), 62 | ( 63 | (INPUT_S, 2129920), 64 | ), 65 | ) 66 | def test(input_s: str, expected: int) -> None: 67 | assert compute(input_s) == expected 68 | 69 | 70 | def main() -> int: 71 | parser = argparse.ArgumentParser() 72 | parser.add_argument('data_file') 73 | args = parser.parse_args() 74 | 75 | with open(args.data_file) as f, timing(): 76 | print(compute(f.read())) 77 | 78 | return 0 79 | 80 | 81 | if __name__ == '__main__': 82 | exit(main()) 83 | -------------------------------------------------------------------------------- /day24/part2.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import argparse 4 | import collections 5 | from typing import Generator 6 | 7 | import pytest 8 | 9 | from support import timing 10 | 11 | 12 | def adjacent( 13 | z: int, 14 | y: int, 15 | x: int, 16 | ) -> Generator[tuple[int, int, int], None, None]: 17 | # above 18 | if y == 0: 19 | yield (z - 1, 1, 2) 20 | elif y == 3 and x == 2: 21 | for val in range(5): 22 | yield (z + 1, 4, val) 23 | else: 24 | yield (z, y - 1, x) 25 | 26 | # left 27 | if x == 0: 28 | yield (z - 1, 2, 1) 29 | elif y == 2 and x == 3: 30 | for val in range(5): 31 | yield (z + 1, val, 4) 32 | else: 33 | yield (z, y, x - 1) 34 | 35 | # down 36 | if y == 4: 37 | yield (z - 1, 3, 2) 38 | elif y == 1 and x == 2: 39 | for val in range(5): 40 | yield (z + 1, 0, val) 41 | else: 42 | yield (z, y + 1, x) 43 | 44 | # right 45 | if x == 4: 46 | yield (z - 1, 2, 3) 47 | elif y == 2 and x == 1: 48 | for val in range(5): 49 | yield (z + 1, val, 0) 50 | else: 51 | yield (z, y, x + 1) 52 | 53 | 54 | def compute(s: str, *, n: int = 200) -> int: 55 | bugs = set() 56 | for y, line in enumerate(s.splitlines()): 57 | for x, c in enumerate(line): 58 | if c == '#': 59 | bugs.add((0, y, x)) 60 | 61 | for _ in range(n): 62 | newbugs = set() 63 | 64 | counts: dict[tuple[int, int, int], int] = collections.defaultdict(int) 65 | 66 | for bug in bugs: 67 | for pos in adjacent(*bug): 68 | counts[pos] += 1 69 | 70 | for k, v in counts.items(): 71 | if k in bugs and v == 1: 72 | newbugs.add(k) 73 | elif k not in bugs and v in (1, 2): 74 | newbugs.add(k) 75 | 76 | bugs = newbugs 77 | 78 | return len(bugs) 79 | 80 | 81 | INPUT_S = '''\ 82 | ....# 83 | #..#. 84 | #..## 85 | ..#.. 86 | #.... 87 | ''' 88 | 89 | 90 | @pytest.mark.parametrize( 91 | ('input_s', 'expected'), 92 | ( 93 | (INPUT_S, 99), 94 | ), 95 | ) 96 | def test(input_s: str, expected: int) -> None: 97 | assert compute(input_s, n=10) == expected 98 | 99 | 100 | def main() -> int: 101 | parser = argparse.ArgumentParser() 102 | parser.add_argument('data_file') 103 | args = parser.parse_args() 104 | 105 | with open(args.data_file) as f, timing(): 106 | print(compute(f.read())) 107 | 108 | return 0 109 | 110 | 111 | if __name__ == '__main__': 112 | exit(main()) 113 | -------------------------------------------------------------------------------- /day25/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anthonywritescode/aoc2019/811c800db6c65c7890fd3c7b5b613450a1ac6d35/day25/__init__.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | -e support 2 | pytest 3 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [mypy] 2 | check_untyped_defs = true 3 | disallow_any_generics = true 4 | disallow_incomplete_defs = true 5 | disallow_untyped_defs = true 6 | no_implicit_optional = true 7 | warn_redundant_casts = true 8 | warn_unused_ignores = true 9 | -------------------------------------------------------------------------------- /support/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = support 3 | 4 | [options] 5 | py_modules = support 6 | -------------------------------------------------------------------------------- /support/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | setup() 3 | -------------------------------------------------------------------------------- /support/support.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import time 3 | from typing import Generator 4 | 5 | 6 | @contextlib.contextmanager 7 | def timing(name: str = '') -> Generator[None, None, None]: 8 | before = time.time() 9 | try: 10 | yield 11 | finally: 12 | after = time.time() 13 | t = (after - before) * 1000 14 | unit = 'ms' 15 | if t < 100: 16 | t *= 1000 17 | unit = 'μs' 18 | if name: 19 | name = f' ({name})' 20 | print(f'> {int(t)} {unit}{name}') 21 | --------------------------------------------------------------------------------