├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── badges │ └── completion.json └── workflows │ └── nodejs.yml ├── .gitignore ├── LICENSE ├── README.md ├── day-01-report-repair ├── README.md ├── report.js ├── report2.js └── test.js ├── day-02-password-philosophy ├── README.md ├── password.js ├── password2.js └── test.js ├── day-03-toboggan-trajectory ├── README.md ├── test.js ├── trajectory.js └── trajectory2.js ├── day-04-passport-processing ├── README.md ├── passport.js ├── passport2.js └── test.js ├── day-05-binary-boarding ├── README.md ├── boarding.js ├── boarding2.js └── test.js ├── day-06-custom-customs ├── README.md ├── customs.js ├── customs2.js └── test.js ├── day-07-handy-haversacks ├── README.md ├── haversacks.js ├── haversacks2.js └── test.js ├── day-08-handheld-halting ├── README.md ├── handheld.js ├── handheld2.js └── test.js ├── day-09-encoding-error ├── README.md ├── encoding.js ├── encoding2.js └── test.js ├── day-10-adapter-array ├── README.md ├── adapter.js ├── adapter2.js └── test.js ├── day-11-seating-system ├── README.md ├── seating.js ├── seating2.js └── test.js ├── day-12-rain-risk ├── README.md ├── ship.js ├── ship2.js └── test.js ├── day-13-shuttle-search ├── README.md ├── shuttle.js ├── shuttle2.js └── test.js ├── day-14-docking-data ├── README.md ├── docking.js ├── docking2.js └── test.js ├── day-15-rambunctious-recitation ├── README.md ├── recitation.js ├── recitation2.js └── test.js ├── day-16-ticket-translation ├── README.md ├── test.js ├── ticket.js └── ticket2.js ├── day-17-conway-cubes ├── README.md ├── cubes.js ├── cubes2.js └── test.js ├── day-18-operation-order ├── README.md ├── order.js ├── order2.js └── test.js ├── day-19-monster-messages ├── README.md ├── message.js ├── message2.js └── test.js ├── day-21-allergen-assessment ├── README.md ├── allergen-assessment.js ├── allergen-assessment2.js └── test.js ├── day-22-crab-combat ├── README.md ├── crab-combat.js └── test.js ├── package-lock.json └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true; 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | input.js 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'es6': true, 4 | 'node': true, 5 | 'mocha': true, 6 | }, 7 | 'extends': 'eslint:recommended', 8 | 'parserOptions': { 9 | 'sourceType': 'module', 10 | }, 11 | 'rules': { 12 | 'indent': ['error', 2, { 'SwitchCase': 1 }], 13 | 'quotes': [2, 'single'], 14 | 'semi': 2, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /.github/badges/completion.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "label": "completion", 4 | "message": "41/50", 5 | "color": "yellow" 6 | } 7 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [18.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: npm install, lint, and test 21 | run: | 22 | npm ci 23 | npm run lint 24 | npm test 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | 3 | input.js 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mario Tacke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent of Code 2020 - My Solutions 2 | [![Build Status](https://github.com/mariotacke/advent-of-code-2020/workflows/build/badge.svg)](https://github.com/mariotacke/advent-of-code-2020/actions) 3 | [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2020/master/.github/badges/completion.json)](https://github.com/mariotacke/advent-of-code-2020) 4 | ![Libraries.io dependency status for GitHub repo](https://img.shields.io/librariesio/github/mariotacke/advent-of-code-2020) 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/mariotacke/advent-of-code-2020/master/LICENSE) 6 | 7 | After saving Christmas [five years in a row](https://adventofcode.com/events), you've decided to take a vacation at a nice resort on a tropical island. Surely, Christmas will go on without you. 8 | 9 | The tropical island has its own currency and is entirely cash-only. The gold coins used there have a little picture of a starfish; the locals just call them **stars**. None of the currency exchanges seem to have heard of them, but somehow, you'll need to find fifty of these coins by the time you arrive so you can pay the deposit on your room. 10 | 11 | To save your vacation, you need to get all **fifty stars** by December 25th. 12 | 13 | Collect stars by solving puzzles. Two puzzles will be made available on each day in the Advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants **one star**. Good luck! 14 | 15 | ## Days 16 | 17 | - [Day 1: Report Repair](day-01-report-repair/) 18 | - [Day 2: Password Philosophy](day-02-password-philosophy/) 19 | - [Day 3: Toboggan Trajectory](day-03-toboggan-trajectory/) 20 | - [Day 4: Passport Processing](day-04-passport-processing/) 21 | - [Day 5: Binary Boarding](day-05-binary-boarding/) 22 | - [Day 6: Custom Customs](day-06-custom-customs/) 23 | - [Day 7: Handy Haversacks](day-07-handy-haversacks/) 24 | - [Day 8: Handheld Halting](day-08-handheld-halting/) 25 | - [Day 9: Encoding Error](day-09-encoding-error/) 26 | - [Day 10: Adapter Array](day-10-adapter-array/) 27 | - [Day 11: Seating System](day-11-seating-system/) 28 | - [Day 12: Rain Risk](day-12-rain-risk/) 29 | - [Day 13: Shuttle Search](day-13-shuttle-search/) 30 | - [Day 14: Docking Data](day-14-docking-data/) 31 | - [Day 15: Rambunctious Recitation](day-15-rambunctious-recitation/) 32 | - [Day 16: Ticket Translation](day-16-ticket-translation/) 33 | - [Day 17: Conway Cubes](day-17-conway-cubes/) 34 | - [Day 18: Operation Order](day-18-operation-order/) 35 | - [Day 19: Monster Messages](day-19-monster-messages/) 36 | - [Day 20: TBD](day-20/) 37 | - [Day 21: Allergen Assessment](day-21-allergen-assessment/) 38 | - [Day 22: Crab Combat](day-22-crab-combat/) 39 | - [Day 23: TBD](day-23/) 40 | - [Day 24: TBD](day-24/) 41 | - [Day 25: TBD](day-25/) 42 | 43 | ## Running Tests 44 | 45 | Each day contains its own set of tests. To run them type `npm test`. 46 | 47 | ## Previous Years 48 | - [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2019/master/.github/badges/completion.json&label=2019)](https://github.com/mariotacke/advent-of-code-2019) 49 | - [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2018/master/.github/badges/completion.json&label=2018)](https://github.com/mariotacke/advent-of-code-2018) 50 | - [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2017/master/.github/badges/completion.json&label=2017)](https://github.com/mariotacke/advent-of-code-2017) 51 | - [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2016/master/.github/badges/completion.json&label=2016)](https://github.com/mariotacke/advent-of-code-2016) 52 | - [![Completion Status](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/mariotacke/advent-of-code-2015/master/.github/badges/completion.json&label=2015)](https://github.com/mariotacke/advent-of-code-2015) 53 | 54 | ## Visit 55 | - http://adventofcode.com/2020 56 | -------------------------------------------------------------------------------- /day-01-report-repair/README.md: -------------------------------------------------------------------------------- 1 | # Day 1: Report Repair 2 | 3 | Before you leave, the Elves in accounting just need you to fix your **expense report** (your puzzle input); apparently, something isn't quite adding up. 4 | 5 | Specifically, they need you to **find the two entries that sum to `2020`** and then multiply those two numbers together. 6 | 7 | For example, suppose your expense report contained the following: 8 | 9 | ``` 10 | 1721 11 | 979 12 | 366 13 | 299 14 | 675 15 | 1456 16 | ``` 17 | 18 | In this list, the two entries that sum to `2020` are `1721` and `299`. Multiplying them together produces `1721 * 299 = 514579`, so the correct answer is **`514579`**. 19 | 20 | Of course, your expense report is much larger. **Find the two entries that sum to `2020`; what do you get if you multiply them together?** 21 | 22 | ## Part Two 23 | 24 | The Elves in accounting are thankful for your help; one of them even offers you a starfish coin they had left over from a past vacation. They offer you a second one if you can find **three** numbers in your expense report that meet the same criteria. 25 | 26 | Using the above example again, the three entries that sum to `2020` are `979`, `366`, and `675`. Multiplying them together produces the answer, **`241861950`**. 27 | 28 | In your expense report, **what is the product of the three entries that sum to `2020`?** 29 | 30 | ## References 31 | - https://adventofcode.com/2020/day/1 32 | -------------------------------------------------------------------------------- /day-01-report-repair/report.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const numbers = input.split('\n').map((line) => parseInt(line)); 3 | 4 | for (let i = 0; i < numbers.length; i++) { 5 | for (let j = 0; j < numbers.length; j++) { 6 | if (numbers[i] + numbers[j] === 2020) { 7 | return numbers[i] * numbers[j]; 8 | } 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /day-01-report-repair/report2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const numbers = input.split('\n').map((line) => parseInt(line)); 3 | 4 | for (let i = 0; i < numbers.length; i++) { 5 | for (let j = 0; j < numbers.length; j++) { 6 | for (let k = 0; k < numbers.length; k++) { 7 | if (numbers[i] + numbers[j] + numbers[k] === 2020) { 8 | return numbers[i] * numbers[j] * numbers[k]; 9 | } 10 | } 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /day-01-report-repair/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const report = require('./report'); 4 | const report2 = require('./report2'); 5 | 6 | describe('Day 1: Report Repair', () => { 7 | it('should determine numbers adding to 2020', () => { 8 | const numbers = 9 | `1721 10 | 979 11 | 366 12 | 299 13 | 675 14 | 1456`; 15 | 16 | assert.strictEqual(report(numbers), 514579); 17 | }); 18 | 19 | describe('Part Two', () => { 20 | it('should determine three numbers adding to 2020', () => { 21 | const numbers = 22 | `1721 23 | 979 24 | 366 25 | 299 26 | 675 27 | 1456`; 28 | 29 | assert.strictEqual(report2(numbers), 241861950); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /day-02-password-philosophy/README.md: -------------------------------------------------------------------------------- 1 | # Day 2: Password Philosophy 2 | 3 | Your flight departs in a few days from the coastal airport; the easiest way down to the coast from here is via [toboggan](https://en.wikipedia.org/wiki/Toboggan). 4 | 5 | The shopkeeper at the North Pole Toboggan Rental Shop is having a bad day. "Something's wrong with our computers; we can't log in!" You ask if you can take a look. 6 | 7 | Their password database seems to be a little corrupted: some of the passwords wouldn't have been allowed by the Official Toboggan Corporate Policy that was in effect when they were chosen. 8 | 9 | To try to debug the problem, they have created a list (your puzzle input) of **passwords** (according to the corrupted database) and the **corporate policy when that password was set**. 10 | 11 | For example, suppose you have the following list: 12 | 13 | ``` 14 | 1-3 a: abcde 15 | 1-3 b: cdefg 16 | 2-9 c: ccccccccc 17 | ``` 18 | 19 | Each line gives the password policy and then the password. The password policy indicates the lowest and highest number of times a given letter must appear for the password to be valid. For example, `1-3 a` means that the password must contain `a` at least `1` time and at most `3` times. 20 | 21 | In the above example, **`2`** passwords are valid. The middle password, `cdefg`, is not; it contains no instances of `b`, but needs at least `1`. The first and third passwords are valid: they contain one `a` or nine `c`, both within the limits of their respective policies. 22 | 23 | **How many passwords are valid** according to their policies? 24 | 25 | ## Part Two 26 | 27 | While it appears you validated the passwords correctly, they don't seem to be what the Official Toboggan Corporate Authentication System is expecting. 28 | 29 | The shopkeeper suddenly realizes that he just accidentally explained the password policy rules from his old job at the sled rental place down the street! The Official Toboggan Corporate Policy actually works a little differently. 30 | 31 | Each policy actually describes two **positions in the password**, where `1` means the first character, `2` means the second character, and so on. (Be careful; Toboggan Corporate Policies have no concept of "index zero"!) **Exactly one of these positions** must contain the given letter. Other occurrences of the letter are irrelevant for the purposes of policy enforcement. 32 | 33 | Given the same example list from above: 34 | - `1-3 a: abcde` is **valid**: position `1` contains `a` and position `3` does not. 35 | - `1-3 b: cdefg` is **invalid**: neither position `1` nor position `3` contains `b`. 36 | - `2-9 c: ccccccccc` is **invalid**: both position `2` and position `9` contain `c`. 37 | 38 | **How many passwords are valid** according to the new interpretation of the policies? 39 | 40 | ## References 41 | - https://adventofcode.com/2020/day/2 42 | -------------------------------------------------------------------------------- /day-02-password-philosophy/password.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | return input.split('\n').reduce((numberOfValidPasswords, line) => { 3 | const [, min, max, character, password] = 4 | /(\d+)-(\d+) ([a-z]): ([a-z]+)/.exec(line); 5 | 6 | const count = password.split('').filter((c) => c === character).length; 7 | 8 | return count >= min && count <= max 9 | ? numberOfValidPasswords + 1 10 | : numberOfValidPasswords; 11 | }, 0); 12 | }; 13 | -------------------------------------------------------------------------------- /day-02-password-philosophy/password2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | return input.split('\n').reduce((numberOfValidPasswords, line) => { 3 | const [, min, max, character, password] = 4 | /(\d+)-(\d+) ([a-z]): ([a-z]+)/.exec(line); 5 | 6 | if ((password[min - 1] === character && password[max - 1] !== character) || 7 | (password[min - 1] !== character && password[max - 1] === character)) { 8 | numberOfValidPasswords++; 9 | } 10 | 11 | return numberOfValidPasswords; 12 | }, 0); 13 | }; 14 | -------------------------------------------------------------------------------- /day-02-password-philosophy/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const password = require('./password'); 4 | const password2 = require('./password2'); 5 | 6 | describe('Day 2: Password Philosophy', () => { 7 | it('should validate passwords', () => { 8 | const input = 9 | `1-3 a: abcde 10 | 1-3 b: cdefg 11 | 2-9 c: ccccccccc`; 12 | 13 | assert.strictEqual(password(input), 2); 14 | }); 15 | 16 | describe('Part Two', () => { 17 | it('should validate passwords again', () => { 18 | const input = 19 | `1-3 a: abcde 20 | 1-3 b: cdefg 21 | 2-9 c: ccccccccc`; 22 | 23 | assert.strictEqual(password2(input), 1); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /day-03-toboggan-trajectory/README.md: -------------------------------------------------------------------------------- 1 | # Day 3: Toboggan Trajectory 2 | 3 | With the toboggan login problems resolved, you set off toward the airport. While travel by toboggan might be easy, it's certainly not safe: there's very minimal steering and the area is covered in trees. You'll need to see which angles will take you near the fewest trees. 4 | 5 | Due to the local geology, trees in this area only grow on exact integer coordinates in a grid. You make a map (your puzzle input) of the open squares (`.`) and trees (`#`) you can see. For example: 6 | 7 | ``` 8 | ..##....... 9 | #...#...#.. 10 | .#....#..#. 11 | ..#.#...#.# 12 | .#...##..#. 13 | ..#.##..... 14 | .#.#.#....# 15 | .#........# 16 | #.##...#... 17 | #...##....# 18 | .#..#...#.# 19 | ``` 20 | 21 | These aren't the only trees, though; due to something you read about once involving arboreal genetics and biome stability, the same pattern repeats to the right many times: 22 | 23 | ``` 24 | ..##.........##.........##.........##.........##.........##....... ---> 25 | #...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#.. 26 | .#....#..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#. 27 | ..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.# 28 | .#...##..#..#...##..#..#...##..#..#...##..#..#...##..#..#...##..#. 29 | ..#.##.......#.##.......#.##.......#.##.......#.##.......#.##..... ---> 30 | .#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....# 31 | .#........#.#........#.#........#.#........#.#........#.#........# 32 | #.##...#...#.##...#...#.##...#...#.##...#...#.##...#...#.##...#... 33 | #...##....##...##....##...##....##...##....##...##....##...##....# 34 | .#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.# ---> 35 | ``` 36 | 37 | You start on the open square (`.`) in the top-left corner and need to reach the bottom (below the bottom-most row on your map). 38 | 39 | The toboggan can only follow a few specific slopes (you opted for a cheaper model that prefers rational numbers); start by **counting all the trees** you would encounter for the slope **right 3, down 1**: 40 | 41 | From your starting position at the top-left, check the position that is right 3 and down 1. Then, check the position that is right 3 and down 1 from there, and so on until you go past the bottom of the map. 42 | 43 | The locations you'd check in the above example are marked here with **`O`** where there was an open square and X where there was a tree: 44 | 45 | ``` 46 | ..##.........##.........##.........##.........##.........##....... ---> 47 | #..O#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#.. 48 | .#....X..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#. 49 | ..#.#...#O#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.# 50 | .#...##..#..X...##..#..#...##..#..#...##..#..#...##..#..#...##..#. 51 | ..#.##.......#.X#.......#.##.......#.##.......#.##.......#.##..... ---> 52 | .#.#.#....#.#.#.#.O..#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....# 53 | .#........#.#........X.#........#.#........#.#........#.#........# 54 | #.##...#...#.##...#...#.X#...#...#.##...#...#.##...#...#.##...#... 55 | #...##....##...##....##...#X....##...##....##...##....##...##....# 56 | .#..#...#.#.#..#...#.#.#..#...X.#.#..#...#.#.#..#...#.#.#..#...#.# ---> 57 | ``` 58 | 59 | In this example, traversing the map using this slope would cause you to encounter **`7`** trees. 60 | 61 | Starting at the top-left corner of your map and following a slope of right 3 and down 1, **how many trees would you encounter?** 62 | 63 | ## Part Two 64 | 65 | Time to check the rest of the slopes - you need to minimize the probability of a sudden arboreal stop, after all. 66 | 67 | Determine the number of trees you would encounter if, for each of the following slopes, you start at the top-left corner and traverse the map all the way to the bottom: 68 | 69 | - Right 1, down 1. 70 | - Right 3, down 1. (This is the slope you already checked.) 71 | - Right 5, down 1. 72 | - Right 7, down 1. 73 | - Right 1, down 2. 74 | 75 | In the above example, these slopes would find `2`, `7`, `3`, `4`, and `2` tree(s) respectively; multiplied together, these produce the answer **`336`**. 76 | 77 | **What do you get if you multiply together the number of trees encountered on each of the listed slopes?** 78 | 79 | ## References 80 | - https://adventofcode.com/2020/day/3 81 | -------------------------------------------------------------------------------- /day-03-toboggan-trajectory/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const trajectory = require('./trajectory'); 4 | const trajectory2 = require('./trajectory2'); 5 | 6 | describe('Day 3: Toboggan Trajectory', () => { 7 | it('should count trees on the way down', () => { 8 | const input = 9 | `..##....... 10 | #...#...#.. 11 | .#....#..#. 12 | ..#.#...#.# 13 | .#...##..#. 14 | ..#.##..... 15 | .#.#.#....# 16 | .#........# 17 | #.##...#... 18 | #...##....# 19 | .#..#...#.#`; 20 | 21 | assert.strictEqual(trajectory(input), 7); 22 | }); 23 | 24 | describe('Part Two', () => { 25 | it('should count and multiply trees on the way down', () => { 26 | const input = 27 | `..##....... 28 | #...#...#.. 29 | .#....#..#. 30 | ..#.#...#.# 31 | .#...##..#. 32 | ..#.##..... 33 | .#.#.#....# 34 | .#........# 35 | #.##...#... 36 | #...##....# 37 | .#..#...#.#`; 38 | 39 | assert.strictEqual(trajectory2(input), 336); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /day-03-toboggan-trajectory/trajectory.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const map = input.split('\n').map((line) => line.trim().split('')); 3 | 4 | let x = 0; 5 | let y = 0; 6 | let counter = 0; 7 | 8 | while (y < map.length) { 9 | if (map[y][x % map[0].length] === '#') { 10 | counter++; 11 | } 12 | 13 | x += 3; 14 | y += 1; 15 | } 16 | 17 | return counter; 18 | }; 19 | -------------------------------------------------------------------------------- /day-03-toboggan-trajectory/trajectory2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const map = input.split('\n').map((line) => line.trim().split('')); 3 | 4 | function trees(stepsX, stepsY) { 5 | let x = 0; 6 | let y = 0; 7 | let counter = 0; 8 | 9 | while (y < map.length) { 10 | if (map[y][x % map[0].length] === '#') { 11 | counter++; 12 | } 13 | 14 | x += stepsX; 15 | y += stepsY; 16 | } 17 | 18 | return counter; 19 | } 20 | 21 | return trees(1, 1) * trees(3, 1) * trees(5, 1) * trees(7, 1) * trees(1, 2); 22 | }; 23 | -------------------------------------------------------------------------------- /day-04-passport-processing/README.md: -------------------------------------------------------------------------------- 1 | # Day 4: Passport Processing 2 | 3 | You arrive at the airport only to realize that you grabbed your North Pole Credentials instead of your passport. While these documents are extremely similar, North Pole Credentials aren't issued by a country and therefore aren't actually valid documentation for travel in most of the world. 4 | 5 | It seems like you're not the only one having problems, though; a very long line has formed for the automatic passport scanners, and the delay could upset your travel itinerary. 6 | 7 | Due to some questionable network security, you realize you might be able to solve both of these problems at the same time. 8 | 9 | The automatic passport scanners are slow because they're having trouble **detecting which passports have all required fields**. The expected fields are as follows: 10 | 11 | - `byr` (Birth Year) 12 | - `iyr` (Issue Year) 13 | - `eyr` (Expiration Year) 14 | - `hgt` (Height) 15 | - `hcl` (Hair Color) 16 | - `ecl` (Eye Color) 17 | - `pid` (Passport ID) 18 | - `cid` (Country ID) 19 | 20 | Passport data is validated in batch files (your puzzle input). Each passport is represented as a sequence of `key:value` pairs separated by spaces or newlines. Passports are separated by blank lines. 21 | 22 | Here is an example batch file containing four passports: 23 | 24 | ``` 25 | ecl:gry pid:860033327 eyr:2020 hcl:#fffffd 26 | byr:1937 iyr:2017 cid:147 hgt:183cm 27 | 28 | iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 29 | hcl:#cfa07d byr:1929 30 | 31 | hcl:#ae17e1 iyr:2013 32 | eyr:2024 33 | ecl:brn pid:760753108 byr:1931 34 | hgt:179cm 35 | 36 | hcl:#cfa07d eyr:2025 pid:166559648 37 | iyr:2011 ecl:brn hgt:59in 38 | ``` 39 | 40 | The first passport is **valid** - all eight fields are present. The second passport is **invalid** - it is missing `hgt` (the Height field). 41 | 42 | The third passport is interesting; the **only missing field** is `cid`, so it looks like data from North Pole Credentials, not a passport at all! Surely, nobody would mind if you made the system temporarily ignore missing `cid` fields. Treat this "passport" as **valid**. 43 | 44 | The fourth passport is missing two fields, `cid` and `byr`. Missing `cid` is fine, but missing any other field is not, so this passport is **invalid**. 45 | 46 | According to the above rules, your improved system would report **`2`** valid passports. 47 | 48 | Count the number of **valid** passports - those that have all required fields. Treat `cid` as optional. **In your batch file, how many passports are valid?** 49 | 50 | ## Part Two 51 | 52 | The line is moving more quickly now, but you overhear airport security talking about how passports with invalid data are getting through. Better add some data validation, quick! 53 | 54 | You can continue to ignore the `cid` field, but each other field has strict rules about what values are valid for automatic validation: 55 | 56 | - `byr` (Birth Year) - four digits; at least `1920` and at most `2002`. 57 | - `iyr` (Issue Year) - four digits; at least `2010` and at most `2020`. 58 | - `eyr` (Expiration Year) - four digits; at least `2020` and at most `2030`. 59 | - `hgt` (Height) - a number followed by either `cm` or `in`: 60 | - If `cm`, the number must be at least `150` and at most `193`. 61 | - If `in`, the number must be at least `59` and at most `76`. 62 | - `hcl` (Hair Color) - a `#` followed by exactly six characters `0`-`9` or `a`-`f`. 63 | - `ecl` (Eye Color) - exactly one of: `amb` `blu` `brn` `gry` `grn` `hzl` `oth`. 64 | - `pid` (Passport ID) - a nine-digit number, including leading zeroes. 65 | - `cid` (Country ID) - ignored, missing or not. 66 | 67 | Your job is to count the passports where all required fields are both **present** and **valid** according to the above rules. Here are some example values: 68 | 69 | ``` 70 | byr valid: 2002 71 | byr invalid: 2003 72 | 73 | hgt valid: 60in 74 | hgt valid: 190cm 75 | hgt invalid: 190in 76 | hgt invalid: 190 77 | 78 | hcl valid: #123abc 79 | hcl invalid: #123abz 80 | hcl invalid: 123abc 81 | 82 | ecl valid: brn 83 | ecl invalid: wat 84 | 85 | pid valid: 000000001 86 | pid invalid: 0123456789 87 | ``` 88 | 89 | Here are some invalid passports: 90 | 91 | ``` 92 | eyr:1972 cid:100 93 | hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926 94 | 95 | iyr:2019 96 | hcl:#602927 eyr:1967 hgt:170cm 97 | ecl:grn pid:012533040 byr:1946 98 | 99 | hcl:dab227 iyr:2012 100 | ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277 101 | 102 | hgt:59cm ecl:zzz 103 | eyr:2038 hcl:74454a iyr:2023 104 | pid:3556412378 byr:2007 105 | ``` 106 | 107 | Here are some valid passports: 108 | 109 | ``` 110 | pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980 111 | hcl:#623a2f 112 | 113 | eyr:2029 ecl:blu cid:129 byr:1989 114 | iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm 115 | 116 | hcl:#888785 117 | hgt:164cm byr:2001 iyr:2015 cid:88 118 | pid:545766238 ecl:hzl 119 | eyr:2022 120 | 121 | iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719 122 | ``` 123 | 124 | Count the number of **valid** passports - those that have all required fields **and valid values**. Continue to treat `cid` as optional. **In your batch file, how many passports are valid?** 125 | 126 | ## References 127 | - https://adventofcode.com/2020/day/4 128 | -------------------------------------------------------------------------------- /day-04-passport-processing/passport.js: -------------------------------------------------------------------------------- 1 | function validate(passport) { 2 | const requiredFields = ['ecl', 'pid','eyr', 'hcl','byr', 'iyr', 'hgt']; 3 | const fields = passport 4 | .split('\n') 5 | .join(' ') 6 | .split(' ') 7 | .map((field) => /(\w+):/.exec(field)[1]); 8 | 9 | return requiredFields.every((field) => fields.includes(field)); 10 | } 11 | 12 | module.exports = (input) => { 13 | return input.split(/\n{2,}/g).filter(validate).length; 14 | }; 15 | -------------------------------------------------------------------------------- /day-04-passport-processing/passport2.js: -------------------------------------------------------------------------------- 1 | function validate(passport) { 2 | const requiredFields = ['ecl', 'pid','eyr', 'hcl','byr', 'iyr', 'hgt']; 3 | const fields = passport 4 | .split('\n') 5 | .join(' ') 6 | .split(' ') 7 | .reduce((keys, entry) => { 8 | const [, field, value] = /(\w+):(.*)/.exec(entry); 9 | 10 | keys[field] = value; 11 | 12 | return keys; 13 | }, {}); 14 | 15 | if (!requiredFields.every((entry) => Object.keys(fields).includes(entry))) { 16 | return false; 17 | } 18 | 19 | const birthYear = +/(\d{4})/.exec(fields['byr'])[1]; 20 | const issueYear = +/(\d{4})/.exec(fields['iyr'])[1]; 21 | const expirationYear = +/(\d{4})/.exec(fields['eyr'])[1]; 22 | const [, height, heightUnit] = fields['hgt'].match(/(\d+)(cm|in)?/); 23 | const hairColor = fields['hcl']; 24 | const eyeColor = fields['ecl']; 25 | const passportId = fields['pid']; 26 | 27 | return birthYear >= 1920 && birthYear <= 2002 && 28 | issueYear >= 2010 && issueYear <= 2020 && 29 | expirationYear >= 2020 && expirationYear <= 2030 && 30 | ( 31 | (heightUnit === 'cm' && +height >= 150 && +height <= 193) || 32 | (heightUnit === 'in' && +height >= 59 && +height <= 76) 33 | ) && 34 | /#[a-f0-9]{6}/.test(hairColor) && 35 | /(amb|blu|brn|gry|grn|hzl|oth)/.test(eyeColor) && 36 | /^\d{9}$/.test(passportId); 37 | } 38 | 39 | module.exports = (input) => { 40 | return input.split(/\n{2,}/g).filter(validate).length; 41 | }; 42 | -------------------------------------------------------------------------------- /day-04-passport-processing/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const passport = require('./passport'); 4 | const passport2 = require('./passport2'); 5 | 6 | const displayHelper = (input) => input.split('\n').map((line) => line.trim()).join('\n'); 7 | 8 | describe('Day 4: Passport Processing', () => { 9 | it('should validate passports', () => { 10 | const passports = 11 | `ecl:gry pid:860033327 eyr:2020 hcl:#fffffd 12 | byr:1937 iyr:2017 cid:147 hgt:183cm 13 | 14 | iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 15 | hcl:#cfa07d byr:1929 16 | 17 | hcl:#ae17e1 iyr:2013 18 | eyr:2024 19 | ecl:brn pid:760753108 byr:1931 20 | hgt:179cm 21 | 22 | hcl:#cfa07d eyr:2025 pid:166559648 23 | iyr:2011 ecl:brn hgt:59in`; 24 | 25 | assert.strictEqual(passport(displayHelper(passports)), 2); 26 | }); 27 | 28 | describe('Part Two', () => { 29 | it('should validate passports (invalid)', () => { 30 | const passports = 31 | `eyr:1972 cid:100 32 | hcl:#18171d ecl:amb hgt:170 pid:186cm iyr:2018 byr:1926 33 | 34 | iyr:2019 35 | hcl:#602927 eyr:1967 hgt:170cm 36 | ecl:grn pid:012533040 byr:1946 37 | 38 | hcl:dab227 iyr:2012 39 | ecl:brn hgt:182cm pid:021572410 eyr:2020 byr:1992 cid:277 40 | 41 | hgt:59cm ecl:zzz 42 | eyr:2038 hcl:74454a iyr:2023 43 | pid:3556412378 byr:2007`; 44 | 45 | assert.strictEqual(passport2(displayHelper(passports)), 0); 46 | }); 47 | 48 | it('should validate passports (valid)', () => { 49 | const passports = 50 | `pid:087499704 hgt:74in ecl:grn iyr:2012 eyr:2030 byr:1980 51 | hcl:#623a2f 52 | 53 | eyr:2029 ecl:blu cid:129 byr:1989 54 | iyr:2014 pid:896056539 hcl:#a97842 hgt:165cm 55 | 56 | hcl:#888785 57 | hgt:164cm byr:2001 iyr:2015 cid:88 58 | pid:545766238 ecl:hzl 59 | eyr:2022 60 | 61 | iyr:2010 hgt:158cm hcl:#b6652a ecl:blu byr:1944 eyr:2021 pid:093154719`; 62 | 63 | assert.strictEqual(passport2(displayHelper(passports)), 4); 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /day-05-binary-boarding/README.md: -------------------------------------------------------------------------------- 1 | # Day 5: Binary Boarding 2 | 3 | You board your plane only to discover a new problem: you dropped your boarding pass! You aren't sure which seat is yours, and all of the flight attendants are busy with the flood of people that suddenly made it through passport control. 4 | 5 | You write a quick program to use your phone's camera to scan all of the nearby boarding passes (your puzzle input); perhaps you can find your seat through process of elimination. 6 | 7 | Instead of [zones or groups](https://www.youtube.com/watch?v=oAHbLRjF0vo), this airline uses **binary space partitioning** to seat people. A seat might be specified like `FBFBBFFRLR`, where `F` means "front", `B` means "back", `L` means "left", and `R` means "right". 8 | 9 | The first 7 characters will either be `F` or `B`; these specify exactly one of the **128 rows** on the plane (numbered `0` through `127`). Each letter tells you which half of a region the given seat is in. Start with the whole list of rows; the first letter indicates whether the seat is in the **front** (`0` through `63`) or the **back** (`64` through `127`). The next letter indicates which half of that region the seat is in, and so on until you're left with exactly one row. 10 | 11 | For example, consider just the first seven characters of `FBFBBFFRLR`: 12 | 13 | - Start by considering the whole range, rows 0 through 127. 14 | - `F` means to take the **lower half**, keeping rows `0` through `63`. 15 | - `B` means to take the **upper half**, keeping rows `32` through `63`. 16 | - `F` means to take the **lower half**, keeping rows `32` through `47`. 17 | - `B` means to take the **upper half**, keeping rows `40` through `47`. 18 | - `B` keeps rows `44` through `47`. 19 | - `F` keeps rows `44` through `45`. 20 | - The final `F` keeps the lower of the two, **row `44`**. 21 | 22 | The last three characters will be either `L` or `R`; these specify exactly one of the **8 columns** of seats on the plane (numbered `0` through `7`). The same process as above proceeds again, this time with only three steps. `L` means to keep the **lower half**, while `R` means to keep the **upper half**. 23 | 24 | For example, consider just the last 3 characters of `FBFBBFFRLR`: 25 | 26 | - Start by considering the whole range, columns `0` through `7`. 27 | - `R` means to take the **upper half**, keeping columns `4` through `7`. 28 | - `L` means to take the **lower half**, keeping columns `4` through `5`. 29 | - The final `R` keeps the upper of the two, **column `5`**. 30 | 31 | So, decoding `FBFBBFFRLR` reveals that it is the seat at **row `44`, column `5`**. 32 | 33 | Every seat also has a unique **seat ID**: multiply the row by 8, then add the column. In this example, the seat has ID `44 * 8 + 5 = 357`. 34 | 35 | Here are some other boarding passes: 36 | 37 | - `BFFFBBFRRR`: row `70`, column `7`, seat ID `567`. 38 | - `FFFBBBFRRR`: row `14`, column `7`, seat ID `119`. 39 | - `BBFFBBFRLL`: row `102`, column `4`, seat ID `820`. 40 | 41 | As a sanity check, look through your list of boarding passes. **What is the highest seat ID on a boarding pass?** 42 | 43 | ## Part Two 44 | 45 | **Ding!** The "fasten seat belt" signs have turned on. Time to find your seat. 46 | 47 | It's a completely full flight, so your seat should be the only missing boarding pass in your list. However, there's a catch: some of the seats at the very front and back of the plane don't exist on this aircraft, so they'll be missing from your list as well. 48 | 49 | Your seat wasn't at the very front or back, though; the seats with IDs +1 and -1 from yours will be in your list. 50 | 51 | **What is the ID of your seat?** 52 | 53 | ## References 54 | - https://adventofcode.com/2020/day/5 55 | -------------------------------------------------------------------------------- /day-05-binary-boarding/boarding.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | return Math.max(...input.split('\n').map((line) => { 3 | return parseInt(line.replace(/F|L/g, 0).replace(/B|R/g, 1), 2); 4 | })); 5 | }; 6 | -------------------------------------------------------------------------------- /day-05-binary-boarding/boarding2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const boardingPasses = new Set(input.split('\n').map((line) => { 3 | return parseInt(line.replace(/F|L/g, 0).replace(/B|R/g, 1), 2); 4 | })); 5 | 6 | for (const seatId of boardingPasses.values()) { 7 | if (!boardingPasses.has(seatId - 1) && boardingPasses.has(seatId - 2)) { 8 | return seatId - 1; 9 | } 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /day-05-binary-boarding/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const boardingPass = require('./boarding'); 4 | 5 | describe('Day 5: Binary Boarding', () => { 6 | it('should compute seat ID for FBFBBFFRLR', () => { 7 | assert.strictEqual(boardingPass('FBFBBFFRLR'), 357); 8 | }); 9 | 10 | it('should compute seat ID for BFFFBBFRRR', () => { 11 | assert.strictEqual(boardingPass('BFFFBBFRRR'), 567); 12 | }); 13 | 14 | it('should compute seat ID for FFFBBBFRRR', () => { 15 | assert.strictEqual(boardingPass('FFFBBBFRRR'), 119); 16 | }); 17 | 18 | it('should compute seat ID for BBFFBBFRLL', () => { 19 | assert.strictEqual(boardingPass('BBFFBBFRLL'), 820); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /day-06-custom-customs/README.md: -------------------------------------------------------------------------------- 1 | # Day 6: Custom Customs 2 | 3 | As your flight approaches the regional airport where you'll switch to a much larger plane, [customs declaration](https://en.wikipedia.org/wiki/Customs_declaration) forms are distributed to the passengers. 4 | 5 | The form asks a series of 26 yes-or-no questions marked `a` through `z`. All you need to do is identify the questions for which **anyone in your group** answers "yes". Since your group is just you, this doesn't take very long. 6 | 7 | However, the person sitting next to you seems to be experiencing a language barrier and asks if you can help. For each of the people in their group, you write down the questions for which they answer "yes", one per line. For example: 8 | 9 | ``` 10 | abcx 11 | abcy 12 | abcz 13 | ``` 14 | 15 | In this group, there are **`6`** questions to which anyone answered "yes": `a`, `b`, `c`, `x`, `y`, and `z`. (Duplicate answers to the same question don't count extra; each question counts at most once.) 16 | 17 | Another group asks for your help, then another, and eventually you've collected answers from every group on the plane (your puzzle input). Each group's answers are separated by a blank line, and within each group, each person's answers are on a single line. For example: 18 | 19 | ``` 20 | abc 21 | 22 | a 23 | b 24 | c 25 | 26 | ab 27 | ac 28 | 29 | a 30 | a 31 | a 32 | a 33 | 34 | b 35 | ``` 36 | 37 | This list represents answers from five groups: 38 | 39 | - The first group contains one person who answered "yes" to **`3`** questions: `a`, `b`, and `c`. 40 | - The second group contains three people; combined, they answered "yes" to **`3`** questions: `a`, `b`, and `c`. 41 | - The third group contains two people; combined, they answered "yes" to **`3`** questions: `a`, `b`, and `c`. 42 | - The fourth group contains four people; combined, they answered "yes" to only **`1`** question, `a`. 43 | - The last group contains one person who answered "yes" to only **`1`** question, `b`. 44 | 45 | In this example, the sum of these counts is `3 + 3 + 3 + 1 + 1` = **`11`**. 46 | 47 | For each group, count the number of questions to which anyone answered "yes". **What is the sum of those counts?** 48 | 49 | ## Part Two 50 | 51 | As you finish the last group's customs declaration, you notice that you misread one word in the instructions: 52 | 53 | You don't need to identify the questions to which **anyone** answered "yes"; you need to identify the questions to which **everyone** answered "yes"! 54 | 55 | Using the same example as above: 56 | 57 | ``` 58 | abc 59 | 60 | a 61 | b 62 | c 63 | 64 | ab 65 | ac 66 | 67 | a 68 | a 69 | a 70 | a 71 | 72 | b 73 | ``` 74 | 75 | This list represents answers from five groups: 76 | 77 | - In the first group, everyone (all 1 person) answered "yes" to **`3`** questions: `a`, `b`, and `c`. 78 | - In the second group, there is **no** question to which everyone answered "yes". 79 | - In the third group, everyone answered yes to only **`1`** question, `a`. Since some people did not answer "yes" to `b` or `c`, they don't count. 80 | - In the fourth group, everyone answered yes to only **`1`** question, `a`. 81 | - In the fifth group, everyone (all 1 person) answered "yes" to **`1`** question, `b`. 82 | 83 | In this example, the sum of these counts is `3 + 0 + 1 + 1 + 1` = **`6`**. 84 | 85 | For each group, count the number of questions to which **everyone** answered "yes". **What is the sum of those counts?** 86 | 87 | ## References 88 | - https://adventofcode.com/2020/day/6 89 | -------------------------------------------------------------------------------- /day-06-custom-customs/customs.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | return input.split(/\n{2,}/g).reduce((count, group) => { 3 | const answers = group.split('\n').map((answer) => answer.trim()); 4 | 5 | return count + new Set(answers.join('').split('')).size; 6 | }, 0); 7 | }; 8 | -------------------------------------------------------------------------------- /day-06-custom-customs/customs2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | return input.split(/\n{2,}/g).reduce((count, group) => { 3 | const answers = group.split('\n').map((answer) => answer.trim().split('')); 4 | 5 | return count + [...new Set(answers.flat())].reduce((sum, question) => { 6 | return answers.every((answer) => answer.includes(question)) ? ++sum : sum; 7 | }, 0); 8 | }, 0); 9 | }; 10 | -------------------------------------------------------------------------------- /day-06-custom-customs/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const boardingPass = require('./customs'); 4 | const boardingPass2 = require('./customs2'); 5 | 6 | describe('Day 6: Custom Customs', () => { 7 | it('should sum counts of anyone answering yes', () => { 8 | const answers = 9 | `abc 10 | 11 | a 12 | b 13 | c 14 | 15 | ab 16 | ac 17 | 18 | a 19 | a 20 | a 21 | a 22 | 23 | b`; 24 | assert.strictEqual(boardingPass(answers), 11); 25 | }); 26 | 27 | describe('Part Two', () => { 28 | it('should sum counts of everyone answering yes', () => { 29 | const answers = 30 | `abc 31 | 32 | a 33 | b 34 | c 35 | 36 | ab 37 | ac 38 | 39 | a 40 | a 41 | a 42 | a 43 | 44 | b`; 45 | assert.strictEqual(boardingPass2(answers), 6); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /day-07-handy-haversacks/README.md: -------------------------------------------------------------------------------- 1 | # Day 7: Handy Haversacks 2 | 3 | You land at the regional airport in time for your next flight. In fact, it looks like you'll even have time to grab some food: all flights are currently delayed due to **issues in luggage processing**. 4 | 5 | Due to recent aviation regulations, many rules (your puzzle input) are being enforced about bags and their contents; bags must be color-coded and must contain specific quantities of other color-coded bags. Apparently, nobody responsible for these regulations considered how long they would take to enforce! 6 | 7 | For example, consider the following rules: 8 | 9 | ``` 10 | light red bags contain 1 bright white bag, 2 muted yellow bags. 11 | dark orange bags contain 3 bright white bags, 4 muted yellow bags. 12 | bright white bags contain 1 shiny gold bag. 13 | muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. 14 | shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. 15 | dark olive bags contain 3 faded blue bags, 4 dotted black bags. 16 | vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. 17 | faded blue bags contain no other bags. 18 | dotted black bags contain no other bags. 19 | ``` 20 | 21 | These rules specify the required contents for 9 bag types. In this example, every `faded blue` bag is empty, every `vibrant plum` bag contains 11 bags (5 `faded blue` and 6 `dotted black`), and so on. 22 | 23 | You have a **`shiny gold`** bag. If you wanted to carry it in at least one other bag, how many different bag colors would be valid for the outermost bag? (In other words: how many colors can, eventually, contain at least one `shiny gold` bag?) 24 | 25 | In the above rules, the following options would be available to you: 26 | 27 | - A `bright white` bag, which can hold your `shiny gold` bag directly. 28 | - A `muted yellow` bag, which can hold your `shiny gold` bag directly, plus some other bags. 29 | - A `dark orange` bag, which can hold `bright white` and `muted yellow` bags, either of which could then hold your - `shiny gold` bag. 30 | - A `light red` bag, which can hold `bright white` and `muted yellow` bags, either of which could then hold your `shiny gold` bag. 31 | 32 | So, in this example, the number of bag colors that can eventually contain at least one `shiny gold` bag is **`4`**. 33 | 34 | **How many bag colors can eventually contain at least one `shiny gold` bag?** (The list of rules is quite long; make sure you get all of it.) 35 | 36 | ## Part Two 37 | 38 | It's getting pretty expensive to fly these days - not because of ticket prices, but because of the ridiculous number of bags you need to buy! 39 | 40 | Consider again your `shiny gold` bag and the rules from the above example: 41 | 42 | - `faded blue` bags contain `0` other bags. 43 | - `dotted black` bags contain `0` other bags. 44 | - `vibrant plum` bags contain `11` other bags: 5 `faded blue` bags and 6 `dotted black` bags. 45 | - `dark olive` bags contain `7` other bags: 3 `faded blue` bags and 4 `dotted black` bags. 46 | 47 | So, a single `shiny gold` bag must contain 1 `dark olive` bag (and the 7 bags within it) plus 2 `vibrant plum` bags (and the 11 bags within **each** of those): `1 + 1*7 + 2 + 2*11` = **`32`** bags! 48 | 49 | Of course, the actual rules have a small chance of going several levels deeper than this example; be sure to count all of the bags, even if the nesting becomes topologically impractical! 50 | 51 | Here's another example: 52 | 53 | ``` 54 | shiny gold bags contain 2 dark red bags. 55 | dark red bags contain 2 dark orange bags. 56 | dark orange bags contain 2 dark yellow bags. 57 | dark yellow bags contain 2 dark green bags. 58 | dark green bags contain 2 dark blue bags. 59 | dark blue bags contain 2 dark violet bags. 60 | dark violet bags contain no other bags. 61 | ``` 62 | 63 | In this example, a single `shiny gold` bag must contain **`126`** other bags. 64 | 65 | How many individual bags are required inside your single `shiny gold` bag? 66 | 67 | ## References 68 | - https://adventofcode.com/2020/day/7 69 | -------------------------------------------------------------------------------- /day-07-handy-haversacks/haversacks.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const rules = input.split('\n').reduce((rules, line) => { 3 | const [, color, otherColors] = /(\w+ \w+) bags contain (.*)\./.exec(line); 4 | 5 | const compatibleWith = otherColors !== 'no other bags' 6 | ? otherColors.split(', ').map((other) => /(\w+ \w+) bags?/.exec(other)[1]) 7 | : []; 8 | 9 | rules[color] = new Set(); 10 | 11 | compatibleWith.forEach((otherColor) => rules[color].add(otherColor)); 12 | 13 | return rules; 14 | }, {}); 15 | 16 | const expand = (bag) => { 17 | const colors = [...rules[bag].values()]; 18 | 19 | for (const color of rules[bag].values()) { 20 | colors.push(...expand(color)); 21 | } 22 | 23 | return colors; 24 | }; 25 | 26 | return Object 27 | .keys(rules) 28 | .filter((key) => expand(key).includes('shiny gold')) 29 | .length; 30 | }; 31 | -------------------------------------------------------------------------------- /day-07-handy-haversacks/haversacks2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const rules = input.split('\n').reduce((rules, line) => { 3 | const [, color, otherColors] = /(\w+ \w+) bags contain (.*)\./.exec(line); 4 | 5 | const compatibleWith = otherColors !== 'no other bags' 6 | ? otherColors.split(', ').map((other) => { 7 | const [, units, color] = /(\d+) (\w+ \w+) bags?/.exec(other); 8 | 9 | return { units: parseInt(units), color }; 10 | }) 11 | : []; 12 | 13 | rules.set(color, []); 14 | 15 | compatibleWith.forEach((otherColor) => rules.get(color).push(otherColor)); 16 | 17 | return rules; 18 | }, new Map()); 19 | 20 | const traverse = (bag) => { 21 | let total = 0; 22 | 23 | for (const { color, units } of rules.get(bag)) { 24 | total += units + units * traverse(color); 25 | } 26 | 27 | return total; 28 | }; 29 | 30 | return traverse('shiny gold'); 31 | }; 32 | -------------------------------------------------------------------------------- /day-07-handy-haversacks/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const haversacks = require('./haversacks'); 4 | const haversacks2 = require('./haversacks2'); 5 | 6 | describe('Day 7: Handy Haversacks', () => { 7 | it('should determine how many bags can contain one "shiny gold" bag', () => { 8 | const rules = 9 | `light red bags contain 1 bright white bag, 2 muted yellow bags. 10 | dark orange bags contain 3 bright white bags, 4 muted yellow bags. 11 | bright white bags contain 1 shiny gold bag. 12 | muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. 13 | shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. 14 | dark olive bags contain 3 faded blue bags, 4 dotted black bags. 15 | vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. 16 | faded blue bags contain no other bags. 17 | dotted black bags contain no other bags.`; 18 | 19 | assert.strictEqual(haversacks(rules), 4); 20 | }); 21 | 22 | describe('Part Two', () => { 23 | it('should find individual bags required inside "shiny gold" bag (#1)', () => { 24 | const rules = 25 | `light red bags contain 1 bright white bag, 2 muted yellow bags. 26 | dark orange bags contain 3 bright white bags, 4 muted yellow bags. 27 | bright white bags contain 1 shiny gold bag. 28 | muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. 29 | shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. 30 | dark olive bags contain 3 faded blue bags, 4 dotted black bags. 31 | vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. 32 | faded blue bags contain no other bags. 33 | dotted black bags contain no other bags.`; 34 | 35 | assert.strictEqual(haversacks2(rules), 32); 36 | }); 37 | 38 | it('should find individual bags required inside "shiny gold" bag (#2)', () => { 39 | const rules = 40 | `shiny gold bags contain 2 dark red bags. 41 | dark red bags contain 2 dark orange bags. 42 | dark orange bags contain 2 dark yellow bags. 43 | dark yellow bags contain 2 dark green bags. 44 | dark green bags contain 2 dark blue bags. 45 | dark blue bags contain 2 dark violet bags. 46 | dark violet bags contain no other bags.`; 47 | 48 | assert.strictEqual(haversacks2(rules), 126); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /day-08-handheld-halting/README.md: -------------------------------------------------------------------------------- 1 | # Day 8: Handheld Halting 2 | 3 | Your flight to the major airline hub reaches cruising altitude without incident. While you consider checking the in-flight menu for one of those drinks that come with a little umbrella, you are interrupted by the kid sitting next to you. 4 | 5 | Their [handheld game console](https://en.wikipedia.org/wiki/Handheld_game_console) won't turn on! They ask if you can take a look. 6 | 7 | You narrow the problem down to a strange **infinite loop** in the boot code (your puzzle input) of the device. You should be able to fix it, but first you need to be able to run the code in isolation. 8 | 9 | The boot code is represented as a text file with one **instruction** per line of text. Each instruction consists of an **operation** (`acc`, `jmp`, or `nop`) and an **argument** (a signed number like `+4` or `-20`). 10 | 11 | - `acc` increases or decreases a single global value called the **accumulator** by the value given in the argument. For example, `acc +7` would increase the accumulator by 7. The accumulator starts at `0`. After an `acc` instruction, the instruction immediately below it is executed next. 12 | - `jmp` **jumps** to a new instruction relative to itself. The next instruction to execute is found using the argument as an **offset** from the `jmp` instruction; for example, `jmp +2` would skip the next instruction, `jmp +1` would continue to the instruction immediately below it, and `jmp -20` would cause the instruction 20 lines above to be executed next. 13 | - `nop` stands for **No OPeration** - it does nothing. The instruction immediately below it is executed next. 14 | 15 | For example, consider the following program: 16 | 17 | ``` 18 | nop +0 19 | acc +1 20 | jmp +4 21 | acc +3 22 | jmp -3 23 | acc -99 24 | acc +1 25 | jmp -4 26 | acc +6 27 | ``` 28 | 29 | These instructions are visited in this order: 30 | 31 | ``` 32 | nop +0 | 1 33 | acc +1 | 2, 8(!) 34 | jmp +4 | 3 35 | acc +3 | 6 36 | jmp -3 | 7 37 | acc -99 | 38 | acc +1 | 4 39 | jmp -4 | 5 40 | acc +6 | 41 | ``` 42 | 43 | First, the `nop +0` does nothing. Then, the accumulator is increased from 0 to 1 (`acc +1`) and `jmp +4` sets the next instruction to the other `acc +1` near the bottom. After it increases the accumulator from 1 to 2, `jmp -4` executes, setting the next instruction to the only `acc +3`. It sets the accumulator to 5, and `jmp -3` causes the program to continue back at the first `acc +1`. 44 | 45 | This is an **infinite loop**: with this sequence of jumps, the program will run forever. The moment the program tries to run any instruction a second time, you know it will never terminate. 46 | 47 | Immediately **before** the program would run an instruction a second time, the value in the accumulator is **`5`**. 48 | 49 | Run your copy of the boot code. Immediately before any instruction is executed a second time, **what value is in the accumulator?** 50 | 51 | ## Part Two 52 | 53 | After some careful analysis, you believe that **exactly one instruction is corrupted**. 54 | 55 | Somewhere in the program, **either** a `jmp` is supposed to be a `nop`, **or** a `nop` is supposed to be a `jmp`. (No `acc` instructions were harmed in the corruption of this boot code.) 56 | 57 | The program is supposed to terminate by **attempting to execute an instruction immediately after the last instruction in the file**. By changing exactly one `jmp` or `nop`, you can repair the boot code and make it terminate correctly. 58 | 59 | For example, consider the same program from above: 60 | 61 | ``` 62 | nop +0 63 | acc +1 64 | jmp +4 65 | acc +3 66 | jmp -3 67 | acc -99 68 | acc +1 69 | jmp -4 70 | acc +6 71 | ``` 72 | 73 | If you change the first instruction from `nop +0` to `jmp +0`, it would create a single-instruction infinite loop, never leaving that instruction. If you change almost any of the `jmp` instructions, the program will still eventually find another `jmp` instruction and loop forever. 74 | 75 | However, if you change the second-to-last instruction (from `jmp -4` to `nop -4`), the program terminates! The instructions are visited in this order: 76 | 77 | ``` 78 | nop +0 | 1 79 | acc +1 | 2 80 | jmp +4 | 3 81 | acc +3 | 82 | jmp -3 | 83 | acc -99 | 84 | acc +1 | 4 85 | nop -4 | 5 86 | acc +6 | 6 87 | ``` 88 | 89 | After the last instruction (`acc +6`), the program terminates by attempting to run the instruction below the last instruction in the file. With this change, after the program terminates, the accumulator contains the value **`8`** (`acc +1`, `acc +1`, `acc +6`). 90 | 91 | Fix the program so that it terminates normally by changing exactly one `jmp` (to `nop`) or `nop` (to `jmp`). **What is the value of the accumulator after the program terminates?** 92 | 93 | ## References 94 | - https://adventofcode.com/2020/day/8 95 | -------------------------------------------------------------------------------- /day-08-handheld-halting/handheld.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const instructions = input.split('\n').map((line) => { 3 | const [, operation, value] = /(\w+) ([+-]\d+)/.exec(line.trim()); 4 | 5 | return [operation, parseInt(value)]; 6 | }); 7 | 8 | let acc = 0; 9 | let i = 0; 10 | 11 | const executed = new Set(); 12 | 13 | while (!executed.has(i)) { 14 | executed.add(i); 15 | 16 | const [operation, argument] = instructions[i]; 17 | 18 | switch (operation) { 19 | case 'acc': 20 | acc += argument; 21 | i++; 22 | 23 | break; 24 | case 'jmp': 25 | i += argument; 26 | 27 | break; 28 | case 'nop': 29 | i++; 30 | 31 | break; 32 | } 33 | } 34 | 35 | return acc; 36 | }; 37 | -------------------------------------------------------------------------------- /day-08-handheld-halting/handheld2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const instructions = input.split('\n').map((line) => { 3 | const [, operation, value] = /(\w+) ([+-]\d+)/.exec(line.trim()); 4 | 5 | return [operation, parseInt(value)]; 6 | }); 7 | 8 | for (let i = 0; i < instructions.length; i++) { 9 | const patched = instructions.map(([operation, value]) => [operation, value]); 10 | 11 | if (instructions[i][0] === 'jmp') { 12 | patched[i][0] = 'nop'; 13 | } else if (instructions[i][0] === 'nop') { 14 | patched[i][0] = 'jmp'; 15 | } else { 16 | continue; 17 | } 18 | 19 | const executed = new Set(); 20 | 21 | let acc = 0; 22 | let p = 0; 23 | 24 | while (!executed.has(p)) { 25 | executed.add(p); 26 | 27 | const [operation, argument] = patched[p]; 28 | 29 | switch (operation) { 30 | case 'acc': 31 | acc += argument; 32 | p++; 33 | 34 | break; 35 | case 'jmp': 36 | p += argument; 37 | 38 | break; 39 | case 'nop': 40 | p++; 41 | 42 | break; 43 | } 44 | 45 | if (p >= patched.length) { 46 | return acc; 47 | } 48 | } 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /day-08-handheld-halting/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const handheld = require('./handheld'); 4 | const handheld2 = require('./handheld2'); 5 | 6 | describe('Day 8: Handheld Halting', () => { 7 | it('should find accumulator value', () => { 8 | const instructions = 9 | `nop +0 10 | acc +1 11 | jmp +4 12 | acc +3 13 | jmp -3 14 | acc -99 15 | acc +1 16 | jmp -4 17 | acc +6`; 18 | 19 | assert.strictEqual(handheld(instructions), 5); 20 | }); 21 | 22 | describe('Part Two', () => { 23 | it('should find accumulator value of patched boot code', () => { 24 | const instructions = 25 | `nop +0 26 | acc +1 27 | jmp +4 28 | acc +3 29 | jmp -3 30 | acc -99 31 | acc +1 32 | jmp -4 33 | acc +6`; 34 | 35 | assert.strictEqual(handheld2(instructions), 8); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /day-09-encoding-error/README.md: -------------------------------------------------------------------------------- 1 | # Day 9: Encoding Error 2 | 3 | With your neighbor happily enjoying their video game, you turn your attention to an open data port on the little screen in the seat in front of you. 4 | 5 | Though the port is non-standard, you manage to connect it to your computer through the clever use of several paperclips. Upon connection, the port outputs a series of numbers (your puzzle input). 6 | 7 | The data appears to be encrypted with the eXchange-Masking Addition System (XMAS) which, conveniently for you, is an old cypher with an important weakness. 8 | 9 | XMAS starts by transmitting a **preamble** of 25 numbers. After that, each number you receive should be the sum of any two of the 25 immediately previous numbers. The two numbers will have different values, and there might be more than one such pair. 10 | 11 | For example, suppose your preamble consists of the numbers `1` through `25` in a random order. To be valid, the next number must be the sum of two of those numbers: 12 | 13 | - `26` would be a **valid** next number, as it could be `1` plus `25` (or many other pairs, like `2` and `24`). 14 | - `49` would be a **valid** next number, as it is the sum of `24` and `25`. 15 | - `100` would **not** be valid; no two of the previous 25 numbers sum to `100`. 16 | - `50` would also **not** be valid; although `25` appears in the previous 25 numbers, the two numbers in the pair must be different. 17 | 18 | Suppose the 26th number is `45`, and the first number (no longer an option, as it is more than 25 numbers ago) was `20`. Now, for the next number to be valid, there needs to be some pair of numbers among `1`-`19`, `21`-`25`, or `45` that add up to it: 19 | 20 | - `26` would still be a **valid** next number, as `1` and `25` are still within the previous 25 numbers. 21 | - `65` would **not** be valid, as no two of the available numbers sum to it. 22 | - `64` and `66` would both be **valid**, as they are the result of `19+45` and `21+45` respectively. 23 | 24 | Here is a larger example which only considers the previous **5** numbers (and has a preamble of length 5): 25 | 26 | ``` 27 | 35 28 | 20 29 | 15 30 | 25 31 | 47 32 | 40 33 | 62 34 | 55 35 | 65 36 | 95 37 | 102 38 | 117 39 | 150 40 | 182 41 | 127 42 | 219 43 | 299 44 | 277 45 | 309 46 | 576 47 | ``` 48 | 49 | In this example, after the 5-number preamble, almost every number is the sum of two of the previous 5 numbers; the only number that does not follow this rule is **`127`**. 50 | 51 | The first step of attacking the weakness in the XMAS data is to find the first number in the list (after the preamble) which is **not** the sum of two of the 25 numbers before it. **What is the first number that does not have this property?** 52 | 53 | ## Part Two 54 | 55 | The final step in breaking the XMAS encryption relies on the invalid number you just found: you must **find a contiguous set of at least two numbers** in your list which sum to the invalid number from step 1. 56 | 57 | Again consider the above example: 58 | 59 | ``` 60 | 35 61 | 20 62 | 15 63 | 25 64 | 47 65 | 40 66 | 62 67 | 55 68 | 65 69 | 95 70 | 102 71 | 117 72 | 150 73 | 182 74 | 127 75 | 219 76 | 299 77 | 277 78 | 309 79 | 576 80 | ``` 81 | 82 | In this list, adding up all of the numbers from `15` through `40` produces the invalid number from step 1, `127`. (Of course, the contiguous set of numbers in your actual list might be much longer.) 83 | 84 | To find the **encryption weakness**, add together the `smallest` and `largest` number in this contiguous range; in this example, these are `15` and `47`, producing **`62`**. 85 | 86 | **What is the encryption weakness in your XMAS-encrypted list of numbers?** 87 | 88 | ## References 89 | - https://adventofcode.com/2020/day/9 90 | -------------------------------------------------------------------------------- /day-09-encoding-error/encoding.js: -------------------------------------------------------------------------------- 1 | module.exports = (input, preamble = 25) => { 2 | const numbers = input.split('\n').map((line) => parseInt(line)); 3 | 4 | const contains = (sum, range) => { 5 | return [...range.values()].some((number) => range.has(sum - number)); 6 | }; 7 | 8 | for (let i = preamble; i < numbers.length; i++) { 9 | if (!contains(numbers[i], new Set(numbers.slice(i - preamble, i)))) { 10 | return numbers[i]; 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /day-09-encoding-error/encoding2.js: -------------------------------------------------------------------------------- 1 | const contiguous = (numbers, sum) => { 2 | for (let i = 0; i < numbers.length; i++) { 3 | let total = numbers[i]; 4 | 5 | for (let j = i + 1; j < numbers.length; j++) { 6 | const subTotal = total + numbers[j]; 7 | 8 | if (subTotal > sum) { 9 | break; 10 | } else if (subTotal === sum) { 11 | const range = numbers.slice(i, j); 12 | 13 | return Math.min(...range) + Math.max(...range); 14 | } else { 15 | total += numbers[j]; 16 | } 17 | } 18 | } 19 | }; 20 | 21 | module.exports = (input, preamble = 25) => { 22 | const numbers = input.split('\n').map((line) => parseInt(line)); 23 | 24 | const contains = (sum, range) => { 25 | return [...range.values()].some((number) => range.has(sum - number)); 26 | }; 27 | 28 | for (let i = preamble; i < numbers.length; i++) { 29 | if (!contains(numbers[i], new Set(numbers.slice(i - preamble, i)))) { 30 | return contiguous(numbers, numbers[i]); 31 | } 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /day-09-encoding-error/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const encoding = require('./encoding'); 4 | const encoding2 = require('./encoding2'); 5 | 6 | describe('Day 9: Encoding Error', () => { 7 | it('should find first number not summing to previous 5', () => { 8 | const input = 9 | `35 10 | 20 11 | 15 12 | 25 13 | 47 14 | 40 15 | 62 16 | 55 17 | 65 18 | 95 19 | 102 20 | 117 21 | 150 22 | 182 23 | 127 24 | 219 25 | 299 26 | 277 27 | 309 28 | 576`; 29 | 30 | assert.strictEqual(encoding(input, 5), 127); 31 | }); 32 | 33 | describe('Part Two', () => { 34 | it('should find encryption weakness', () => { 35 | const input = 36 | `35 37 | 20 38 | 15 39 | 25 40 | 47 41 | 40 42 | 62 43 | 55 44 | 65 45 | 95 46 | 102 47 | 117 48 | 150 49 | 182 50 | 127 51 | 219 52 | 299 53 | 277 54 | 309 55 | 576`; 56 | 57 | assert.strictEqual(encoding2(input, 5), 62); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /day-10-adapter-array/README.md: -------------------------------------------------------------------------------- 1 | # Day 10: Adapter Array 2 | 3 | Patched into the aircraft's data port, you discover weather forecasts of a massive tropical storm. Before you can figure out whether it will impact your vacation plans, however, your device suddenly turns off! 4 | 5 | Its battery is dead. 6 | 7 | You'll need to plug it in. There's only one problem: the charging outlet near your seat produces the wrong number of **jolts**. Always prepared, you make a list of all of the joltage adapters in your bag. 8 | 9 | Each of your joltage adapters is rated for a specific **output joltage** (your puzzle input). Any given adapter can take an input `1`, `2`, or `3` jolts **lower** than its rating and still produce its rated output joltage. 10 | 11 | In addition, your device has a built-in joltage adapter rated for **`3` jolts higher** than the highest-rated adapter in your bag. (If your adapter list were `3`, `9`, and `6`, your device's built-in adapter would be rated for `12` jolts.) 12 | 13 | Treat the charging outlet near your seat as having an effective joltage rating of `0`. 14 | 15 | Since you have some time to kill, you might as well test all of your adapters. Wouldn't want to get to your resort and realize you can't even charge your device! 16 | 17 | If you **use every adapter in your bag** at once, what is the distribution of joltage differences between the charging outlet, the adapters, and your device? 18 | 19 | For example, suppose that in your bag, you have adapters with the following joltage ratings: 20 | 21 | ``` 22 | 16 23 | 10 24 | 15 25 | 5 26 | 1 27 | 11 28 | 7 29 | 19 30 | 6 31 | 12 32 | 4 33 | ``` 34 | 35 | With these adapters, your device's built-in joltage adapter would be rated for `19 + 3 = 22` jolts, 3 higher than the highest-rated adapter. 36 | 37 | Because adapters can only connect to a source 1-3 jolts lower than its rating, in order to use every adapter, you'd need to choose them like this: 38 | 39 | - The charging outlet has an effective rating of `0` jolts, so the only adapters that could connect to it directly would need to have a joltage rating of `1`, `2`, or `3` jolts. Of these, only one you have is an adapter rated `1` jolt (difference of **`1`**). 40 | - From your `1`-jolt rated adapter, the only choice is your `4`-jolt rated adapter (difference of **`3`**). 41 | - From the `4`-jolt rated adapter, the adapters rated `5`, `6`, or `7` are valid choices. However, in order to not skip any adapters, you have to pick the adapter rated `5` jolts (difference of **`1`**). 42 | - Similarly, the next choices would need to be the adapter rated `6` and then the adapter rated `7` (with difference of **`1`** and **`1`**). 43 | - The only adapter that works with the `7`-jolt rated adapter is the one rated `10` jolts (difference of **`3`**). 44 | - From `10`, the choices are `11` or `12`; choose `11` (difference of **`1`**) and then `12` (difference of **`1`**). 45 | - After `12`, only valid adapter has a rating of `15` (difference of **`3`**), then `16` (difference of **`1`**), then `19` (difference of **`3`**). 46 | - Finally, your device's built-in adapter is always 3 higher than the highest adapter, so its rating is `22` jolts (always a difference of **`3`**). 47 | 48 | In this example, when using every adapter, there are **`7`** differences of 1 jolt and **`5`** differences of 3 jolts. 49 | 50 | Here is a larger example: 51 | 52 | ``` 53 | 28 54 | 33 55 | 18 56 | 42 57 | 31 58 | 14 59 | 46 60 | 20 61 | 48 62 | 47 63 | 24 64 | 23 65 | 49 66 | 45 67 | 19 68 | 38 69 | 39 70 | 11 71 | 1 72 | 32 73 | 25 74 | 35 75 | 8 76 | 17 77 | 7 78 | 9 79 | 4 80 | 2 81 | 34 82 | 10 83 | 3 84 | ``` 85 | 86 | In this larger example, in a chain that uses all of the adapters, there are **`22`** differences of 1 jolt and **`10`** differences of 3 jolts. 87 | 88 | Find a chain that uses all of your adapters to connect the charging outlet to your device's built-in adapter and count the joltage differences between the charging outlet, the adapters, and your device. **What is the number of 1-jolt differences multiplied by the number of 3-jolt differences?** 89 | 90 | ## Part Two 91 | 92 | To completely determine whether you have enough adapters, you'll need to figure out how many different ways they can be arranged. Every arrangement needs to connect the charging outlet to your device. The previous rules about when adapters can successfully connect still apply. 93 | 94 | The first example above (the one that starts with `16`, `10`, `15`) supports the following arrangements: 95 | 96 | ``` 97 | (0), 1, 4, 5, 6, 7, 10, 11, 12, 15, 16, 19, (22) 98 | (0), 1, 4, 5, 6, 7, 10, 12, 15, 16, 19, (22) 99 | (0), 1, 4, 5, 7, 10, 11, 12, 15, 16, 19, (22) 100 | (0), 1, 4, 5, 7, 10, 12, 15, 16, 19, (22) 101 | (0), 1, 4, 6, 7, 10, 11, 12, 15, 16, 19, (22) 102 | (0), 1, 4, 6, 7, 10, 12, 15, 16, 19, (22) 103 | (0), 1, 4, 7, 10, 11, 12, 15, 16, 19, (22) 104 | (0), 1, 4, 7, 10, 12, 15, 16, 19, (22) 105 | ``` 106 | 107 | (The charging outlet and your device's built-in adapter are shown in parentheses.) Given the adapters from the first example, the total number of arrangements that connect the charging outlet to your device is **`8`**. 108 | 109 | The second example above (the one that starts with `28`, `33`, `18`) has many arrangements. Here are a few: 110 | 111 | ``` 112 | (0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, 113 | 32, 33, 34, 35, 38, 39, 42, 45, 46, 47, 48, 49, (52) 114 | 115 | (0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, 116 | 32, 33, 34, 35, 38, 39, 42, 45, 46, 47, 49, (52) 117 | 118 | (0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, 119 | 32, 33, 34, 35, 38, 39, 42, 45, 46, 48, 49, (52) 120 | 121 | (0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, 122 | 32, 33, 34, 35, 38, 39, 42, 45, 46, 49, (52) 123 | 124 | (0), 1, 2, 3, 4, 7, 8, 9, 10, 11, 14, 17, 18, 19, 20, 23, 24, 25, 28, 31, 125 | 32, 33, 34, 35, 38, 39, 42, 45, 47, 48, 49, (52) 126 | 127 | (0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, 128 | 46, 48, 49, (52) 129 | 130 | (0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, 131 | 46, 49, (52) 132 | 133 | (0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, 134 | 47, 48, 49, (52) 135 | 136 | (0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, 137 | 47, 49, (52) 138 | 139 | (0), 3, 4, 7, 10, 11, 14, 17, 20, 23, 25, 28, 31, 34, 35, 38, 39, 42, 45, 140 | 48, 49, (52) 141 | ``` 142 | 143 | In total, this set of adapters can connect the charging outlet to your device in **`19208`** distinct arrangements. 144 | 145 | You glance back down at your bag and try to remember why you brought so many adapters; there must be **more than a trillion** valid ways to arrange them! Surely, there must be an efficient way to count the arrangements. 146 | 147 | **What is the total number of distinct ways you can arrange the adapters to connect the charging outlet to your device?** 148 | 149 | ## References 150 | - https://adventofcode.com/2020/day/10 151 | -------------------------------------------------------------------------------- /day-10-adapter-array/adapter.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const adapters = [0, ...input 3 | .split('\n') 4 | .map((x) => parseInt(x)) 5 | .sort((a, b) => a - b)]; 6 | 7 | let ones = 0; 8 | let threes = 1; 9 | 10 | for (let i = 0; i < adapters.length - 1; i++) { 11 | const difference = adapters[i + 1] - adapters[i]; 12 | 13 | difference === 1 ? ones++ : difference === 3 ? threes++ : 0; 14 | } 15 | 16 | return ones * threes; 17 | }; 18 | -------------------------------------------------------------------------------- /day-10-adapter-array/adapter2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const adapters = [0, ...input 3 | .split('\n') 4 | .map((x) => parseInt(x)) 5 | .sort((a, b) => a - b)]; 6 | 7 | const ratings = adapters.map((rating) => { 8 | return adapters.filter((otherRating) => { 9 | return rating > otherRating && rating - 3 <= otherRating; 10 | }).length; 11 | }); 12 | 13 | for (let i = 0; i < ratings.length; i++) { 14 | const previous = ratings.slice(i - ratings[i], i); 15 | 16 | ratings[i] = previous.length ? previous.reduce((a, b) => a + b, 0) : 1; 17 | } 18 | 19 | return ratings.slice(-1)[0]; 20 | }; 21 | -------------------------------------------------------------------------------- /day-10-adapter-array/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const adapter = require('./adapter'); 4 | const adapter2 = require('./adapter2'); 5 | 6 | describe('Day 10: Adapter Array', () => { 7 | it('should multiply 1-jolt and 3-jolt differences (#1)', () => { 8 | const input = 9 | `16 10 | 10 11 | 15 12 | 5 13 | 1 14 | 11 15 | 7 16 | 19 17 | 6 18 | 12 19 | 4`; 20 | 21 | assert.strictEqual(adapter(input), 35); 22 | }); 23 | 24 | it('should multiply 1-jolt and 3-jolt differences (#2)', () => { 25 | const input = 26 | `28 27 | 33 28 | 18 29 | 42 30 | 31 31 | 14 32 | 46 33 | 20 34 | 48 35 | 47 36 | 24 37 | 23 38 | 49 39 | 45 40 | 19 41 | 38 42 | 39 43 | 11 44 | 1 45 | 32 46 | 25 47 | 35 48 | 8 49 | 17 50 | 7 51 | 9 52 | 4 53 | 2 54 | 34 55 | 10 56 | 3`; 57 | 58 | assert.strictEqual(adapter(input), 220); 59 | }); 60 | 61 | describe('Part Two', () => { 62 | it('should determine distinct adapter configurations (#1)', () => { 63 | const input = 64 | `16 65 | 10 66 | 15 67 | 5 68 | 1 69 | 11 70 | 7 71 | 19 72 | 6 73 | 12 74 | 4`; 75 | 76 | assert.strictEqual(adapter2(input), 8); 77 | }); 78 | 79 | it('should determine distinct adapter configurations (#2)', () => { 80 | const input = 81 | `28 82 | 33 83 | 18 84 | 42 85 | 31 86 | 14 87 | 46 88 | 20 89 | 48 90 | 47 91 | 24 92 | 23 93 | 49 94 | 45 95 | 19 96 | 38 97 | 39 98 | 11 99 | 1 100 | 32 101 | 25 102 | 35 103 | 8 104 | 17 105 | 7 106 | 9 107 | 4 108 | 2 109 | 34 110 | 10 111 | 3`; 112 | 113 | assert.strictEqual(adapter2(input), 19208); 114 | }); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /day-11-seating-system/README.md: -------------------------------------------------------------------------------- 1 | # Day 11: Seating System 2 | 3 | Your plane lands with plenty of time to spare. The final leg of your journey is a ferry that goes directly to the tropical island where you can finally start your vacation. As you reach the waiting area to board the ferry, you realize you're so early, nobody else has even arrived yet! 4 | 5 | By modeling the process people use to choose (or abandon) their seat in the waiting area, you're pretty sure you can predict the best place to sit. You make a quick map of the seat layout (your puzzle input). 6 | 7 | The seat layout fits neatly on a grid. Each position is either floor (`.`), an empty seat (`L`), or an occupied seat (`#`). For example, the initial seat layout might look like this: 8 | 9 | ``` 10 | L.LL.LL.LL 11 | LLLLLLL.LL 12 | L.L.L..L.. 13 | LLLL.LL.LL 14 | L.LL.LL.LL 15 | L.LLLLL.LL 16 | ..L.L..... 17 | LLLLLLLLLL 18 | L.LLLLLL.L 19 | L.LLLLL.LL 20 | ``` 21 | 22 | Now, you just need to model the people who will be arriving shortly. Fortunately, people are entirely predictable and always follow a simple set of rules. All decisions are based on the **number of occupied seats** adjacent to a given seat (one of the eight positions immediately up, down, left, right, or diagonal from the seat). The following rules are applied to every seat simultaneously: 23 | 24 | - If a seat is **empty** (`L`) and there are **no** occupied seats adjacent to it, the seat becomes **occupied**. 25 | - If a seat is **occupied** (`#`) and **four or more** seats adjacent to it are also occupied, the seat becomes **empty**. 26 | - Otherwise, the seat's state does not change. 27 | 28 | Floor (`.`) never changes; seats don't move, and nobody sits on the floor. 29 | 30 | After one round of these rules, every seat in the example layout becomes occupied: 31 | 32 | ``` 33 | #.##.##.## 34 | #######.## 35 | #.#.#..#.. 36 | ####.##.## 37 | #.##.##.## 38 | #.#####.## 39 | ..#.#..... 40 | ########## 41 | #.######.# 42 | #.#####.## 43 | ``` 44 | 45 | After a second round, the seats with four or more occupied adjacent seats become empty again: 46 | 47 | ``` 48 | #.LL.L#.## 49 | #LLLLLL.L# 50 | L.L.L..L.. 51 | #LLL.LL.L# 52 | #.LL.LL.LL 53 | #.LLLL#.## 54 | ..L.L..... 55 | #LLLLLLLL# 56 | #.LLLLLL.L 57 | #.#LLLL.## 58 | ``` 59 | 60 | This process continues for three more rounds: 61 | 62 | ``` 63 | #.##.L#.## 64 | #L###LL.L# 65 | L.#.#..#.. 66 | #L##.##.L# 67 | #.##.LL.LL 68 | #.###L#.## 69 | ..#.#..... 70 | #L######L# 71 | #.LL###L.L 72 | #.#L###.## 73 | ``` 74 | 75 | ``` 76 | #.#L.L#.## 77 | #LLL#LL.L# 78 | L.L.L..#.. 79 | #LLL.##.L# 80 | #.LL.LL.LL 81 | #.LL#L#.## 82 | ..L.L..... 83 | #L#LLLL#L# 84 | #.LLLLLL.L 85 | #.#L#L#.## 86 | ``` 87 | 88 | ``` 89 | #.#L.L#.## 90 | #LLL#LL.L# 91 | L.#.L..#.. 92 | #L##.##.L# 93 | #.#L.LL.LL 94 | #.#L#L#.## 95 | ..L.L..... 96 | #L#L##L#L# 97 | #.LLLLLL.L 98 | #.#L#L#.## 99 | ``` 100 | 101 | At this point, something interesting happens: the chaos stabilizes and further applications of these rules cause no seats to change state! Once people stop moving around, you count **`37`** occupied seats. 102 | 103 | Simulate your seating area by applying the seating rules repeatedly until no seats change state. **How many seats end up occupied?** 104 | 105 | ## Part Two 106 | 107 | As soon as people start to arrive, you realize your mistake. People don't just care about adjacent seats - they care about **the first seat they can see** in each of those eight directions! 108 | 109 | Now, instead of considering just the eight immediately adjacent seats, consider the **first seat** in each of those eight directions. For example, the empty seat below would see **eight** occupied seats: 110 | 111 | ``` 112 | .......#. 113 | ...#..... 114 | .#....... 115 | ......... 116 | ..#L....# 117 | ....#.... 118 | ......... 119 | #........ 120 | ...#..... 121 | ``` 122 | 123 | The leftmost empty seat below would only see **one** empty seat, but cannot see any of the occupied ones: 124 | 125 | ``` 126 | ............. 127 | .L.L.#.#.#.#. 128 | ............. 129 | ``` 130 | 131 | The empty seat below would see **no** occupied seats: 132 | 133 | ``` 134 | .##.##. 135 | #.#.#.# 136 | ##...## 137 | ...L... 138 | ##...## 139 | #.#.#.# 140 | .##.##. 141 | ``` 142 | 143 | Also, people seem to be more tolerant than you expected: it now takes **five or more** visible occupied seats for an occupied seat to become empty (rather than **four or more** from the previous rules). The other rules still apply: empty seats that see no occupied seats become occupied, seats matching no rule don't change, and floor never changes. 144 | 145 | Given the same starting layout as above, these new rules cause the seating area to shift around as follows: 146 | 147 | ``` 148 | L.LL.LL.LL 149 | LLLLLLL.LL 150 | L.L.L..L.. 151 | LLLL.LL.LL 152 | L.LL.LL.LL 153 | L.LLLLL.LL 154 | ..L.L..... 155 | LLLLLLLLLL 156 | L.LLLLLL.L 157 | L.LLLLL.LL 158 | ``` 159 | 160 | ``` 161 | #.##.##.## 162 | #######.## 163 | #.#.#..#.. 164 | ####.##.## 165 | #.##.##.## 166 | #.#####.## 167 | ..#.#..... 168 | ########## 169 | #.######.# 170 | #.#####.## 171 | ``` 172 | 173 | ``` 174 | #.LL.LL.L# 175 | #LLLLLL.LL 176 | L.L.L..L.. 177 | LLLL.LL.LL 178 | L.LL.LL.LL 179 | L.LLLLL.LL 180 | ..L.L..... 181 | LLLLLLLLL# 182 | #.LLLLLL.L 183 | #.LLLLL.L# 184 | ``` 185 | 186 | ``` 187 | #.L#.##.L# 188 | #L#####.LL 189 | L.#.#..#.. 190 | ##L#.##.## 191 | #.##.#L.## 192 | #.#####.#L 193 | ..#.#..... 194 | LLL####LL# 195 | #.L#####.L 196 | #.L####.L# 197 | ``` 198 | 199 | ``` 200 | #.L#.L#.L# 201 | #LLLLLL.LL 202 | L.L.L..#.. 203 | ##LL.LL.L# 204 | L.LL.LL.L# 205 | #.LLLLL.LL 206 | ..L.L..... 207 | LLLLLLLLL# 208 | #.LLLLL#.L 209 | #.L#LL#.L# 210 | ``` 211 | 212 | ``` 213 | #.L#.L#.L# 214 | #LLLLLL.LL 215 | L.L.L..#.. 216 | ##L#.#L.L# 217 | L.L#.#L.L# 218 | #.L####.LL 219 | ..#.#..... 220 | LLL###LLL# 221 | #.LLLLL#.L 222 | #.L#LL#.L# 223 | ``` 224 | 225 | ``` 226 | #.L#.L#.L# 227 | #LLLLLL.LL 228 | L.L.L..#.. 229 | ##L#.#L.L# 230 | L.L#.LL.L# 231 | #.LLLL#.LL 232 | ..#.L..... 233 | LLL###LLL# 234 | #.LLLLL#.L 235 | #.L#LL#.L# 236 | ``` 237 | 238 | Again, at this point, people stop shifting around and the seating area reaches equilibrium. Once this occurs, you count **`26`** occupied seats. 239 | 240 | Given the new visibility method and the rule change for occupied seats becoming empty, once equilibrium is reached, **how many seats end up occupied?** 241 | 242 | ## References 243 | - https://adventofcode.com/2020/day/11 244 | -------------------------------------------------------------------------------- /day-11-seating-system/seating.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const seats = input.split('\n').map((line) => line.trim().split('')); 3 | 4 | const adjacencyMatrix = [ 5 | [-1, -1], [0, -1], [1, -1], 6 | [-1, 0], /* X */ [1, 0], 7 | [-1, 1], [0, 1], [1, 1], 8 | ]; 9 | 10 | const print = (state) => state.map((row) => row.join('')).join('\n'); 11 | 12 | const cycle = (state) => state.map((row, y) => row.map((column, x) => { 13 | const occupiedNeighbors = adjacencyMatrix 14 | .map(([dx, dy]) => (state[y + dy] || [])[x + dx] || '.') 15 | .filter((seat) => seat === '#'); 16 | 17 | return (column === 'L' && occupiedNeighbors.length === 0) 18 | ? '#' 19 | : (column === '#' && occupiedNeighbors.length >= 4) 20 | ? 'L' 21 | : column; 22 | })); 23 | 24 | let lastState = []; 25 | let currentState = seats; 26 | 27 | while (print(lastState) !== print(currentState)) { 28 | lastState = currentState; 29 | currentState = cycle(currentState); 30 | } 31 | 32 | return currentState.flat().filter((seat) => seat === '#').length; 33 | }; 34 | -------------------------------------------------------------------------------- /day-11-seating-system/seating2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const seats = input.split('\n').map((line) => line.trim().split('')); 3 | 4 | const adjacencyMatrix = [ 5 | [-1, -1], [0, -1], [1, -1], 6 | [-1, 0], /* X */ [1, 0], 7 | [-1, 1], [0, 1], [1, 1], 8 | ]; 9 | 10 | const print = (state) => state.map((row) => row.join('')).join('\n'); 11 | 12 | const marcher = (state, x, y) => ([dx, dy]) => { 13 | let steps = 1; 14 | 15 | // eslint-disable-next-line no-constant-condition 16 | while (true) { 17 | const [px, py] = [x + dx * steps, y + dy * steps]; 18 | 19 | if (!state[py] || !state[py][py]) { 20 | return '.'; 21 | } 22 | 23 | if (state[py][px] !== '.') { 24 | return state[py][px]; 25 | } 26 | 27 | steps++; 28 | } 29 | }; 30 | 31 | const cycle = (state) => state.map((row, y) => row.map((column, x) => { 32 | const occupiedNeighbors = adjacencyMatrix 33 | .map(marcher(state, x, y)) 34 | .filter((seat) => seat === '#'); 35 | 36 | return (column === 'L' && occupiedNeighbors.length === 0) 37 | ? '#' 38 | : (column === '#' && occupiedNeighbors.length >= 5) 39 | ? 'L' 40 | : column; 41 | })); 42 | 43 | let lastState = []; 44 | let currentState = seats; 45 | 46 | while (print(lastState) !== print(currentState)) { 47 | lastState = currentState; 48 | currentState = cycle(currentState); 49 | } 50 | 51 | return currentState.flat().filter((seat) => seat === '#').length; 52 | }; 53 | -------------------------------------------------------------------------------- /day-11-seating-system/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const seating = require('./seating'); 4 | const seating2 = require('./seating2'); 5 | 6 | describe('Day 11: Seating System', () => { 7 | it('should simulate occupied seats in equilibrium', () => { 8 | const input = 9 | `L.LL.LL.LL 10 | LLLLLLL.LL 11 | L.L.L..L.. 12 | LLLL.LL.LL 13 | L.LL.LL.LL 14 | L.LLLLL.LL 15 | ..L.L..... 16 | LLLLLLLLLL 17 | L.LLLLLL.L 18 | L.LLLLL.LL`; 19 | 20 | assert.strictEqual(seating(input), 37); 21 | }); 22 | 23 | describe('Part Two', () => { 24 | it('should simulate occupied seats in equilibrium again', () => { 25 | const input = 26 | `L.LL.LL.LL 27 | LLLLLLL.LL 28 | L.L.L..L.. 29 | LLLL.LL.LL 30 | L.LL.LL.LL 31 | L.LLLLL.LL 32 | ..L.L..... 33 | LLLLLLLLLL 34 | L.LLLLLL.L 35 | L.LLLLL.LL`; 36 | 37 | assert.strictEqual(seating2(input), 26); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /day-12-rain-risk/README.md: -------------------------------------------------------------------------------- 1 | # Day 12: Rain Risk 2 | 3 | Your ferry made decent progress toward the island, but the storm came in faster than anyone expected. The ferry needs to take **evasive actions**! 4 | 5 | Unfortunately, the ship's navigation computer seems to be malfunctioning; rather than giving a route directly to safety, it produced extremely circuitous instructions. When the captain uses the [PA system](https://en.wikipedia.org/wiki/Public_address_system) to ask if anyone can help, you quickly volunteer. 6 | 7 | The navigation instructions (your puzzle input) consists of a sequence of single-character **actions** paired with integer input **values**. After staring at them for a few minutes, you work out what they probably mean: 8 | 9 | - Action **`N`** means to move **north** by the given value. 10 | - Action **`S`** means to move **south** by the given value. 11 | - Action **`E`** means to move **east** by the given value. 12 | - Action **`W`** means to move **west** by the given value. 13 | - Action **`L`** means to turn **left** the given number of degrees. 14 | - Action **`R`** means to turn **right** the given number of degrees. 15 | - Action **`F`** means to move **forward** by the given value in the direction the ship is currently facing. 16 | 17 | The ship starts by facing **east**. Only the `L` and `R` actions change the direction the ship is facing. (That is, if the ship is facing east and the next instruction is `N10`, the ship would move north 10 units, but would still move east if the following action were `F`.) 18 | 19 | For example: 20 | 21 | ``` 22 | F10 23 | N3 24 | F7 25 | R90 26 | F11 27 | ``` 28 | 29 | These instructions would be handled as follows: 30 | 31 | - `F10` would move the ship 10 units east (because the ship starts by facing east) to **east 10, north 0**. 32 | - `N3` would move the ship 3 units north to **east 10, north 3**. 33 | - `F7` would move the ship another 7 units east (because the ship is still facing east) to **east 17, north 3**. 34 | - `R90` would cause the ship to turn right by 90 degrees and face **south**; it remains at **east 17, north 3**. 35 | - `F11` would move the ship 11 units south to **east 17, south 8**. 36 | 37 | At the end of these instructions, the ship's [Manhattan distance](https://en.wikipedia.org/wiki/Manhattan_distance) (sum of the absolute values of its east/west position and its north/south position) from its starting position is `17 + 8` = **`25`**. 38 | 39 | Figure out where the navigation instructions lead. **What is the Manhattan distance between that location and the ship's starting position?** 40 | 41 | ## Part Two 42 | 43 | Before you can give the destination to the captain, you realize that the actual action meanings were printed on the back of the instructions the whole time. 44 | 45 | Almost all of the actions indicate how to move a **waypoint** which is relative to the ship's position: 46 | 47 | - Action **`N`** means to move the waypoint **north** by the given value. 48 | - Action **`S`** means to move the waypoint **south** by the given value. 49 | - Action **`E`** means to move the waypoint **east** by the given value. 50 | - Action **`W`** means to move the waypoint **west** by the given value. 51 | - Action **`L`** means to rotate the waypoint around the ship **left** (**counter-clockwise**) the given number of degrees. 52 | - Action **`R`** means to rotate the waypoint around the ship **right** (**clockwise**) the given number of degrees. 53 | - Action **`F`** means to move **forward** to the waypoint a number of times equal to the given value. 54 | 55 | The waypoint starts **10 units east and 1 unit north** relative to the ship. The waypoint is relative to the ship; that is, if the ship moves, the waypoint moves with it. 56 | 57 | For example, using the same instructions as above: 58 | 59 | - `F10` moves the ship to the waypoint 10 times (a total of **100 units east and 10 units north**), leaving the ship at **east 100, north 10**. The waypoint stays 10 units east and 1 unit north of the ship. 60 | - `N3` moves the waypoint 3 units north to **10 units east and 4 units north of the ship**. The ship remains at **east 100, north 10**. 61 | - `F7` moves the ship to the waypoint 7 times (a total of **70 units east and 28 units north**), leaving the ship at **east 170, north 38**. The waypoint stays 10 units east and 4 units north of the ship. 62 | - `R90` rotates the waypoint around the ship clockwise 90 degrees, moving it to **4 units east and 10 units south of the ship**. The ship remains at **east 170, north 38**. 63 | - `F11` moves the ship to the waypoint 11 times (a total of **44 units east and 110 units south**), leaving the ship at **east 214, south 72**. The waypoint stays 4 units east and 10 units south of the ship. 64 | 65 | After these operations, the ship's Manhattan distance from its starting position is `214 + 72` = **`286`**. 66 | 67 | Figure out where the navigation instructions actually lead. **What is the Manhattan distance between that location and the ship's starting position?** 68 | 69 | ## References 70 | - https://adventofcode.com/2020/day/12 71 | -------------------------------------------------------------------------------- /day-12-rain-risk/ship.js: -------------------------------------------------------------------------------- 1 | const rotate = (origin, degrees) => (origin + degrees + 360) % 360; 2 | 3 | module.exports = (input) => { 4 | const ship = input.split('\n').reduce((ship, line) => { 5 | const action = line.trim()[0]; 6 | const value = parseInt(line.trim().slice(1)); 7 | const heading = ship.heading; 8 | 9 | ship.x += action === 'E' ? value : action === 'W' ? -value : 0; 10 | ship.y += action === 'N' ? value : action === 'S' ? -value : 0; 11 | 12 | ship.heading = 13 | rotate(heading, action === 'L' ? value : action === 'R' ? -value : 0); 14 | 15 | if (action === 'F') { 16 | ship.x += heading === 0 ? value : heading === 180 ? -value : 0; 17 | ship.y += heading === 90 ? value : heading === 270 ? -value : 0; 18 | } 19 | 20 | return ship; 21 | }, { heading: 0, x: 0, y: 0 }); 22 | 23 | return Math.abs(ship.x) + Math.abs(ship.y); 24 | }; 25 | -------------------------------------------------------------------------------- /day-12-rain-risk/ship2.js: -------------------------------------------------------------------------------- 1 | const rotate = ([dx, dy], degrees) => { 2 | const rotations = { 3 | 0: [dx, dy], 4 | 90: [-dy, dx], 5 | 180: [-dx, -dy], 6 | 270: [dy, -dx], 7 | }; 8 | 9 | return rotations[(degrees + 360) % 360]; 10 | }; 11 | 12 | module.exports = (input) => { 13 | const ship = input.split('\n').reduce((ship, line) => { 14 | const action = line.trim()[0]; 15 | const value = parseInt(line.trim().slice(1)); 16 | 17 | ship.wx += action === 'E' ? value : action === 'W' ? -value : 0; 18 | ship.wy += action === 'N' ? value : action === 'S' ? -value : 0; 19 | 20 | const [wx, wy] = rotate( 21 | [ship.wx, ship.wy], 22 | action === 'L' ? value : action === 'R' ? -value : 0 23 | ); 24 | 25 | ship.wx = wx; 26 | ship.wy = wy; 27 | 28 | ship.x += action === 'F' ? ship.wx * value : 0; 29 | ship.y += action === 'F' ? ship.wy * value : 0; 30 | 31 | return ship; 32 | }, { x: 0, y: 0, wx: 10, wy: 1 }); 33 | 34 | return Math.abs(ship.x) + Math.abs(ship.y); 35 | }; 36 | -------------------------------------------------------------------------------- /day-12-rain-risk/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const ship = require('./ship'); 4 | const ship2 = require('./ship2'); 5 | 6 | describe('Day 12: Rain Risk', () => { 7 | it('should determine Manhattan distance to target', () => { 8 | const input = 9 | `F10 10 | N3 11 | F7 12 | R90 13 | F11`; 14 | 15 | assert.strictEqual(ship(input), 25); 16 | }); 17 | 18 | describe('Part Two', () => { 19 | it('should determine Manhattan to target again', () => { 20 | const input = 21 | `F10 22 | N3 23 | F7 24 | R90 25 | F11`; 26 | 27 | assert.strictEqual(ship2(input), 286); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /day-13-shuttle-search/README.md: -------------------------------------------------------------------------------- 1 | # Day 13: Shuttle Search 2 | 3 | Your ferry can make it safely to a nearby port, but it won't get much further. When you call to book another ship, you discover that no ships embark from that port to your vacation island. You'll need to get from the port to the nearest airport. 4 | 5 | Fortunately, a shuttle bus service is available to bring you from the sea port to the airport! Each bus has an ID number that also indicates **how often the bus leaves for the airport**. 6 | 7 | Bus schedules are defined based on a **timestamp** that measures the **number of minutes** since some fixed reference point in the past. At timestamp `0`, every bus simultaneously departed from the sea port. After that, each bus travels to the airport, then various other locations, and finally returns to the sea port to repeat its journey forever. 8 | 9 | The time this loop takes a particular bus is also its ID number: the bus with ID `5` departs from the sea port at timestamps `0`, `5`, `10`, `15`, and so on. The bus with ID `11` departs at `0`, `11`, `22`, `33`, and so on. If you are there when the bus departs, you can ride that bus to the airport! 10 | 11 | Your notes (your puzzle input) consist of two lines. The first line is your estimate of the **earliest timestamp you could depart on a bus**. The second line lists the bus IDs that are in service according to the shuttle company; entries that show `x` must be out of service, so you decide to ignore them. 12 | 13 | To save time once you arrive, your goal is to figure out **the earliest bus you can take to the airport**. (There will be exactly one such bus.) 14 | 15 | For example, suppose you have the following notes: 16 | 17 | ``` 18 | 939 19 | 7,13,x,x,59,x,31,19 20 | ``` 21 | 22 | Here, the earliest timestamp you could depart is `939`, and the bus IDs in service are `7`, `13`, `59`, `31`, and `19`. Near timestamp `939`, these bus IDs depart at the times marked `D`: 23 | 24 | ``` 25 | time bus 7 bus 13 bus 59 bus 31 bus 19 26 | 929 . . . . . 27 | 930 . . . D . 28 | 931 D . . . D 29 | 932 . . . . . 30 | 933 . . . . . 31 | 934 . . . . . 32 | 935 . . . . . 33 | 936 . D . . . 34 | 937 . . . . . 35 | 938 D . . . . 36 | 939 . . . . . 37 | 940 . . . . . 38 | 941 . . . . . 39 | 942 . . . . . 40 | 943 . . . . . 41 | 944 . . D . . 42 | 945 D . . . . 43 | 946 . . . . . 44 | 947 . . . . . 45 | 948 . . . . . 46 | 949 . D . . . 47 | ``` 48 | 49 | The earliest bus you could take is bus ID `59`. It doesn't depart until timestamp `944`, so you would need to wait `944 - 939 = 5` minutes before it departs. Multiplying the bus ID by the number of minutes you'd need to wait gives **`295`**. 50 | 51 | **What is the ID of the earliest bus you can take to the airport multiplied by the number of minutes you'll need to wait for that bus?** 52 | 53 | ## Part Two 54 | 55 | The shuttle company is running a contest: one gold coin for anyone that can find the earliest timestamp such that the first bus ID departs at that time and each subsequent listed bus ID departs at that subsequent minute. (The first line in your input is no longer relevant.) 56 | 57 | For example, suppose you have the same list of bus IDs as above: 58 | 59 | ``` 60 | 7,13,x,x,59,x,31,19 61 | ``` 62 | 63 | An `x` in the schedule means there are no constraints on what bus IDs must depart at that time. 64 | 65 | This means you are looking for the earliest timestamp (called `t`) such that: 66 | 67 | - Bus ID `7` departs at timestamp `t`. 68 | - Bus ID `13` departs one minute after timestamp `t`. 69 | - There are no requirements or restrictions on departures at two or three minutes after timestamp `t`. 70 | - Bus ID `59` departs four minutes after timestamp `t`. 71 | - There are no requirements or restrictions on departures at five minutes after timestamp `t`. 72 | - Bus ID `31` departs six minutes after timestamp `t`. 73 | - Bus ID `19` departs seven minutes after timestamp `t`. 74 | 75 | The only bus departures that matter are the listed bus IDs at their specific offsets from `t`. Those bus IDs can depart at other times, and other bus IDs can depart at those times. For example, in the list above, because bus ID `19` must depart seven minutes after the timestamp at which bus ID `7` departs, bus ID `7` will always **also** be departing with bus ID `19` at seven minutes after timestamp `t`. 76 | 77 | In this example, the earliest timestamp at which this occurs is **`1068781`**: 78 | 79 | ``` 80 | time bus 7 bus 13 bus 59 bus 31 bus 19 81 | 1068773 . . . . . 82 | 1068774 D . . . . 83 | 1068775 . . . . . 84 | 1068776 . . . . . 85 | 1068777 . . . . . 86 | 1068778 . . . . . 87 | 1068779 . . . . . 88 | 1068780 . . . . . 89 | 1068781 D . . . . 90 | 1068782 . D . . . 91 | 1068783 . . . . . 92 | 1068784 . . . . . 93 | 1068785 . . D . . 94 | 1068786 . . . . . 95 | 1068787 . . . D . 96 | 1068788 D . . . D 97 | 1068789 . . . . . 98 | 1068790 . . . . . 99 | 1068791 . . . . . 100 | 1068792 . . . . . 101 | 1068793 . . . . . 102 | 1068794 . . . . . 103 | 1068795 D D . . . 104 | 1068796 . . . . . 105 | 1068797 . . . . . 106 | ``` 107 | 108 | In the above example, bus ID `7` departs at timestamp `1068788` (seven minutes after `t`). This is fine; the only requirement on that minute is that bus ID `19` departs then, and it does. 109 | 110 | Here are some other examples: 111 | 112 | - The earliest timestamp that matches the list `17,x,13,19` is **`3417`**. 113 | - `67,7,59,61` first occurs at timestamp **`754018`**. 114 | - `67,x,7,59,61` first occurs at timestamp **`779210`**. 115 | - `67,7,x,59,61` first occurs at timestamp **`1261476`**. 116 | - `1789,37,47,1889` first occurs at timestamp **`1202161486`**. 117 | 118 | However, with so many bus IDs in your list, surely the actual earliest timestamp will be larger than `100000000000000`! 119 | 120 | **What is the earliest timestamp such that all of the listed bus IDs depart at offsets matching their positions in the list?** 121 | 122 | ## References 123 | - https://adventofcode.com/2020/day/13 124 | -------------------------------------------------------------------------------- /day-13-shuttle-search/shuttle.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const lines = input.split('\n'); 3 | const notBeforeTimestamp = parseInt(lines[0]); 4 | const schedules = lines[1].split(',').filter((item) => item !== 'x').map(Number); 5 | 6 | let earliestBus; 7 | let earliestTimestamp = Infinity; 8 | 9 | for (let i = 0; i < schedules.length; i++) { 10 | const schedule = schedules[i]; 11 | const earliestAfterTimestamp = Math.ceil(notBeforeTimestamp / schedule) * schedule; 12 | 13 | if (earliestAfterTimestamp < earliestTimestamp) { 14 | earliestTimestamp = earliestAfterTimestamp; 15 | earliestBus = schedule; 16 | } 17 | } 18 | 19 | return earliestBus * (earliestTimestamp - notBeforeTimestamp); 20 | }; 21 | -------------------------------------------------------------------------------- /day-13-shuttle-search/shuttle2.js: -------------------------------------------------------------------------------- 1 | const gcd = (a, b) => b === 0 ? a : gcd(b, a % b); 2 | const lcm = (a, b) => (a * b) / gcd(a, b); 3 | 4 | module.exports = (input) => { 5 | const buses = input 6 | .split('\n')[1] 7 | .split(',') 8 | .map((busId, position) => [+busId, position]) 9 | .filter(([busId]) => !Number.isNaN(busId)); 10 | 11 | let t = 0; 12 | let m = buses[0][0]; 13 | 14 | for (let i = 0; i < buses.length; i++) { 15 | const routes = buses.slice(0, i + 1); 16 | 17 | while (!routes.every(([busId, position]) => (t + position) % busId === 0)) { 18 | t += m; 19 | } 20 | 21 | m = routes.reduce((m, [busId]) => lcm(m, busId), m); 22 | } 23 | 24 | return t; 25 | }; 26 | -------------------------------------------------------------------------------- /day-13-shuttle-search/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const shuttle = require('./shuttle'); 4 | const shuttle2 = require('./shuttle2'); 5 | 6 | describe('Day 13', () => { 7 | it('should determine time-to-wait and bus ID', () => { 8 | const input = 9 | `939 10 | 7,13,x,x,59,x,31,19`; 11 | 12 | assert.strictEqual(shuttle(input), 295); 13 | }); 14 | 15 | describe('Part Two', () => { 16 | it('should determine earliest timestamp aligning to positions (#1)', () => { 17 | const input = 18 | `939 19 | 7,13,x,x,59,x,31,19`; 20 | 21 | assert.strictEqual(shuttle2(input), 1068781); 22 | }); 23 | 24 | it('should determine earliest timestamp aligning to positions (#2)', () => { 25 | const input = 26 | `0 27 | 17,x,13,19`; 28 | 29 | assert.strictEqual(shuttle2(input), 3417); 30 | }); 31 | 32 | it('should determine earliest timestamp aligning to positions (#3)', () => { 33 | const input = 34 | `0 35 | 67,7,59,61`; 36 | 37 | assert.strictEqual(shuttle2(input), 754018); 38 | }); 39 | 40 | it('should determine earliest timestamp aligning to positions (#4)', () => { 41 | const input = 42 | `0 43 | 67,x,7,59,61`; 44 | 45 | assert.strictEqual(shuttle2(input), 779210); 46 | }); 47 | 48 | it('should determine earliest timestamp aligning to positions (#5)', () => { 49 | const input = 50 | `0 51 | 67,7,x,59,61`; 52 | 53 | assert.strictEqual(shuttle2(input), 1261476); 54 | }); 55 | 56 | it('should determine earliest timestamp aligning to positions (#6)', () => { 57 | const input = 58 | `0 59 | 1789,37,47,1889`; 60 | 61 | assert.strictEqual(shuttle2(input), 1202161486); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /day-14-docking-data/README.md: -------------------------------------------------------------------------------- 1 | # Day 14: Docking Data 2 | 3 | As your ferry approaches the sea port, the captain asks for your help again. The computer system that runs this port isn't compatible with the docking program on the ferry, so the docking parameters aren't being correctly initialized in the docking program's memory. 4 | 5 | After a brief inspection, you discover that the sea port's computer system uses a strange [bitmask](https://en.wikipedia.org/wiki/Mask_(computing)) system in its initialization program. Although you don't have the correct decoder chip handy, you can emulate it in software! 6 | 7 | The initialization program (your puzzle input) can either update the bitmask or write a value to memory. Values and memory addresses are both 36-bit unsigned integers. For example, ignoring bitmasks for a moment, a line like `mem[8] = 11` would write the value `11` to memory address `8`. 8 | 9 | The bitmask is always given as a string of 36 bits, written with the most significant bit (representing `2^35`) on the left and the least significant bit (`2^0`, that is, the `1`s bit) on the right. The current bitmask is applied to values immediately before they are written to memory: a `0` or `1` overwrites the corresponding bit in the value, while an `X` leaves the bit in the value unchanged. 10 | 11 | For example, consider the following program: 12 | 13 | ``` 14 | mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 15 | mem[8] = 11 16 | mem[7] = 101 17 | mem[8] = 0 18 | ``` 19 | 20 | This program starts by specifying a bitmask (`mask = ....`). The mask it specifies will overwrite two bits in every written value: the `2`s bit is overwritten with `0`, and the `64`s bit is overwritten with `1`. 21 | 22 | The program then attempts to write the value `11` to memory address `8`. By expanding everything out to individual bits, the mask is applied as follows: 23 | 24 | ``` 25 | value: 000000000000000000000000000000001011 (decimal 11) 26 | mask: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 27 | result: 000000000000000000000000000001001001 (decimal 73) 28 | ``` 29 | 30 | So, because of the mask, the value `73` is written to memory address `8` instead. Then, the program tries to write `101` to address `7`: 31 | 32 | ``` 33 | value: 000000000000000000000000000001100101 (decimal 101) 34 | mask: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 35 | result: 000000000000000000000000000001100101 (decimal 101) 36 | ``` 37 | 38 | This time, the mask has no effect, as the bits it overwrote were already the values the mask tried to set. Finally, the program tries to write `0` to address `8`: 39 | 40 | ``` 41 | value: 000000000000000000000000000000000000 (decimal 0) 42 | mask: XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 43 | result: 000000000000000000000000000001000000 (decimal 64) 44 | ``` 45 | 46 | `64` is written to address `8` instead, overwriting the value that was there previously. 47 | 48 | To initialize your ferry's docking program, you need the sum of all values left in memory after the initialization program completes. (The entire 36-bit address space begins initialized to the value `0` at every address.) In the above example, only two values in memory are not zero - `101` (at address `7`) and `64` (at address `8`) - producing a sum of **`165`**. 49 | 50 | Execute the initialization program. **What is the sum of all values left in memory after it completes?** 51 | 52 | ## Part Two 53 | 54 | For some reason, the sea port's computer system still can't communicate with your ferry's docking program. It must be using **version 2** of the decoder chip! 55 | 56 | A version 2 decoder chip doesn't modify the values being written at all. Instead, it acts as a [memory address decoder](https://www.youtube.com/watch?v=PvfhANgLrm4). Immediately before a value is written to memory, each bit in the bitmask modifies the corresponding bit of the destination **memory address** in the following way: 57 | 58 | - If the bitmask bit is `0`, the corresponding memory address bit is **unchanged**. 59 | - If the bitmask bit is `1`, the corresponding memory address bit is **overwritten with `1`**. 60 | - If the bitmask bit is `X`, the corresponding memory address bit is **floating**. 61 | 62 | A **floating** bit is not connected to anything and instead fluctuates unpredictably. In practice, this means the floating bits will take on **all possible values**, potentially causing many memory addresses to be written all at once! 63 | 64 | For example, consider the following program: 65 | 66 | ``` 67 | mask = 000000000000000000000000000000X1001X 68 | mem[42] = 100 69 | mask = 00000000000000000000000000000000X0XX 70 | mem[26] = 1 71 | ``` 72 | 73 | When this program goes to write to memory address `42`, it first applies the bitmask: 74 | 75 | ``` 76 | address: 000000000000000000000000000000101010 (decimal 42) 77 | mask: 000000000000000000000000000000X1001X 78 | result: 000000000000000000000000000000X1101X 79 | ``` 80 | 81 | After applying the mask, four bits are overwritten, three of which are different, and two of which are **floating**. Floating bits take on every possible combination of values; with two floating bits, four actual memory addresses are written: 82 | 83 | ``` 84 | 000000000000000000000000000000011010 (decimal 26) 85 | 000000000000000000000000000000011011 (decimal 27) 86 | 000000000000000000000000000000111010 (decimal 58) 87 | 000000000000000000000000000000111011 (decimal 59) 88 | ``` 89 | 90 | Next, the program is about to write to memory address `26` with a different bitmask: 91 | 92 | ``` 93 | address: 000000000000000000000000000000011010 (decimal 26) 94 | mask: 00000000000000000000000000000000X0XX 95 | result: 00000000000000000000000000000001X0XX 96 | ``` 97 | 98 | This results in an address with three floating bits, causing writes to **eight** memory addresses: 99 | 100 | ``` 101 | 000000000000000000000000000000010000 (decimal 16) 102 | 000000000000000000000000000000010001 (decimal 17) 103 | 000000000000000000000000000000010010 (decimal 18) 104 | 000000000000000000000000000000010011 (decimal 19) 105 | 000000000000000000000000000000011000 (decimal 24) 106 | 000000000000000000000000000000011001 (decimal 25) 107 | 000000000000000000000000000000011010 (decimal 26) 108 | 000000000000000000000000000000011011 (decimal 27) 109 | ``` 110 | 111 | The entire 36-bit address space still begins initialized to the value 0 at every address, and you still need the sum of all values left in memory at the end of the program. In this example, the sum is **`208`**. 112 | 113 | Execute the initialization program using an emulator for a version 2 decoder chip. **What is the sum of all values left in memory after it completes?** 114 | 115 | ## References 116 | - https://adventofcode.com/2020/day/14 117 | -------------------------------------------------------------------------------- /day-14-docking-data/docking.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const lines = input.split('\n'); 3 | const memory = new Map(); 4 | 5 | let bitmask; 6 | 7 | const mask = (value) => { 8 | const bits = value.toString(2).padStart(36, '0').split(''); 9 | const result = bitmask.map((bit, i) => bit === 'X' ? bits[i] : bit); 10 | 11 | return parseInt(result.join(''), 2); 12 | }; 13 | 14 | for (const line of lines) { 15 | if (/mask/.test(line)) { 16 | bitmask = /mask = (.*)/.exec(line)[1].split(''); 17 | } else if (/mem/.test(line)) { 18 | const [, address, value] = /mem\[(\d+)\] = (\d+)/.exec(line); 19 | 20 | memory.set(+address, mask(+value)); 21 | } 22 | } 23 | 24 | return [...memory.values()].reduce((a, b) => a + b, 0); 25 | }; 26 | -------------------------------------------------------------------------------- /day-14-docking-data/docking2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const lines = input.split('\n'); 3 | const memory = new Map(); 4 | 5 | let bitmask; 6 | 7 | const mask = (value) => { 8 | const address = value.toString(2).padStart(36, '0').split(''); 9 | const result = bitmask.map((bit, i) => bit === 'X' ? 'X' : bit === '0' ? address[i] : bit); 10 | const numberOfFloats = result.filter((bit) => bit === 'X').length; 11 | 12 | const floats = Array 13 | .from({ length: Math.pow(2, numberOfFloats) }) 14 | .map((_, i) => { 15 | const combination = i.toString(2).padStart(numberOfFloats, '0').split(''); 16 | const float = result.map((bit) => bit === 'X' ? combination.shift() : bit); 17 | 18 | return parseInt(float.join(''), 2); 19 | }); 20 | 21 | return floats; 22 | }; 23 | 24 | for (const line of lines) { 25 | if (/mask/.test(line)) { 26 | bitmask = /mask = (.*)/.exec(line)[1].split(''); 27 | } else if (/mem/.test(line)) { 28 | const [, address, value] = /mem\[(\d+)\] = (\d+)/.exec(line); 29 | const addresses = mask(+address); 30 | 31 | addresses.forEach((address) => memory.set(address, +value)); 32 | } 33 | } 34 | 35 | return [...memory.values()].reduce((a, b) => a + b, 0); 36 | }; 37 | -------------------------------------------------------------------------------- /day-14-docking-data/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const docking = require('./docking'); 4 | const docking2 = require('./docking2'); 5 | 6 | describe('Day 14: Docking Data', () => { 7 | it('should sum values left in memory after execution', () => { 8 | const input = 9 | `mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X 10 | mem[8] = 11 11 | mem[7] = 101 12 | mem[8] = 0`; 13 | 14 | assert.strictEqual(docking(input), 165); 15 | }); 16 | 17 | describe('Part Two', () => { 18 | it('should sum values left in memory after execution (v2)', () => { 19 | const input = 20 | `mask = 000000000000000000000000000000X1001X 21 | mem[42] = 100 22 | mask = 00000000000000000000000000000000X0XX 23 | mem[26] = 1`; 24 | 25 | assert.strictEqual(docking2(input), 208); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /day-15-rambunctious-recitation/README.md: -------------------------------------------------------------------------------- 1 | # Day 15: Rambunctious Recitation 2 | 3 | You catch the airport shuttle and try to book a new flight to your vacation island. Due to the storm, all direct flights have been cancelled, but a route is available to get around the storm. You take it. 4 | 5 | While you wait for your flight, you decide to check in with the Elves back at the North Pole. They're playing a **memory game** and are ever so excited to explain the rules! 6 | 7 | In this game, the players take turns saying **numbers**. They begin by taking turns reading from a list of **starting numbers** (your puzzle input). Then, each turn consists of considering the **most recently spoken number**: 8 | 9 | - If that was the first time the number has been spoken, the current player says 0. 10 | - Otherwise, the number had been spoken before; the current player announces how many turns apart the number is from when it was previously spoken. 11 | 12 | So, after the starting numbers, each turn results in that player speaking aloud either 0 (if the last number is new) or an age (if the last number is a repeat). 13 | 14 | For example, suppose the starting numbers are 0,3,6: 15 | 16 | - **Turn 1**: The `1`st number spoken is a starting number, **`0`**. 17 | - **Turn 2**: The `2`nd number spoken is a starting number, **`3`**. 18 | - **Turn 3**: The `3`rd number spoken is a starting number, **`6`**. 19 | - **Turn 4**: Now, consider the last number spoken, `6`. Since that was the first time the number had been spoken, the `4`th number spoken is **`0`**. 20 | - **Turn 5**: Next, again consider the last number spoken, `0`. Since it **had** been spoken before, the next number to speak is the difference between the turn number when it was last spoken (the previous turn, `4`) and the turn number of the time it was most recently spoken before then (turn `1`). Thus, the `5`th number spoken is `4 - 1`, **`3`**. 21 | - **Turn 6**: The last number spoken, `3` had also been spoken before, most recently on turns `5` and `2`. So, the `6`th number spoken is `5 - 2`, **`3`**. 22 | - **Turn 7**: Since `3` was just spoken twice in a row, and the last two turns are `1` turn apart, the `7`th number spoken is **`1`**. 23 | - **Turn 8**: Since `1` is new, the `8`th number spoken is **`0`**. 24 | - **Turn 9**: `0` was last spoken on turns `8` and `4`, so the `9`th number spoken is the difference between them, **`4`**. 25 | - **Turn 10**: `4` is new, so the `10`th number spoken is **`0`**. 26 | 27 | (The game ends when the Elves get sick of playing or dinner is ready, whichever comes first.) 28 | 29 | Their question for you is: what will be the **`2020`th** number spoken? In the example above, the `2020`th number spoken will be `436`. 30 | 31 | Here are a few more examples: 32 | 33 | - Given the starting numbers `1,3,2`, the `2020`th number spoken is `1`. 34 | - Given the starting numbers `2,1,3`, the `2020`th number spoken is `10`. 35 | - Given the starting numbers `1,2,3`, the `2020`th number spoken is `27`. 36 | - Given the starting numbers `2,3,1`, the `2020`th number spoken is `78`. 37 | - Given the starting numbers `3,2,1`, the `2020`th number spoken is `438`. 38 | - Given the starting numbers `3,1,2`, the `2020`th number spoken is `1836`. 39 | 40 | Given your starting numbers, **what will be the `2020`th number spoken?** 41 | 42 | ## Part Two 43 | 44 | Impressed, the Elves issue you a challenge: determine the `30000000`th number spoken. For example, given the same starting numbers as above: 45 | 46 | - Given `0,3,6`, the `30000000`th number spoken is `175594`. 47 | - Given `1,3,2`, the `30000000`th number spoken is `2578`. 48 | - Given `2,1,3`, the `30000000`th number spoken is `3544142`. 49 | - Given `1,2,3`, the `30000000`th number spoken is `261214`. 50 | - Given `2,3,1`, the `30000000`th number spoken is `6895259`. 51 | - Given `3,2,1`, the `30000000`th number spoken is `18`. 52 | - Given `3,1,2`, the `30000000`th number spoken is `362`. 53 | 54 | Given your starting numbers, **what will be the `30000000`th number spoken?** 55 | 56 | ## References 57 | - https://adventofcode.com/2020/day/15 58 | -------------------------------------------------------------------------------- /day-15-rambunctious-recitation/recitation.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const lines = input.split(',').map(Number); 3 | const history = {}; 4 | 5 | const add = (number, turn) => { 6 | history[number] = [...(history[number] || []).slice(-1), turn]; 7 | }; 8 | 9 | lines.forEach((number, i) => add(number, i + 1)); 10 | 11 | let lastNumberSpoken = lines.slice(-1)[0]; 12 | 13 | for (let i = lines.length + 1; i <= 2020; i++) { 14 | lastNumberSpoken = history[lastNumberSpoken].length === 1 15 | ? 0 16 | : history[lastNumberSpoken].reduce((a, b) => b - a, 0); 17 | 18 | add(lastNumberSpoken, i); 19 | } 20 | 21 | return lastNumberSpoken; 22 | }; 23 | -------------------------------------------------------------------------------- /day-15-rambunctious-recitation/recitation2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input, recitations = 30000000) => { 2 | const lines = input.split(',').map(Number); 3 | const history = {}; 4 | 5 | const add = (number, turn) => { 6 | const entry = history[number] || []; 7 | 8 | entry.length === 2 && entry.shift(); 9 | entry.push(turn); 10 | 11 | history[number] = entry; 12 | }; 13 | 14 | lines.forEach((number, i) => add(number, i + 1)); 15 | 16 | let lastNumberSpoken = lines.slice(-1)[0]; 17 | 18 | for (let i = lines.length + 1; i <= recitations; i++) { 19 | const entry = history[lastNumberSpoken]; 20 | 21 | lastNumberSpoken = entry.length === 1 22 | ? 0 23 | : entry.reduce((a, b) => b - a, 0); 24 | 25 | add(lastNumberSpoken, i); 26 | } 27 | 28 | return lastNumberSpoken; 29 | }; 30 | -------------------------------------------------------------------------------- /day-15-rambunctious-recitation/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const recitation = require('./recitation'); 4 | const recitation2 = require('./recitation2'); 5 | 6 | describe('Day 15: Rambunctious Recitation', () => { 7 | it('should recite 0,3,6 2020 times', () => { 8 | assert.strictEqual(recitation('0,3,6'), 436); 9 | }); 10 | 11 | it('should recite 1,3,2 2020 times', () => { 12 | assert.strictEqual(recitation('1,3,2'), 1); 13 | }); 14 | 15 | it('should recite 2,1,3 2020 times', () => { 16 | assert.strictEqual(recitation('2,1,3'), 10); 17 | }); 18 | 19 | it('should recite 1,2,3 2020 times', () => { 20 | assert.strictEqual(recitation('1,2,3'), 27); 21 | }); 22 | 23 | it('should recite 2,3,1 2020 times', () => { 24 | assert.strictEqual(recitation('2,3,1'), 78); 25 | }); 26 | 27 | it('should recite 3,2,1 2020 times', () => { 28 | assert.strictEqual(recitation('3,2,1'), 438); 29 | }); 30 | 31 | it('should recite 3,1,2 2020 times', () => { 32 | assert.strictEqual(recitation('3,1,2'), 1836); 33 | }); 34 | 35 | describe('Part Two', () => { 36 | it.skip('should recite 0,3,6 30000000 times', () => { 37 | assert.strictEqual(recitation2('0,3,6'), 175594); 38 | }); 39 | 40 | it.skip('should recite 1,3,2 30000000 times', () => { 41 | assert.strictEqual(recitation2('1,3,2'), 2578); 42 | }); 43 | 44 | it.skip('should recite 2,1,3 30000000 times', () => { 45 | assert.strictEqual(recitation2('2,1,3'), 3544142); 46 | }); 47 | 48 | it.skip('should recite 1,2,3 30000000 times', () => { 49 | assert.strictEqual(recitation2('1,2,3'), 261214); 50 | }); 51 | 52 | it.skip('should recite 2,3,1 30000000 times', () => { 53 | assert.strictEqual(recitation2('2,3,1'), 6895259); 54 | }); 55 | 56 | it.skip('should recite 3,2,1 30000000 times', () => { 57 | assert.strictEqual(recitation2('3,2,1'), 18); 58 | }); 59 | 60 | it.skip('should recite 3,1,2 30000000 times', () => { 61 | assert.strictEqual(recitation2('3,1,2'), 362); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /day-16-ticket-translation/README.md: -------------------------------------------------------------------------------- 1 | # Day 16: Ticket Translation 2 | 3 | As you're walking to yet another connecting flight, you realize that one of the legs of your re-routed trip coming up is on a high-speed train. However, the train ticket you were given is in a language you don't understand. You should probably figure out what it says before you get to the train station after the next flight. 4 | 5 | Unfortunately, you can't actually **read** the words on the ticket. You can, however, read the numbers, and so you figure out **the fields these tickets must have** and **the valid ranges** for values in those fields. 6 | 7 | You collect the **rules for ticket fields**, the **numbers on your ticket**, and the **numbers on other nearby tickets** for the same train service (via the airport security cameras) together into a single document you can reference (your puzzle input). 8 | 9 | The **rules for ticket fields** specify a list of fields that exist **somewhere** on the ticket and the **valid ranges of values** for each field. For example, a rule like `class: 1-3 or 5-7` means that one of the fields in every ticket is named `class` and can be any value in the ranges `1-3` or `5-7` (inclusive, such that `3` and `5` are both valid in this field, but `4` is not). 10 | 11 | Each ticket is represented by a single line of comma-separated values. The values are the numbers on the ticket in the order they appear; every ticket has the same format. For example, consider this ticket: 12 | 13 | ``` 14 | .--------------------------------------------------------. 15 | | ????: 101 ?????: 102 ??????????: 103 ???: 104 | 16 | | | 17 | | ??: 301 ??: 302 ???????: 303 ??????? | 18 | | ??: 401 ??: 402 ???? ????: 403 ????????? | 19 | '--------------------------------------------------------' 20 | ``` 21 | 22 | Here, `?` represents text in a language you don't understand. This ticket might be represented as `101,102,103,104,301,302,303,401,402,403`; of course, the actual train tickets you're looking at are **much** more complicated. In any case, you've extracted just the numbers in such a way that the first number is always the same specific field, the second number is always a different specific field, and so on - you just don't know what each position actually means! 23 | 24 | Start by determining which tickets are **completely invalid**; these are tickets that contain values which **aren't valid for any field**. Ignore **your ticket** for now. 25 | 26 | For example, suppose you have the following notes: 27 | 28 | ``` 29 | class: 1-3 or 5-7 30 | row: 6-11 or 33-44 31 | seat: 13-40 or 45-50 32 | 33 | your ticket: 34 | 7,1,14 35 | 36 | nearby tickets: 37 | 7,3,47 38 | 40,4,50 39 | 55,2,20 40 | 38,6,12 41 | ``` 42 | 43 | It doesn't matter which position corresponds to which field; you can identify invalid **nearby tickets** by considering only whether tickets contain **values that are not valid for any field**. In this example, the values on the first **nearby ticket** are all valid for at least one field. This is not true of the other three **nearby tickets**: the values `4`, `55`, and `12` are are not valid for any field. Adding together all of the invalid values produces your **ticket scanning error rate**: `4 + 55 + 12` = **`71`**. 44 | 45 | Consider the validity of the nearby tickets you scanned. What is your ticket scanning error rate? 46 | 47 | ## Part Two 48 | 49 | Now that you've identified which tickets contain invalid values, **discard those tickets entirely**. Use the remaining valid tickets to determine which field is which. 50 | 51 | Using the valid ranges for each field, determine what order the fields appear on the tickets. The order is consistent between all tickets: if `seat` is the third field, it is the third field on every ticket, including **your ticket**. 52 | 53 | For example, suppose you have the following notes: 54 | 55 | ``` 56 | class: 0-1 or 4-19 57 | row: 0-5 or 8-19 58 | seat: 0-13 or 16-19 59 | 60 | your ticket: 61 | 11,12,13 62 | 63 | nearby tickets: 64 | 3,9,18 65 | 15,1,5 66 | 5,14,9 67 | ``` 68 | 69 | Based on the **nearby tickets** in the above example, the first position must be `row`, the second position must be `class`, and the third position must be `seat`; you can conclude that in **your ticket**, `class` is `12`, `row` is `11`, and `seat` is `13`. 70 | 71 | Once you work out which field is which, look for the six fields on **your ticket** that start with the word `departure`. **What do you get if you multiply those six values together?** 72 | 73 | ## References 74 | - https://adventofcode.com/2020/day/16 75 | -------------------------------------------------------------------------------- /day-16-ticket-translation/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const ticket = require('./ticket'); 4 | const ticket2 = require('./ticket2'); 5 | 6 | describe('Day 16: Ticket Translation', () => { 7 | it('should determine ticket scanning error rate', () => { 8 | const input = 9 | `class: 1-3 or 5-7 10 | row: 6-11 or 33-44 11 | seat: 13-40 or 45-50 12 | 13 | your ticket: 14 | 7,1,14 15 | 16 | nearby tickets: 17 | 7,3,47 18 | 40,4,50 19 | 55,2,20 20 | 38,6,12`; 21 | 22 | assert.strictEqual(ticket(input), 71); 23 | }); 24 | 25 | describe('Part Two', () => { 26 | it('should determine ticket field order', () => { 27 | const input = 28 | `class: 0-1 or 4-19 29 | row: 0-5 or 8-19 30 | seat: 0-13 or 16-19 31 | 32 | your ticket: 33 | 11,12,13 34 | 35 | nearby tickets: 36 | 3,9,18 37 | 15,1,5 38 | 5,14,9`; 39 | 40 | assert.strictEqual(ticket2(input), 1); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /day-16-ticket-translation/ticket.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const blocks = input.split(/\n{2,}/); 3 | 4 | const fields = blocks[0].split('\n').map((field) => { 5 | const [, r1min, r1max, r2min, r2max] = 6 | /: (\d+)-(\d+) or (\d+)-(\d+)/.exec(field); 7 | 8 | return [ 9 | parseInt(r1min), 10 | parseInt(r1max), 11 | parseInt(r2min), 12 | parseInt(r2max), 13 | ]; 14 | }); 15 | 16 | const nearbyTickets = blocks[2] 17 | .split('\n') 18 | .slice(1) 19 | .map((line) => line.split(',').map(Number)); 20 | 21 | return nearbyTickets.flat().reduce((errorRate, ticket) => { 22 | if (!fields.some(([r1min, r1max, r2min, r2max]) => 23 | (ticket >= r1min && ticket <= r1max) || 24 | (ticket >= r2min && ticket <= r2max))) { 25 | errorRate += ticket; 26 | } 27 | 28 | return errorRate; 29 | }, 0); 30 | }; 31 | -------------------------------------------------------------------------------- /day-16-ticket-translation/ticket2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const blocks = input.split(/\n{2,}/); 3 | 4 | let fields = blocks[0].split('\n').map((field) => { 5 | const [, name, r1min, r1max, r2min, r2max] = 6 | /(.*): (\d+)-(\d+) or (\d+)-(\d+)/.exec(field); 7 | 8 | return [ 9 | name.trim(), 10 | parseInt(r1min), 11 | parseInt(r1max), 12 | parseInt(r2min), 13 | parseInt(r2max), 14 | ]; 15 | }); 16 | 17 | const myTicket = blocks[1] 18 | .split('\n') 19 | .slice(1) 20 | .map((line) => line.split(',').map(Number))[0]; 21 | 22 | const nearbyTickets = blocks[2] 23 | .split('\n') 24 | .slice(1) 25 | .map((line) => line.split(',').map(Number)) 26 | .filter((ticket) => { 27 | return ticket.every((number) => fields 28 | .some(([, r1min, r1max, r2min, r2max]) => 29 | (number >= r1min && number <= r1max) || 30 | (number >= r2min && number <= r2max))); 31 | }); 32 | 33 | const columns = Array 34 | .from({ length: myTicket.length }) 35 | .map((_ , i) => [i, nearbyTickets.map((numbers) => numbers[i])]); 36 | 37 | let result = 1; 38 | 39 | while (columns.length) { 40 | const [column, numbers] = columns.shift(); 41 | 42 | const matches = fields.filter(([, r1min, r1max, r2min, r2max]) => { 43 | return numbers.every((number) => (number >= r1min && number <= r1max) || 44 | (number >= r2min && number <= r2max)); 45 | }); 46 | 47 | if (matches.length === 1) { 48 | fields = fields.filter(([name]) => name !== matches[0][0]); 49 | 50 | if (/departure/.test(matches[0][0])) { 51 | result *= myTicket[column]; 52 | } 53 | } else { 54 | columns.push([column, numbers]); 55 | } 56 | } 57 | 58 | return result; 59 | }; 60 | -------------------------------------------------------------------------------- /day-17-conway-cubes/README.md: -------------------------------------------------------------------------------- 1 | # Day 17: Conway Cubes 2 | 3 | As your flight slowly drifts through the sky, the Elves at the Mythical Information Bureau at the North Pole contact you. They'd like some help debugging a malfunctioning experimental energy source aboard one of their super-secret imaging satellites. 4 | 5 | The experimental energy source is based on cutting-edge technology: a set of Conway Cubes contained in a pocket dimension! When you hear it's having problems, you can't help but agree to take a look. 6 | 7 | The pocket dimension contains an infinite 3-dimensional grid. At every integer 3-dimensional coordinate (`x,y,z`), there exists a single cube which is either **active** or **inactive**. 8 | 9 | In the initial state of the pocket dimension, almost all cubes start **inactive**. The only exception to this is a small flat region of cubes (your puzzle input); the cubes in this region start in the specified **active** (`#`) or **inactive** (`.`) state. 10 | 11 | The energy source then proceeds to boot up by executing six **cycles**. 12 | 13 | Each cube only ever considers its **neighbors**: any of the 26 other cubes where any of their coordinates differ by at most `1`. For example, given the cube at `x=1,y=2,z=3`, its neighbors include the cube at `x=2,y=2,z=2`, the cube at `x=0,y=2,z=3`, and so on. 14 | 15 | During a cycle, **all** cubes **simultaneously** change their state according to the following rules: 16 | 17 | - If a cube is **active** and **exactly `2` or `3`** of its neighbors are also active, the cube remains **active**. Otherwise, the cube becomes **inactive**. 18 | - If a cube is **inactive** but **exactly `3`** of its neighbors are active, the cube becomes **active**. Otherwise, the cube remains **inactive**. 19 | 20 | The engineers responsible for this experimental energy source would like you to simulate the pocket dimension and determine what the configuration of cubes should be at the end of the six-cycle boot process. 21 | 22 | For example, consider the following initial state: 23 | 24 | ``` 25 | .#. 26 | ..# 27 | ### 28 | ``` 29 | 30 | Even though the pocket dimension is 3-dimensional, this initial state represents a small 2-dimensional slice of it. (In particular, this initial state defines a 3x3x1 region of the 3-dimensional space.) 31 | 32 | Simulating a few cycles from this initial state produces the following configurations, where the result of each cycle is shown layer-by-layer at each given `z` coordinate (and the frame of view follows the active cells in each cycle): 33 | 34 | ``` 35 | Before any cycles: 36 | 37 | z=0 38 | .#. 39 | ..# 40 | ### 41 | 42 | 43 | After 1 cycle: 44 | 45 | z=-1 46 | #.. 47 | ..# 48 | .#. 49 | 50 | z=0 51 | #.# 52 | .## 53 | .#. 54 | 55 | z=1 56 | #.. 57 | ..# 58 | .#. 59 | 60 | 61 | After 2 cycles: 62 | 63 | z=-2 64 | ..... 65 | ..... 66 | ..#.. 67 | ..... 68 | ..... 69 | 70 | z=-1 71 | ..#.. 72 | .#..# 73 | ....# 74 | .#... 75 | ..... 76 | 77 | z=0 78 | ##... 79 | ##... 80 | #.... 81 | ....# 82 | .###. 83 | 84 | z=1 85 | ..#.. 86 | .#..# 87 | ....# 88 | .#... 89 | ..... 90 | 91 | z=2 92 | ..... 93 | ..... 94 | ..#.. 95 | ..... 96 | ..... 97 | 98 | 99 | After 3 cycles: 100 | 101 | z=-2 102 | ....... 103 | ....... 104 | ..##... 105 | ..###.. 106 | ....... 107 | ....... 108 | ....... 109 | 110 | z=-1 111 | ..#.... 112 | ...#... 113 | #...... 114 | .....## 115 | .#...#. 116 | ..#.#.. 117 | ...#... 118 | 119 | z=0 120 | ...#... 121 | ....... 122 | #...... 123 | ....... 124 | .....## 125 | .##.#.. 126 | ...#... 127 | 128 | z=1 129 | ..#.... 130 | ...#... 131 | #...... 132 | .....## 133 | .#...#. 134 | ..#.#.. 135 | ...#... 136 | 137 | z=2 138 | ....... 139 | ....... 140 | ..##... 141 | ..###.. 142 | ....... 143 | ....... 144 | ....... 145 | ``` 146 | 147 | After the full six-cycle boot process completes, **`112`** cubes are left in the **active** state. 148 | 149 | Starting with your given initial configuration, simulate six cycles. **How many cubes are left in the active state after the sixth cycle?** 150 | 151 | ## Part Two 152 | 153 | For some reason, your simulated results don't match what the experimental energy source engineers expected. Apparently, the pocket dimension actually has **four spatial dimensions**, not three. 154 | 155 | The pocket dimension contains an infinite 4-dimensional grid. At every integer 4-dimensional coordinate (`x,y,z,w`), there exists a single cube (really, a **hypercube**) which is still either **active** or **inactive**. 156 | 157 | Each cube only ever considers its **neighbors**: any of the 80 other cubes where any of their coordinates differ by at most `1`. For example, given the cube at `x=1,y=2,z=3,w=4`, its neighbors include the cube at `x=2,y=2,z=3,w=3`, the cube at `x=0,y=2,z=3,w=4`, and so on. 158 | 159 | The initial state of the pocket dimension still consists of a small flat region of cubes. Furthermore, the same rules for cycle updating still apply: during each cycle, consider the **number of active neighbors** of each cube. 160 | 161 | For example, consider the same initial state as in the example above. Even though the pocket dimension is 4-dimensional, this initial state represents a small 2-dimensional slice of it. (In particular, this initial state defines a 3x3x1x1 region of the 4-dimensional space.) 162 | 163 | Simulating a few cycles from this initial state produces the following configurations, where the result of each cycle is shown layer-by-layer at each given `z` and `w` coordinate: 164 | 165 | ``` 166 | Before any cycles: 167 | 168 | z=0, w=0 169 | .#. 170 | ..# 171 | ### 172 | 173 | 174 | After 1 cycle: 175 | 176 | z=-1, w=-1 177 | #.. 178 | ..# 179 | .#. 180 | 181 | z=0, w=-1 182 | #.. 183 | ..# 184 | .#. 185 | 186 | z=1, w=-1 187 | #.. 188 | ..# 189 | .#. 190 | 191 | z=-1, w=0 192 | #.. 193 | ..# 194 | .#. 195 | 196 | z=0, w=0 197 | #.# 198 | .## 199 | .#. 200 | 201 | z=1, w=0 202 | #.. 203 | ..# 204 | .#. 205 | 206 | z=-1, w=1 207 | #.. 208 | ..# 209 | .#. 210 | 211 | z=0, w=1 212 | #.. 213 | ..# 214 | .#. 215 | 216 | z=1, w=1 217 | #.. 218 | ..# 219 | .#. 220 | 221 | 222 | After 2 cycles: 223 | 224 | z=-2, w=-2 225 | ..... 226 | ..... 227 | ..#.. 228 | ..... 229 | ..... 230 | 231 | z=-1, w=-2 232 | ..... 233 | ..... 234 | ..... 235 | ..... 236 | ..... 237 | 238 | z=0, w=-2 239 | ###.. 240 | ##.## 241 | #...# 242 | .#..# 243 | .###. 244 | 245 | z=1, w=-2 246 | ..... 247 | ..... 248 | ..... 249 | ..... 250 | ..... 251 | 252 | z=2, w=-2 253 | ..... 254 | ..... 255 | ..#.. 256 | ..... 257 | ..... 258 | 259 | z=-2, w=-1 260 | ..... 261 | ..... 262 | ..... 263 | ..... 264 | ..... 265 | 266 | z=-1, w=-1 267 | ..... 268 | ..... 269 | ..... 270 | ..... 271 | ..... 272 | 273 | z=0, w=-1 274 | ..... 275 | ..... 276 | ..... 277 | ..... 278 | ..... 279 | 280 | z=1, w=-1 281 | ..... 282 | ..... 283 | ..... 284 | ..... 285 | ..... 286 | 287 | z=2, w=-1 288 | ..... 289 | ..... 290 | ..... 291 | ..... 292 | ..... 293 | 294 | z=-2, w=0 295 | ###.. 296 | ##.## 297 | #...# 298 | .#..# 299 | .###. 300 | 301 | z=-1, w=0 302 | ..... 303 | ..... 304 | ..... 305 | ..... 306 | ..... 307 | 308 | z=0, w=0 309 | ..... 310 | ..... 311 | ..... 312 | ..... 313 | ..... 314 | 315 | z=1, w=0 316 | ..... 317 | ..... 318 | ..... 319 | ..... 320 | ..... 321 | 322 | z=2, w=0 323 | ###.. 324 | ##.## 325 | #...# 326 | .#..# 327 | .###. 328 | 329 | z=-2, w=1 330 | ..... 331 | ..... 332 | ..... 333 | ..... 334 | ..... 335 | 336 | z=-1, w=1 337 | ..... 338 | ..... 339 | ..... 340 | ..... 341 | ..... 342 | 343 | z=0, w=1 344 | ..... 345 | ..... 346 | ..... 347 | ..... 348 | ..... 349 | 350 | z=1, w=1 351 | ..... 352 | ..... 353 | ..... 354 | ..... 355 | ..... 356 | 357 | z=2, w=1 358 | ..... 359 | ..... 360 | ..... 361 | ..... 362 | ..... 363 | 364 | z=-2, w=2 365 | ..... 366 | ..... 367 | ..#.. 368 | ..... 369 | ..... 370 | 371 | z=-1, w=2 372 | ..... 373 | ..... 374 | ..... 375 | ..... 376 | ..... 377 | 378 | z=0, w=2 379 | ###.. 380 | ##.## 381 | #...# 382 | .#..# 383 | .###. 384 | 385 | z=1, w=2 386 | ..... 387 | ..... 388 | ..... 389 | ..... 390 | ..... 391 | 392 | z=2, w=2 393 | ..... 394 | ..... 395 | ..#.. 396 | ..... 397 | ..... 398 | ``` 399 | 400 | After the full six-cycle boot process completes, **`848`** cubes are left in the **active** state. 401 | 402 | Starting with your given initial configuration, simulate six cycles in a 4-dimensional space. **How many cubes are left in the active state after the sixth cycle?** 403 | 404 | ## References 405 | - https://adventofcode.com/2020/day/17 406 | -------------------------------------------------------------------------------- /day-17-conway-cubes/cubes.js: -------------------------------------------------------------------------------- 1 | module.exports = (input, cycles = 6) => { 2 | let cubes = input.split('\n').reduce((state, line, y) => { 3 | line.trim().split('').forEach((cube, x) => { 4 | if (cube === '#') { state.add(`${x},${y},0`); } 5 | }); 6 | 7 | return state; 8 | }, new Set()); 9 | 10 | const getNeighbors = (xyz) => { 11 | const neighbors = []; 12 | const [x, y, z] = xyz.split(',').map(Number); 13 | 14 | for (let vz = -1; vz <= 1; vz++) { 15 | for (let vy = -1; vy <= 1; vy++) { 16 | for (let vx = -1; vx <= 1; vx++) { 17 | neighbors.push(`${x + vx},${y + vy},${z + vz}`); 18 | } 19 | } 20 | } 21 | 22 | return neighbors; 23 | }; 24 | 25 | const cycle = (state) => { 26 | const neighbors = [...state.values()].reduce((cubes, key) => { 27 | getNeighbors(key).forEach((cube) => cubes.add(cube)); 28 | 29 | return cubes; 30 | }, new Set()); 31 | 32 | return [...neighbors.values()].reduce((cubes, key) => { 33 | const numberOfNeighbors = getNeighbors(key).reduce((count, cube) => { 34 | return state.has(cube) && cube !== key ? count + 1 : count; 35 | }, 0); 36 | 37 | if (state.has(key) && numberOfNeighbors >= 2 && numberOfNeighbors <= 3) { 38 | cubes.add(key); 39 | } else if (!state.has(key) && numberOfNeighbors === 3) { 40 | cubes.add(key); 41 | } 42 | 43 | return cubes; 44 | }, new Set()); 45 | }; 46 | 47 | for (let i = 0; i < cycles; i++) { 48 | cubes = cycle(cubes); 49 | } 50 | 51 | return cubes.size; 52 | }; 53 | -------------------------------------------------------------------------------- /day-17-conway-cubes/cubes2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input, cycles = 6) => { 2 | let cubes = input.split('\n').reduce((state, line, y) => { 3 | line.trim().split('').forEach((cube, x) => { 4 | if (cube === '#') { state.add(`${x},${y},0,0`); } 5 | }); 6 | 7 | return state; 8 | }, new Set()); 9 | 10 | const getNeighbors = (xyzw) => { 11 | const neighbors = []; 12 | const [x, y, z, w] = xyzw.split(',').map(Number); 13 | 14 | for (let vz = -1; vz <= 1; vz++) { 15 | for (let vy = -1; vy <= 1; vy++) { 16 | for (let vx = -1; vx <= 1; vx++) { 17 | for (let vw = -1; vw <= 1; vw++) { 18 | neighbors.push(`${x + vx},${y + vy},${z + vz},${w + vw}`); 19 | } 20 | } 21 | } 22 | } 23 | 24 | return neighbors; 25 | }; 26 | 27 | const cycle = (state) => { 28 | const neighbors = [...state.values()].reduce((cubes, key) => { 29 | getNeighbors(key).forEach((cube) => cubes.add(cube)); 30 | 31 | return cubes; 32 | }, new Set()); 33 | 34 | return [...neighbors.values()].reduce((cubes, key) => { 35 | const numberOfNeighbors = getNeighbors(key).reduce((count, cube) => { 36 | return state.has(cube) && cube !== key ? count + 1 : count; 37 | }, 0); 38 | 39 | if (state.has(key) && numberOfNeighbors >= 2 && numberOfNeighbors <= 3) { 40 | cubes.add(key); 41 | } else if (!state.has(key) && numberOfNeighbors === 3) { 42 | cubes.add(key); 43 | } 44 | 45 | return cubes; 46 | }, new Set()); 47 | }; 48 | 49 | for (let i = 0; i < cycles; i++) { 50 | cubes = cycle(cubes); 51 | } 52 | 53 | return cubes.size; 54 | }; 55 | -------------------------------------------------------------------------------- /day-17-conway-cubes/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const cubes = require('./cubes'); 4 | const cubes2 = require('./cubes2'); 5 | 6 | describe('Day 17: Conway Cubes', () => { 7 | it('should calculate how many cubes are left after six cycles', () => { 8 | const input = 9 | `.#. 10 | ..# 11 | ###`; 12 | 13 | assert.strictEqual(cubes(input), 112); 14 | }); 15 | 16 | describe('Part Two', () => { 17 | it('should calculate how many cubes are left after six cycles (4 dimensions)', () => { 18 | const input = 19 | `.#. 20 | ..# 21 | ###`; 22 | 23 | assert.strictEqual(cubes2(input), 848); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /day-18-operation-order/README.md: -------------------------------------------------------------------------------- 1 | # Day 18: Operation Order 2 | 3 | As you look out the window and notice a heavily-forested continent slowly appear over the horizon, you are interrupted by the child sitting next to you. They're curious if you could help them with their math homework. 4 | 5 | Unfortunately, it seems like this "math" [follows different rules](https://www.youtube.com/watch?v=3QtRK7Y2pPU&t=15) than you remember. 6 | 7 | The homework (your puzzle input) consists of a series of expressions that consist of addition (`+`), multiplication (`*`), and parentheses `((...))`. Just like normal math, parentheses indicate that the expression inside must be evaluated before it can be used by the surrounding expression. Addition still finds the sum of the numbers on both sides of the operator, and multiplication still finds the product. 8 | 9 | However, the rules of **operator precedence** have changed. Rather than evaluating multiplication before addition, the operators have the **same precedence**, and are evaluated left-to-right regardless of the order in which they appear. 10 | 11 | For example, the steps to evaluate the expression` 1 + 2 * 3 + 4 * 5 + 6` are as follows: 12 | 13 | ``` 14 | 1 + 2 * 3 + 4 * 5 + 6 15 | 3 * 3 + 4 * 5 + 6 16 | 9 + 4 * 5 + 6 17 | 13 * 5 + 6 18 | 65 + 6 19 | 71 20 | ``` 21 | 22 | Parentheses can override this order; for example, here is what happens if parentheses are added to form `1 + (2 * 3) + (4 * (5 + 6))`: 23 | 24 | ``` 25 | 1 + (2 * 3) + (4 * (5 + 6)) 26 | 1 + 6 + (4 * (5 + 6)) 27 | 7 + (4 * (5 + 6)) 28 | 7 + (4 * 11 ) 29 | 7 + 44 30 | 51 31 | ``` 32 | 33 | Here are a few more examples: 34 | 35 | - `2 * 3 + (4 * 5)` becomes **`26`**. 36 | - `5 + (8 * 3 + 9 + 3 * 4 * 3)` becomes **`437`**. 37 | - `5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))` becomes **`12240`**. 38 | - `((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2` becomes **`13632`**. 39 | 40 | Before you can help with the homework, you need to understand it yourself. **`Evaluate the expression on each line of the homework; what is the sum of the resulting values?`** 41 | 42 | ## Part Two 43 | 44 | You manage to answer the child's questions and they finish part 1 of their homework, but get stuck when they reach the next section: **advanced** math. 45 | 46 | Now, addition and multiplication have **different** precedence levels, but they're not the ones you're familiar with. Instead, addition is evaluated **before** multiplication. 47 | 48 | For example, the steps to evaluate the expression `1 + 2 * 3 + 4 * 5 + 6` are now as follows: 49 | 50 | ``` 51 | 1 + 2 * 3 + 4 * 5 + 6 52 | 3 * 3 + 4 * 5 + 6 53 | 3 * 7 * 5 + 6 54 | 3 * 7 * 11 55 | 21 * 11 56 | 231 57 | ``` 58 | 59 | Here are the other examples from above: 60 | 61 | - `1 + (2 * 3) + (4 * (5 + 6))` still becomes **`51`**. 62 | - `2 * 3 + (4 * 5)` becomes **`46`**. 63 | - `5 + (8 * 3 + 9 + 3 * 4 * 3)` becomes **`1445`**. 64 | - `5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))` becomes **`669060`**. 65 | - `((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2` becomes **`23340`**. 66 | 67 | **What do you get if you add up the results of evaluating the homework problems using these new rules?** 68 | 69 | ## References 70 | - https://adventofcode.com/2020/day/18 71 | -------------------------------------------------------------------------------- /day-18-operation-order/order.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const evaluate = (expression) => { 3 | const parts = expression.split(' '); 4 | let sum = parseInt(parts[0]); 5 | 6 | for (let i = 1; i < parts.length; i += 2) { 7 | const b = parseInt(parts[i + 1]); 8 | 9 | sum = parts[i] === '*' ? sum * b : sum + b; 10 | } 11 | 12 | return sum; 13 | }; 14 | 15 | const solve = (expression) => { 16 | const innerExpressionStart = expression.lastIndexOf('('); 17 | const innerExpressionEnd = expression.indexOf(')', innerExpressionStart); 18 | 19 | if (innerExpressionStart === -1) { 20 | const sum = evaluate(expression); 21 | 22 | return sum; 23 | } else { 24 | const left = expression.slice(0, innerExpressionStart); 25 | const right = expression.slice(innerExpressionEnd + 1); 26 | const innerExpression = expression.slice(innerExpressionStart + 1, innerExpressionEnd); 27 | const nextExpression = `${left}${evaluate(innerExpression)}${right}`; 28 | 29 | return solve(nextExpression); 30 | } 31 | }; 32 | 33 | const results = input.split('\n').map(solve); 34 | 35 | return results.reduce((a, b) => a + b, 0); 36 | }; 37 | -------------------------------------------------------------------------------- /day-18-operation-order/order2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const evaluate = (expression) => { 3 | return expression.split(' * ').reduce((sum, part) => { 4 | return sum * part.split(' + ').reduce((total, x) => total + +x, 0); 5 | }, 1); 6 | }; 7 | 8 | const solve = (expression) => { 9 | const innerExpressionStart = expression.lastIndexOf('('); 10 | const innerExpressionEnd = expression.indexOf(')', innerExpressionStart); 11 | 12 | if (innerExpressionStart === -1) { 13 | const sum = evaluate(expression); 14 | 15 | return sum; 16 | } else { 17 | const left = expression.slice(0, innerExpressionStart); 18 | const right = expression.slice(innerExpressionEnd + 1); 19 | const innerExpression = expression.slice(innerExpressionStart + 1, innerExpressionEnd); 20 | const nextExpression = `${left}${evaluate(innerExpression)}${right}`; 21 | 22 | return solve(nextExpression); 23 | } 24 | }; 25 | 26 | const results = input.split('\n').map(solve); 27 | 28 | return results.reduce((a, b) => a + b, 0); 29 | }; 30 | -------------------------------------------------------------------------------- /day-18-operation-order/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const order = require('./order'); 4 | const order2 = require('./order2'); 5 | 6 | describe('Day 18: Operation Order', () => { 7 | it('should solve expression #1', () => { 8 | assert.strictEqual(order('1 + 2 * 3 + 4 * 5 + 6'), 71); 9 | }); 10 | 11 | it('should solve expression #2', () => { 12 | assert.strictEqual(order('1 + (2 * 3) + (4 * (5 + 6))'), 51); 13 | }); 14 | 15 | it('should solve expression #3', () => { 16 | assert.strictEqual(order('2 * 3 + (4 * 5)'), 26); 17 | }); 18 | 19 | it('should solve expression #4', () => { 20 | assert.strictEqual(order('5 + (8 * 3 + 9 + 3 * 4 * 3)'), 437); 21 | }); 22 | 23 | it('should solve expression #5', () => { 24 | assert.strictEqual(order('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))'), 12240); 25 | }); 26 | 27 | it('should solve expression #6', () => { 28 | assert.strictEqual(order('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2'), 13632); 29 | }); 30 | 31 | describe('Part Two', () => { 32 | it('should solve advanced expression #1', () => { 33 | assert.strictEqual(order2('1 + 2 * 3 + 4 * 5 + 6'), 231); 34 | }); 35 | 36 | it('should solve advanced expression #2', () => { 37 | assert.strictEqual(order2('1 + (2 * 3) + (4 * (5 + 6))'), 51); 38 | }); 39 | 40 | it('should solve advanced expression #3', () => { 41 | assert.strictEqual(order2('2 * 3 + (4 * 5)'), 46); 42 | }); 43 | 44 | it('should solve advanced expression #4', () => { 45 | assert.strictEqual(order2('5 + (8 * 3 + 9 + 3 * 4 * 3)'), 1445); 46 | }); 47 | 48 | it('should solve advanced expression #5', () => { 49 | assert.strictEqual(order2('5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))'), 669060); 50 | }); 51 | 52 | it('should solve advanced expression #6', () => { 53 | assert.strictEqual(order2('((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2'), 23340); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /day-19-monster-messages/README.md: -------------------------------------------------------------------------------- 1 | # Day 19: Monster Messages 2 | 3 | You land in an airport surrounded by dense forest. As you walk to your high-speed train, the Elves at the Mythical Information Bureau contact you again. They think their satellite has collected an image of a **sea monster**! Unfortunately, the connection to the satellite is having problems, and many of the messages sent back from the satellite have been corrupted. 4 | 5 | They sent you a list of **the rules valid messages should obey** and a list of **received messages** they've collected so far (your puzzle input). 6 | 7 | The **rules for valid messages** (the top part of your puzzle input) are numbered and build upon each other. For example: 8 | 9 | ``` 10 | 0: 1 2 11 | 1: "a" 12 | 2: 1 3 | 3 1 13 | 3: "b" 14 | ``` 15 | 16 | Some rules, like `3: "b"`, simply match a single character (in this case, `b`). 17 | 18 | The remaining rules list the sub-rules that must be followed; for example, the rule `0: 1 2` means that to match rule `0`, the text being checked must match rule `1`, and the text after the part that matched rule `1` must then match rule `2`. 19 | 20 | Some of the rules have multiple lists of sub-rules separated by a pipe (`|`). This means that **at least one** list of sub-rules must match. (The ones that match might be different each time the rule is encountered.) For example, the rule `2: 1 3 | 3 1` means that to match rule `2`, the text being checked must match rule `1` followed by rule `3` **or** it must match rule `3` followed by rule `1`. 21 | 22 | Fortunately, there are no loops in the rules, so the list of possible matches will be finite. Since rule `1` matches `a` and rule `3` matches `b`, rule `2` matches either `ab` or `ba`. Therefore, rule `0` matches `aab` or `aba`. 23 | 24 | Here's a more interesting example: 25 | 26 | ``` 27 | 0: 4 1 5 28 | 1: 2 3 | 3 2 29 | 2: 4 4 | 5 5 30 | 3: 4 5 | 5 4 31 | 4: "a" 32 | 5: "b" 33 | ``` 34 | 35 | Here, because rule `4` matches `a` and rule `5` matches `b`, rule `2` matches two letters that are the same (`aa` or `bb`), and rule `3` matches two letters that are different (`ab` or `ba`). 36 | 37 | Since rule `1` matches rules `2` and `3` once each in either order, it must match two pairs of letters, one pair with matching letters and one pair with different letters. This leaves eight possibilities: `aaab`, `aaba`, `bbab`, `bbba`, `abaa`, `abbb`, `baaa`, or `babb`. 38 | 39 | Rule `0`, therefore, matches `a` (rule `4`), then any of the eight options from rule `1`, then `b` (rule `5`): `aaaabb`, `aaabab`, `abbabb`, `abbbab`, `aabaab`, `aabbbb`, `abaaab`, or `ababbb`. 40 | 41 | The **received messages** (the bottom part of your puzzle input) need to be checked against the rules so you can determine which are valid and which are corrupted. Including the rules and the messages together, this might look like: 42 | 43 | ``` 44 | 0: 4 1 5 45 | 1: 2 3 | 3 2 46 | 2: 4 4 | 5 5 47 | 3: 4 5 | 5 4 48 | 4: "a" 49 | 5: "b" 50 | 51 | ababbb 52 | bababa 53 | abbbab 54 | aaabbb 55 | aaaabbb 56 | ``` 57 | 58 | Your goal is to determine **the number of messages that completely match rule `0`**. In the above example, `ababbb` and `abbbab` match, but `bababa`, `aaabbb`, and `aaaabbb` do not, producing the answer **`2`**. The whole message must match all of rule `0`; there can't be extra unmatched characters in the message. (For example, `aaaabbb` might appear to match rule `0` above, but it has an extra unmatched `b` on the end.) 59 | 60 | **How many messages completely match rule `0`?** 61 | 62 | ## Part Two 63 | 64 | As you look over the list of messages, you realize your matching rules aren't quite right. To fix them, completely replace rules `8: 42` and `11: 42 31` with the following: 65 | 66 | ``` 67 | 8: 42 | 42 8 68 | 11: 42 31 | 42 11 31 69 | ``` 70 | 71 | This small change has a big impact: now, the rules **do** contain loops, and the list of messages they could hypothetically match is infinite. You'll need to determine how these changes affect which messages are valid. 72 | 73 | Fortunately, many of the rules are unaffected by this change; it might help to start by looking at which rules always match the same set of values and how **those** rules (especially rules `42` and `31`) are used by the new versions of rules `8` and `11`. 74 | 75 | (Remember, **you only need to handle the rules you have**; building a solution that could handle any hypothetical combination of rules would be [significantly more difficult](https://en.wikipedia.org/wiki/Formal_grammar).) 76 | 77 | For example: 78 | 79 | ``` 80 | 42: 9 14 | 10 1 81 | 9: 14 27 | 1 26 82 | 10: 23 14 | 28 1 83 | 1: "a" 84 | 11: 42 31 85 | 5: 1 14 | 15 1 86 | 19: 14 1 | 14 14 87 | 12: 24 14 | 19 1 88 | 16: 15 1 | 14 14 89 | 31: 14 17 | 1 13 90 | 6: 14 14 | 1 14 91 | 2: 1 24 | 14 4 92 | 0: 8 11 93 | 13: 14 3 | 1 12 94 | 15: 1 | 14 95 | 17: 14 2 | 1 7 96 | 23: 25 1 | 22 14 97 | 28: 16 1 98 | 4: 1 1 99 | 20: 14 14 | 1 15 100 | 3: 5 14 | 16 1 101 | 27: 1 6 | 14 18 102 | 14: "b" 103 | 21: 14 1 | 1 14 104 | 25: 1 1 | 1 14 105 | 22: 14 14 106 | 8: 42 107 | 26: 14 22 | 1 20 108 | 18: 15 15 109 | 7: 14 5 | 1 21 110 | 24: 14 1 111 | 112 | abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa 113 | bbabbbbaabaabba 114 | babbbbaabbbbbabbbbbbaabaaabaaa 115 | aaabbbbbbaaaabaababaabababbabaaabbababababaaa 116 | bbbbbbbaaaabbbbaaabbabaaa 117 | bbbababbbbaaaaaaaabbababaaababaabab 118 | ababaaaaaabaaab 119 | ababaaaaabbbaba 120 | baabbaaaabbaaaababbaababb 121 | abbbbabbbbaaaababbbbbbaaaababb 122 | aaaaabbaabaaaaababaa 123 | aaaabbaaaabbaaa 124 | aaaabbaabbaaaaaaabbbabbbaaabbaabaaa 125 | babaaabbbaaabaababbaabababaaab 126 | aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba 127 | ``` 128 | 129 | Without updating rules `8` and `11`, these rules only match three messages: `bbabbbbaabaabba`, `ababaaaaaabaaab`, and `ababaaaaabbbaba`. 130 | 131 | However, after updating rules `8` and `11`, a total of **`12`** messages match: 132 | 133 | - `bbabbbbaabaabba` 134 | - `babbbbaabbbbbabbbbbbaabaaabaaa` 135 | - `aaabbbbbbaaaabaababaabababbabaaabbababababaaa` 136 | - `bbbbbbbaaaabbbbaaabbabaaa` 137 | - `bbbababbbbaaaaaaaabbababaaababaabab` 138 | - `ababaaaaaabaaab` 139 | - `ababaaaaabbbaba` 140 | - `baabbaaaabbaaaababbaababb` 141 | - `abbbbabbbbaaaababbbbbbaaaababb` 142 | - `aaaaabbaabaaaaababaa` 143 | - `aaaabbaabbaaaaaaabbbabbbaaabbaabaaa` 144 | - `aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba` 145 | 146 | **After updating rules `8` and `11`, how many messages completely match rule `0`?** 147 | 148 | ## References 149 | - https://adventofcode.com/2020/day/19 150 | -------------------------------------------------------------------------------- /day-19-monster-messages/message.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const parts = input.split(/\n{2,}/); 3 | const messages = parts[1].split('\n').map((line) => line.trim()); 4 | const rules = parts[0].split('\n').reduce((rules, line) => { 5 | const [, number, rule] = /(\d+): (.*)/.exec(line.trim()); 6 | 7 | rules.set(number, rule.split(' | ').map((r) => r.split(' '))); 8 | 9 | return rules; 10 | }, new Map()); 11 | 12 | const compose = (number) => { 13 | const rule = rules.get(number); 14 | 15 | return !/\d+/.test(rule[0][0]) 16 | ? rule[0][0][1] 17 | : `(?:${rule.map((part) => part.map((x) => compose(x)).join('')).join('|')})`; 18 | }; 19 | 20 | const regex = new RegExp(`^${compose('0')}$`); 21 | 22 | return messages.filter((message) => regex.test(message)).length; 23 | }; 24 | -------------------------------------------------------------------------------- /day-19-monster-messages/message2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const parts = input.split(/\n{2,}/); 3 | const messages = parts[1].split('\n').map((line) => line.trim()); 4 | const rules = parts[0].split('\n').reduce((rules, line) => { 5 | const [, number, rule] = /(\d+): (.*)/.exec(line.trim()); 6 | 7 | rules.set(number, rule.split(' | ').map((r) => r.split(' '))); 8 | 9 | return rules; 10 | }, new Map()); 11 | 12 | // patch in rules with loops 13 | rules.set('8', [['42'], ['42', '8']]); 14 | 15 | // HACK: we're manually expanding the rule enough to pass the input and test 16 | // rules.set('11', [['42', '31'], ['42', '11', '31']]); 17 | rules.set('11', [ 18 | ['42', '31'], 19 | ['42', '42', '31', '31'], 20 | ['42', '42', '42', '31', '31', '31'], 21 | ['42', '42', '42', '42', '31', '31', '31', '31'], 22 | ]); 23 | 24 | const compose = (number) => { 25 | const rule = rules.get(number); 26 | 27 | if (!/\d+/.test(rule[0][0])) { 28 | return rule[0][0][1]; 29 | } 30 | 31 | const inner = rule 32 | .map((part) => { 33 | return part.map((x, i, n) => { 34 | if (x === number) { 35 | // instead of expanding rule 11 by hand, the following should 36 | // expand both sub-expressions n-times, but it does not work... 🤔 37 | // RE: context-free-grammar, pumping lemma, nested reference 38 | // https://stackoverflow.com/questions/133601/can-regular-expressions-be-used-to-match-nested-patterns 39 | // https://stackoverflow.com/questions/3644266/how-can-we-match-an-bn/3644267#3644267 40 | // if (n[i + 1]) { 41 | // return `${compose(n[i - 1])}+${compose(n[i + 1])}+`; 42 | // } 43 | 44 | return `${compose(n[i - 1])}+`; 45 | } 46 | 47 | return compose(x); 48 | }).join(''); 49 | }); 50 | 51 | return `(?:${inner.join('|')})`; 52 | }; 53 | 54 | const regex = new RegExp(`^${compose('0')}$`); 55 | 56 | return messages.filter((message) => regex.test(message)).length; 57 | }; 58 | -------------------------------------------------------------------------------- /day-19-monster-messages/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const message = require('./message'); 4 | const message2 = require('./message2'); 5 | 6 | describe('Day 19: Monster Messages', () => { 7 | it('should validate messages', () => { 8 | const input = 9 | `0: 4 1 5 10 | 1: 2 3 | 3 2 11 | 2: 4 4 | 5 5 12 | 3: 4 5 | 5 4 13 | 4: "a" 14 | 5: "b" 15 | 16 | ababbb 17 | bababa 18 | abbbab 19 | aaabbb 20 | aaaabbb`; 21 | 22 | assert.strictEqual(message(input), 2); 23 | }); 24 | 25 | describe('Part Two', () => { 26 | it('should validate messages with loops', () => { 27 | const input = 28 | `42: 9 14 | 10 1 29 | 9: 14 27 | 1 26 30 | 10: 23 14 | 28 1 31 | 1: "a" 32 | 11: 42 31 33 | 5: 1 14 | 15 1 34 | 19: 14 1 | 14 14 35 | 12: 24 14 | 19 1 36 | 16: 15 1 | 14 14 37 | 31: 14 17 | 1 13 38 | 6: 14 14 | 1 14 39 | 2: 1 24 | 14 4 40 | 0: 8 11 41 | 13: 14 3 | 1 12 42 | 15: 1 | 14 43 | 17: 14 2 | 1 7 44 | 23: 25 1 | 22 14 45 | 28: 16 1 46 | 4: 1 1 47 | 20: 14 14 | 1 15 48 | 3: 5 14 | 16 1 49 | 27: 1 6 | 14 18 50 | 14: "b" 51 | 21: 14 1 | 1 14 52 | 25: 1 1 | 1 14 53 | 22: 14 14 54 | 8: 42 55 | 26: 14 22 | 1 20 56 | 18: 15 15 57 | 7: 14 5 | 1 21 58 | 24: 14 1 59 | 60 | abbbbbabbbaaaababbaabbbbabababbbabbbbbbabaaaa 61 | bbabbbbaabaabba 62 | babbbbaabbbbbabbbbbbaabaaabaaa 63 | aaabbbbbbaaaabaababaabababbabaaabbababababaaa 64 | bbbbbbbaaaabbbbaaabbabaaa 65 | bbbababbbbaaaaaaaabbababaaababaabab 66 | ababaaaaaabaaab 67 | ababaaaaabbbaba 68 | baabbaaaabbaaaababbaababb 69 | abbbbabbbbaaaababbbbbbaaaababb 70 | aaaaabbaabaaaaababaa 71 | aaaabbaaaabbaaa 72 | aaaabbaabbaaaaaaabbbabbbaaabbaabaaa 73 | babaaabbbaaabaababbaabababaaab 74 | aabbbbbaabbbaaaaaabbbbbababaaaaabbaaabba`; 75 | 76 | assert.strictEqual(message2(input), 12); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /day-21-allergen-assessment/README.md: -------------------------------------------------------------------------------- 1 | # Day 21: Allergen Assessment 2 | 3 | You reach the train's last stop and the closest you can get to your vacation island without getting wet. There aren't even any boats here, but nothing can stop you now: you build a raft. You just need a few days' worth of food for your journey. 4 | 5 | You don't speak the local language, so you can't read any ingredients lists. However, sometimes, allergens are listed in a language you **do** understand. You should be able to use this information to determine which ingredient contains which allergen and work out which foods are safe to take with you on your trip. 6 | 7 | You start by compiling a list of foods (your puzzle input), one food per line. Each line includes that food's **ingredients list** followed by some or all of the allergens the food contains. 8 | 9 | Each allergen is found in exactly one ingredient. Each ingredient contains zero or one allergen. **Allergens aren't always marked**; when they're listed (as in (`contains nuts, shellfish`) after an ingredients list), the ingredient that contains each listed allergen will be **somewhere in the corresponding ingredients list**. However, even if an allergen isn't listed, the ingredient that contains that allergen could still be present: maybe they forgot to label it, or maybe it was labeled in a language you don't know. 10 | 11 | For example, consider the following list of foods: 12 | 13 | ``` 14 | mxmxvkd kfcds sqjhc nhms (contains dairy, fish) 15 | trh fvjkl sbzzf mxmxvkd (contains dairy) 16 | sqjhc fvjkl (contains soy) 17 | sqjhc mxmxvkd sbzzf (contains fish) 18 | ``` 19 | 20 | The first food in the list has four ingredients (written in a language you don't understand): `mxmxvkd`, `kfcds`, `sqjhc`, and `nhms`. While the food might contain other allergens, a few allergens the food definitely contains are listed afterward: `dairy` and `fish`. 21 | 22 | The first step is to determine which ingredients **can't possibly** contain any of the allergens in any food in your list. In the above example, none of the ingredients `kfcds`, `nhms`, `sbzzf`, or `trh` can contain an allergen. Counting the number of times any of these ingredients appear in any ingredients list produces **`5`**: they all appear once each except `sbzzf`, which appears twice. 23 | 24 | Determine which ingredients cannot possibly contain any of the allergens in your list. **How many times do any of those ingredients appear?** 25 | 26 | ## Part Two 27 | 28 | Now that you've isolated the inert ingredients, you should have enough information to figure out which ingredient contains which allergen. 29 | 30 | In the above example: 31 | 32 | - `mxmxvkd` contains `dairy`. 33 | - `sqjhc` contains `fish`. 34 | - `fvjkl` contains `soy`. 35 | 36 | Arrange the ingredients **alphabetically by their allergen** and separate them by commas to produce your **canonical dangerous ingredient list**. (There should **not be any spaces** in your canonical dangerous ingredient list.) In the above example, this would be **`mxmxvkd,sqjhc,fvjkl`**. 37 | 38 | Time to stock your raft with supplies. **What is your canonical dangerous ingredient list?** 39 | 40 | ## References 41 | - https://adventofcode.com/2020/day/21 42 | -------------------------------------------------------------------------------- /day-21-allergen-assessment/allergen-assessment.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const foods = input.split('\n').map((line) => { 3 | const ingredients = new Set(/(.*) \(.*\)/.exec(line.trim())[1].split(' ')); 4 | const allergens = new Set(/.* \(contains (.*)\)/.exec(line.trim())[1].split(', ')); 5 | 6 | return { 7 | ingredients, 8 | allergens, 9 | }; 10 | }); 11 | 12 | const uniqueIngredients = new Set(foods.reduce((map, { ingredients }) => map.concat(...ingredients), [])); 13 | const uniqueAllergens = new Set(foods.reduce((map, { allergens }) => map.concat(...allergens), [])); 14 | const allergenExclusionMap = new Map([...uniqueIngredients].map((ingredient) => [ingredient, new Set()])); 15 | const ingredientCounts = new Map([...uniqueIngredients].map((ingredient) => [ingredient, 0])); 16 | 17 | for (let i = 0; i < foods.length; i++) { 18 | const { ingredients, allergens } = foods[i]; 19 | const otherFoods = [...foods.slice(0, i), ...foods.slice(i + 1)]; 20 | 21 | [...allergens].forEach((allergen) => { 22 | otherFoods.forEach(({ ingredients: otherIngredients }) => { 23 | [...otherIngredients] 24 | .forEach((otherIngredient) => { 25 | if (!ingredients.has(otherIngredient)) { 26 | const ingredientExclusions = allergenExclusionMap.get(otherIngredient); 27 | 28 | if (!ingredientExclusions.has(allergen)) { 29 | ingredientExclusions.add(allergen); 30 | } 31 | } 32 | }); 33 | }); 34 | }); 35 | } 36 | 37 | foods.forEach(({ ingredients }) => { 38 | ingredients.forEach((ingredient) => { 39 | ingredientCounts.set(ingredient, ingredientCounts.get(ingredient) + 1); 40 | }); 41 | }); 42 | 43 | const ingredientsWithoutAllergens = [...allergenExclusionMap.entries()] 44 | .reduce((count, [ingredient, allergens]) => { 45 | return count + (allergens.size === uniqueAllergens.size ? ingredientCounts.get(ingredient) : 0); 46 | }, 0); 47 | 48 | return ingredientsWithoutAllergens; 49 | }; 50 | -------------------------------------------------------------------------------- /day-21-allergen-assessment/allergen-assessment2.js: -------------------------------------------------------------------------------- 1 | module.exports = (input) => { 2 | const foods = input.split('\n').map((line) => { 3 | const ingredients = new Set(/(.*) \(.*\)/.exec(line.trim())[1].split(' ')); 4 | const allergens = new Set(/.* \(contains (.*)\)/.exec(line.trim())[1].split(', ')); 5 | 6 | return { 7 | ingredients, 8 | allergens, 9 | }; 10 | }); 11 | 12 | const uniqueIngredients = new Set(foods.reduce((map, { ingredients }) => map.concat(...ingredients), [])); 13 | const uniqueAllergens = new Set(foods.reduce((map, { allergens }) => map.concat(...allergens), [])); 14 | const allergenExclusionMap = new Map([...uniqueIngredients].map((ingredient) => [ingredient, new Set()])); 15 | const ingredientCounts = new Map([...uniqueIngredients].map((ingredient) => [ingredient, 0])); 16 | 17 | for (let i = 0; i < foods.length; i++) { 18 | const { ingredients, allergens } = foods[i]; 19 | const otherFoods = [...foods.slice(0, i), ...foods.slice(i + 1)]; 20 | 21 | [...allergens].forEach((allergen) => { 22 | otherFoods.forEach(({ ingredients: otherIngredients }) => { 23 | [...otherIngredients] 24 | .forEach((otherIngredient) => { 25 | if (!ingredients.has(otherIngredient)) { 26 | const ingredientExclusions = allergenExclusionMap.get(otherIngredient); 27 | 28 | if (!ingredientExclusions.has(allergen)) { 29 | ingredientExclusions.add(allergen); 30 | } 31 | } 32 | }); 33 | }); 34 | }); 35 | } 36 | 37 | foods.forEach(({ ingredients }) => { 38 | ingredients.forEach((ingredient) => { 39 | ingredientCounts.set(ingredient, ingredientCounts.get(ingredient) + 1); 40 | }); 41 | }); 42 | 43 | const allergenSort = (a, b) => b[1].size - a[1].size; 44 | 45 | const ingredientsAndAllergensTheyMayContain = [...allergenExclusionMap.entries()] 46 | .reduce((map, [ingredient, allergensTheyDontContain]) => { 47 | const allergensTheyMayContain = [...uniqueAllergens].filter((allergen) => { 48 | return !allergensTheyDontContain.has(allergen); 49 | }); 50 | 51 | if (allergensTheyMayContain.length) { 52 | map.push([ingredient, new Set(allergensTheyMayContain)]); 53 | } 54 | 55 | return map; 56 | }, []); 57 | 58 | const pairs = []; 59 | 60 | while (ingredientsAndAllergensTheyMayContain.length) { 61 | ingredientsAndAllergensTheyMayContain.sort(allergenSort); 62 | 63 | const [ingredient, allergens] = ingredientsAndAllergensTheyMayContain.pop(); 64 | const identifiedAllergen = [...allergens][0]; 65 | 66 | pairs.push([ingredient, identifiedAllergen]); 67 | ingredientsAndAllergensTheyMayContain 68 | .forEach(([, allergens]) => allergens.delete(identifiedAllergen)); 69 | } 70 | 71 | return pairs 72 | .sort((a, b) => a[1].localeCompare(b[1])) 73 | .map(([ingredient]) => ingredient).join(','); 74 | }; 75 | -------------------------------------------------------------------------------- /day-21-allergen-assessment/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const allergenAssessment = require('./allergen-assessment'); 4 | const allergenAssessment2 = require('./allergen-assessment2'); 5 | 6 | describe('Day 21: Allergen Assessment', () => { 7 | it('should count ingredients without allergens', () => { 8 | const input = 9 | `mxmxvkd kfcds sqjhc nhms (contains dairy, fish) 10 | trh fvjkl sbzzf mxmxvkd (contains dairy) 11 | sqjhc fvjkl (contains soy) 12 | sqjhc mxmxvkd sbzzf (contains fish)`; 13 | 14 | assert.strictEqual(allergenAssessment(input), 5); 15 | }); 16 | 17 | describe('Part Two', () => { 18 | it('should output canonical dangerous ingredient list', () => { 19 | const input = 20 | `mxmxvkd kfcds sqjhc nhms (contains dairy, fish) 21 | trh fvjkl sbzzf mxmxvkd (contains dairy) 22 | sqjhc fvjkl (contains soy) 23 | sqjhc mxmxvkd sbzzf (contains fish)`; 24 | 25 | assert.strictEqual(allergenAssessment2(input), 'mxmxvkd,sqjhc,fvjkl'); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /day-22-crab-combat/README.md: -------------------------------------------------------------------------------- 1 | # Day 22: Crab Combat 2 | 3 | It only takes a few hours of sailing the ocean on a raft for boredom to sink in. Fortunately, you brought a small deck of [space cards](https://adventofcode.com/2019/day/22)! You'd like to play a game of **Combat**, and there's even an opponent available: a small crab that climbed aboard your raft before you left. 4 | 5 | Fortunately, it doesn't take long to teach the crab the rules. 6 | 7 | Before the game starts, split the cards so each player has their own deck (your puzzle input). Then, the game consists of a series of rounds: both players draw their top card, and the player with the higher-valued card wins the **round**. The winner keeps both cards, placing them on the bottom of their own deck so that the winner's card is above the other card. If this causes a player to have all of the cards, they win, and the game ends. 8 | 9 | For example, consider the following starting decks: 10 | 11 | ``` 12 | Player 1: 13 | 9 14 | 2 15 | 6 16 | 3 17 | 1 18 | 19 | Player 2: 20 | 5 21 | 8 22 | 4 23 | 7 24 | 10 25 | ``` 26 | 27 | This arrangement means that player 1's deck contains 5 cards, with `9` on top and `1` on the bottom; player 2's deck also contains 5 cards, with `5` on top and `10` on the bottom. 28 | 29 | The first round begins with both players drawing the top card of their decks: `9` and `5`. Player 1 has the higher card, so both cards move to the bottom of player 1's deck such that `9` is above `5`. In total, it takes 29 rounds before a player has all of the cards: 30 | 31 | ``` 32 | -- Round 1 -- 33 | Player 1's deck: 9, 2, 6, 3, 1 34 | Player 2's deck: 5, 8, 4, 7, 10 35 | Player 1 plays: 9 36 | Player 2 plays: 5 37 | Player 1 wins the round! 38 | 39 | -- Round 2 -- 40 | Player 1's deck: 2, 6, 3, 1, 9, 5 41 | Player 2's deck: 8, 4, 7, 10 42 | Player 1 plays: 2 43 | Player 2 plays: 8 44 | Player 2 wins the round! 45 | 46 | -- Round 3 -- 47 | Player 1's deck: 6, 3, 1, 9, 5 48 | Player 2's deck: 4, 7, 10, 8, 2 49 | Player 1 plays: 6 50 | Player 2 plays: 4 51 | Player 1 wins the round! 52 | 53 | -- Round 4 -- 54 | Player 1's deck: 3, 1, 9, 5, 6, 4 55 | Player 2's deck: 7, 10, 8, 2 56 | Player 1 plays: 3 57 | Player 2 plays: 7 58 | Player 2 wins the round! 59 | 60 | -- Round 5 -- 61 | Player 1's deck: 1, 9, 5, 6, 4 62 | Player 2's deck: 10, 8, 2, 7, 3 63 | Player 1 plays: 1 64 | Player 2 plays: 10 65 | Player 2 wins the round! 66 | 67 | ...several more rounds pass... 68 | 69 | -- Round 27 -- 70 | Player 1's deck: 5, 4, 1 71 | Player 2's deck: 8, 9, 7, 3, 2, 10, 6 72 | Player 1 plays: 5 73 | Player 2 plays: 8 74 | Player 2 wins the round! 75 | 76 | -- Round 28 -- 77 | Player 1's deck: 4, 1 78 | Player 2's deck: 9, 7, 3, 2, 10, 6, 8, 5 79 | Player 1 plays: 4 80 | Player 2 plays: 9 81 | Player 2 wins the round! 82 | 83 | -- Round 29 -- 84 | Player 1's deck: 1 85 | Player 2's deck: 7, 3, 2, 10, 6, 8, 5, 9, 4 86 | Player 1 plays: 1 87 | Player 2 plays: 7 88 | Player 2 wins the round! 89 | 90 | 91 | == Post-game results == 92 | Player 1's deck: 93 | Player 2's deck: 3, 2, 10, 6, 8, 5, 9, 4, 7, 1 94 | ``` 95 | 96 | Once the game ends, you can calculate the winning player's **score**. The bottom card in their deck is worth the value of the card multiplied by 1, the second-from-the-bottom card is worth the value of the card multiplied by 2, and so on. With 10 cards, the top card is worth the value on the card multiplied by 10. In this example, the winning player's score is: 97 | 98 | ``` 99 | 3 * 10 100 | + 2 * 9 101 | + 10 * 8 102 | + 6 * 7 103 | + 8 * 6 104 | + 5 * 5 105 | + 9 * 4 106 | + 4 * 3 107 | + 7 * 2 108 | + 1 * 1 109 | = 306 110 | ``` 111 | 112 | So, once the game ends, the winning player's score is **`306`**. 113 | 114 | Play the small crab in a game of Combat using the two decks you just dealt. **What is the winning player's score?** 115 | 116 | ## References 117 | - https://adventofcode.com/2020/day/22 118 | -------------------------------------------------------------------------------- /day-22-crab-combat/crab-combat.js: -------------------------------------------------------------------------------- 1 | const PRINT_COMBAT_TEXT = false; 2 | 3 | function print () { 4 | if (PRINT_COMBAT_TEXT) { 5 | const args = Array.from(arguments); 6 | 7 | console.log(...args); 8 | } 9 | } 10 | 11 | class Node { 12 | constructor (value) { 13 | this.value = value; 14 | this._previous = null; 15 | this._next = null; 16 | } 17 | } 18 | 19 | class LinkedList { 20 | constructor () { 21 | this.head = null; 22 | this.tail = null; 23 | this.size = 0; 24 | } 25 | 26 | addLast (value) { 27 | const newNode = new Node(value); 28 | 29 | if (this.tail) { 30 | newNode._previous = this.tail; 31 | this.tail._next = newNode; 32 | this.tail = newNode; 33 | } else { 34 | this.head = newNode; 35 | this.tail = newNode; 36 | } 37 | 38 | this.size++; 39 | } 40 | 41 | removeFirst () { 42 | if (!this.head) { 43 | return null; 44 | } 45 | 46 | const removedNode = this.head; 47 | 48 | if (this.head._next) { 49 | this.head = this.head._next; 50 | this.head._previous = null; 51 | } else { 52 | this.head = null; 53 | } 54 | 55 | this.size--; 56 | 57 | return removedNode.value; 58 | } 59 | } 60 | 61 | const linkedListToArray = (linkedList) => { 62 | const array = []; 63 | 64 | let node = linkedList.head; 65 | 66 | while (node !== null) { 67 | array.push(node.value); 68 | 69 | node = node._next; 70 | } 71 | 72 | return array; 73 | }; 74 | 75 | module.exports = (input) => { 76 | const decks = input.split('\n\n').map((cards) => { 77 | const deck = new LinkedList(); 78 | 79 | cards.split('\n').slice(1).forEach((card) => deck.addLast(+card)); 80 | 81 | return deck; 82 | }); 83 | 84 | let round = 1; 85 | 86 | while (decks[0].size && decks[1].size) { 87 | print(`-- Round ${round} --`); 88 | print(`Player 1's deck: ${linkedListToArray(decks[0]).join(', ')}`); 89 | print(`Player 2's deck: ${linkedListToArray(decks[1]).join(', ')}`); 90 | 91 | const card1 = decks[0].removeFirst(); 92 | const card2 = decks[1].removeFirst(); 93 | 94 | print(`Player 1 plays: ${card1}`); 95 | print(`Player 2 plays: ${card2}`); 96 | 97 | if (card1 > card2) { 98 | print('Player 1 wins the round!\n'); 99 | decks[0].addLast(card1); 100 | decks[0].addLast(card2); 101 | } else { 102 | print('Player 2 wins the round!\n'); 103 | decks[1].addLast(card2); 104 | decks[1].addLast(card1); 105 | } 106 | 107 | round++; 108 | } 109 | 110 | print('== Post-game results=='); 111 | print(`Player 1's deck: ${linkedListToArray(decks[0]).join(', ')}`); 112 | print(`Player 2's deck: ${linkedListToArray(decks[1]).join(', ')}`); 113 | 114 | const winningDeck = linkedListToArray(decks[0].size > 0 ? decks[0] : decks[1]); 115 | const winningScore = winningDeck.reverse().reduce((score, card, i) => score + card * (i + 1), 0); 116 | 117 | return winningScore; 118 | }; 119 | -------------------------------------------------------------------------------- /day-22-crab-combat/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | const crabCombat = require('./crab-combat'); 4 | 5 | describe('Day 22: Crab Combat', () => { 6 | it('should compute winning player\'s score', () => { 7 | const input = 8 | `Player 1: 9 | 9 10 | 2 11 | 6 12 | 3 13 | 1 14 | 15 | Player 2: 16 | 5 17 | 8 18 | 4 19 | 7 20 | 10`; 21 | 22 | assert.strictEqual(crabCombat(input), 306); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "advent-of-code-2020", 3 | "version": "1.0.0", 4 | "description": "Advent of Code 2020", 5 | "main": "index.js", 6 | "author": "Mario Tacke (https://www.mariotacke.io)", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "mocha day-*/**/test.js", 10 | "lint": "eslint ." 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/mariotacke/advent-of-code-2020.git" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^8.29.0", 18 | "mocha": "^10.1.0" 19 | } 20 | } 21 | --------------------------------------------------------------------------------