Compare commits
254 Commits
bd8c2cca31
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 68d1803e8f | |||
| a39f261ba9 | |||
| 0c77a62ab4 | |||
| 60ee8c0307 | |||
| dfe08db759 | |||
| 081f4ea295 | |||
| 34be9e0847 | |||
| 6ea67eac0c | |||
| bb366fbe17 | |||
| 681b7bae16 | |||
| c8ded5c42d | |||
| b37f1ec366 | |||
| 40e2e329e0 | |||
| fa5bf2e85b | |||
| ea1b57b17e | |||
| 174671e6f5 | |||
| 40bcf3052f | |||
| 1e634b7ee9 | |||
| 6e625dcf06 | |||
| 141216920d | |||
| b685e81c58 | |||
| 1adc10ea88 | |||
| db7c31cb39 | |||
| 1ad1da1309 | |||
| 228392fe83 | |||
| 4837cbf290 | |||
| 8503cee52b | |||
| b16b052115 | |||
| bc76283458 | |||
| 9d2a087801 | |||
| 0bd3b6dc69 | |||
| da81f67b7f | |||
| caa7da5a7d | |||
| eebe707ef9 | |||
| 8960dcb072 | |||
| 99eb0fbaa8 | |||
| a739425209 | |||
| af306eb73c | |||
| cdbc3f3b16 | |||
| ed445a8be7 | |||
| 8bbf6662b1 | |||
| b60c971d50 | |||
| 5dab84eeb3 | |||
| 2283b62503 | |||
| ccf50c12c9 | |||
| f25d7511aa | |||
| 243ccf3da9 | |||
| aedf9cd06d | |||
| c053c905ad | |||
| 7dff40745c | |||
| 0e8563b216 | |||
| 6720bbabc1 | |||
| 6035989da4 | |||
| dc16893777 | |||
| fdfad57cee | |||
| 9cf00b290f | |||
| 47072a4982 | |||
| 62748990cb | |||
| 42c69d44e5 | |||
| c6eb51a395 | |||
| 6777696df6 | |||
| a8e244f9ab | |||
| 911f9bfe7b | |||
| 24a65d3752 | |||
| 0bd0ec5f1e | |||
| 0ad418ed1f | |||
| ec48231aae | |||
| 5631822e73 | |||
| 630d32ba11 | |||
| 22500b7076 | |||
| 9d2d27b257 | |||
| 879509c7ba | |||
| a0805111b4 | |||
| b0cd4f37b1 | |||
| 8999f45aad | |||
| 838803c53e | |||
| c4063b5390 | |||
| 1c5bd1e448 | |||
| 6d9b2092bd | |||
| 9b218d763d | |||
| 10c5b0fbc6 | |||
| 503eec14c6 | |||
| deac7f97bb | |||
| a3fb7ac353 | |||
| 8f4e11215f | |||
| 78b0032578 | |||
| 536f6f52ff | |||
| 96ca1afb9b | |||
| 780263e78b | |||
| 49ff399f97 | |||
| 27d14b1711 | |||
| 89b2ec90f2 | |||
| f8a2e839b9 | |||
| 94ecbd27bf | |||
| b44a592808 | |||
| b00d2f13a3 | |||
| b26f1531f5 | |||
| 7de5fa7794 | |||
| 66e91e05a4 | |||
| a20bb8ab09 | |||
| c5fcc8b353 | |||
| 0357f263cc | |||
| 8488debc25 | |||
| 00dd40428b | |||
| 3d35a57723 | |||
| 0d40e32a39 | |||
| fd34db28cb | |||
| 65edd3258e | |||
| e914bc6492 | |||
| 0eed8089b6 | |||
| 1048d20cef | |||
| 00ccbaf0d0 | |||
| 51f733127c | |||
| d96febeae3 | |||
| 79b31dad19 | |||
| d5146e7e3e | |||
| d2d6f280b3 | |||
| 79d9f8d7cc | |||
| bcc4fc3432 | |||
| cc2d7d1a3d | |||
| 83f6db5e33 | |||
| 12b54d51d2 | |||
| 89c154de37 | |||
| ea037debed | |||
| a0a0c43690 | |||
| bce49d51f7 | |||
| 77a352aa2e | |||
| fd6db0cc65 | |||
| 2b548fa1ef | |||
| e0465b9b8d | |||
| 0523610080 | |||
| e341c12763 | |||
| 6cff0b7931 | |||
| f5bfe1e578 | |||
| 0fbe2956c3 | |||
| 9280430285 | |||
| b91e34bc8e | |||
| 9f10576a0c | |||
| 8a9f4cb506 | |||
| d3098c0d9c | |||
| 8be18b6b38 | |||
| b39b8c885b | |||
| 56901ca553 | |||
| e3c9da9621 | |||
| 070bd8be9a | |||
| c1ba2ca02b | |||
| 146b63706a | |||
| 1421062a75 | |||
| b9928789df | |||
| b7aafeec52 | |||
| a935e35d82 | |||
| e2c2d0df71 | |||
| e3a47b0e16 | |||
| 86370f27c8 | |||
| c61e573e14 | |||
| d16f70cf00 | |||
| 6f7561213e | |||
| 94b15548cf | |||
| 4e9e2b399c | |||
| 035e56bf53 | |||
| 61cf84aa8a | |||
| 5513ae8386 | |||
| f1730c30cb | |||
| ce7d42621f | |||
| 8eafb1f7c5 | |||
| 81be03e8ee | |||
| d662f693b8 | |||
| b0d16b1bac | |||
| 3eb9120dc3 | |||
| 85ae14acbf | |||
| b7a98033c6 | |||
| 8f265eae05 | |||
| fe20a0b654 | |||
| aa80e4eb8e | |||
| 0d029f2861 | |||
| edf94432f4 | |||
| 33552358f8 | |||
| 45d3e93a93 | |||
| 707f34e706 | |||
| a05450c73a | |||
| 6b95f5ced0 | |||
| 9caee546f0 | |||
| 8e831d85fe | |||
| b8ab5fae7b | |||
| 3262d1cbb8 | |||
| 766ee97dd3 | |||
| c41c96e628 | |||
| fb46fceb75 | |||
| c9fe217e4b | |||
| d88d64edd4 | |||
| e81194721c | |||
| a680e0ba48 | |||
| 345defec4d | |||
| 3756279dab | |||
| 70189f4295 | |||
| cdacf7ae06 | |||
| 074c762960 | |||
| 6babf31a20 | |||
| cdefd68320 | |||
| f28611a7bf | |||
| f98034b00c | |||
| 375b756718 | |||
| 6668e8ae1b | |||
| e58959778a | |||
| eb72fe9ebd | |||
| e355423675 | |||
| b1be29c21c | |||
| b04dcc5aea | |||
| 11b6227d0e | |||
| 31660c7510 | |||
| 728bbb2a06 | |||
| 16a99ba8d8 | |||
| 0949840317 | |||
| daec5a8671 | |||
| d66cd1179d | |||
| 2d3828c55d | |||
| b7a7bfb5c7 | |||
| be918bdf6c | |||
| c8517b674f | |||
| f3d73b7c4b | |||
| bcef8844ec | |||
| 20ab5fe4e5 | |||
| 959c05b769 | |||
| 4179c88afb | |||
| 0dc0c3af3d | |||
| c6dc950d3f | |||
| e5a1504f6b | |||
| 9da1fa02c8 | |||
| 99857d8ca5 | |||
| e198caf1b9 | |||
| 9dd5f1354f | |||
| 8ad1b166f3 | |||
| 1f3b42b266 | |||
| 9521677ca8 | |||
| 8938992384 | |||
| a0ce63e5a5 | |||
| a12c8df252 | |||
| 8d69f10924 | |||
| e41d1fa220 | |||
| da78d01d9f | |||
| ff4b7b281c | |||
| a3f54530f6 | |||
| 301d93157c | |||
| 7e0a1e71a7 | |||
| 962bc923f3 | |||
| 76568a801d | |||
| 1f5b8247a9 | |||
| 950eec898e | |||
| 2d6c89d7c9 | |||
| 6bc4c1b5a5 | |||
| 7b0ccf8a40 | |||
| ea62c2ce0a | |||
| 87a7dee129 | |||
| 53d72ae163 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
bin/aoc
|
bin/aoc
|
||||||
|
*.test
|
||||||
|
|||||||
93
Makefile
93
Makefile
@@ -1,60 +1,61 @@
|
|||||||
GO = go
|
GO = go
|
||||||
BIN = bin/aoc
|
BIN = bin/aoc
|
||||||
|
|
||||||
.PHONY: build test clean new
|
.PHONY: build test clean
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@mkdir -p $(dir $(BIN))
|
@mkdir -p $(dir $(BIN))
|
||||||
@$(GO) build -o $(BIN) ./cmd/aoc
|
@$(GO) build -o $(BIN) ./cmd/aoc
|
||||||
|
|
||||||
test:
|
test:
|
||||||
@$(GO) test ./...
|
@$(GO) test ./internal/...
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
@rm -f $(BIN)
|
@rm -f $(BIN)
|
||||||
|
|
||||||
new:
|
|
||||||
@if [ -z "$(filter-out $@,$(MAKECMDGOALS))" ]; then \
|
|
||||||
echo "Usage: make new 2020D9"; \
|
|
||||||
exit 1; \
|
|
||||||
fi
|
|
||||||
@DAY_ARG=$$(echo $(filter-out $@,$(MAKECMDGOALS)) | awk '{print $$1}'); \
|
|
||||||
YEAR=$$(echo $$DAY_ARG | sed 's/D.*//'); \
|
|
||||||
DAY_NUM=$$(echo $$DAY_ARG | sed 's/.*D//'); \
|
|
||||||
DAY_NAME=$$(case $$DAY_NUM in \
|
|
||||||
1) echo "One" ;; \
|
|
||||||
2) echo "Two" ;; \
|
|
||||||
3) echo "Three" ;; \
|
|
||||||
4) echo "Four" ;; \
|
|
||||||
5) echo "Five" ;; \
|
|
||||||
6) echo "Six" ;; \
|
|
||||||
7) echo "Seven" ;; \
|
|
||||||
8) echo "Eight" ;; \
|
|
||||||
9) echo "Nine" ;; \
|
|
||||||
10) echo "Ten" ;; \
|
|
||||||
11) echo "Eleven" ;; \
|
|
||||||
12) echo "Twelve" ;; \
|
|
||||||
13) echo "Thirteen" ;; \
|
|
||||||
14) echo "Fourteen" ;; \
|
|
||||||
15) echo "Fifteen" ;; \
|
|
||||||
16) echo "Sixteen" ;; \
|
|
||||||
17) echo "Seventeen" ;; \
|
|
||||||
18) echo "Eighteen" ;; \
|
|
||||||
19) echo "Nineteen" ;; \
|
|
||||||
20) echo "Twenty" ;; \
|
|
||||||
21) echo "TwentyOne" ;; \
|
|
||||||
22) echo "TwentyTwo" ;; \
|
|
||||||
23) echo "TwentyThree" ;; \
|
|
||||||
24) echo "TwentyFour" ;; \
|
|
||||||
25) echo "TwentyFive" ;; \
|
|
||||||
*) echo "Unknown" ;; \
|
|
||||||
esac); \
|
|
||||||
mkdir -p internal/$$YEAR/Day$$DAY_NAME; \
|
|
||||||
mkdir -p internal/data/$$YEAR/Day$$DAY_NAME; \
|
|
||||||
touch internal/$$YEAR/Day$$DAY_NAME/code.go; \
|
|
||||||
touch internal/$$YEAR/Day$$DAY_NAME/code_test.go; \
|
|
||||||
touch internal/data/$$YEAR/Day$$DAY_NAME/input.txt; \
|
|
||||||
echo "Created files for $$DAY_ARG"
|
|
||||||
|
|
||||||
%:
|
%:
|
||||||
@:
|
@DAY_ARG=$@; \
|
||||||
|
if echo $$DAY_ARG | grep -qE '^[0-9]{4}D[0-9]+$$'; then \
|
||||||
|
YEAR=$$(echo $$DAY_ARG | sed 's/D.*//'); \
|
||||||
|
DAY_NUM=$$(echo $$DAY_ARG | sed 's/.*D//'); \
|
||||||
|
DAY_NAME=$$(case $$DAY_NUM in \
|
||||||
|
1) echo "One" ;; \
|
||||||
|
2) echo "Two" ;; \
|
||||||
|
3) echo "Three" ;; \
|
||||||
|
4) echo "Four" ;; \
|
||||||
|
5) echo "Five" ;; \
|
||||||
|
6) echo "Six" ;; \
|
||||||
|
7) echo "Seven" ;; \
|
||||||
|
8) echo "Eight" ;; \
|
||||||
|
9) echo "Nine" ;; \
|
||||||
|
10) echo "Ten" ;; \
|
||||||
|
11) echo "Eleven" ;; \
|
||||||
|
12) echo "Twelve" ;; \
|
||||||
|
13) echo "Thirteen" ;; \
|
||||||
|
14) echo "Fourteen" ;; \
|
||||||
|
15) echo "Fifteen" ;; \
|
||||||
|
16) echo "Sixteen" ;; \
|
||||||
|
17) echo "Seventeen" ;; \
|
||||||
|
18) echo "Eighteen" ;; \
|
||||||
|
19) echo "Nineteen" ;; \
|
||||||
|
20) echo "Twenty" ;; \
|
||||||
|
21) echo "TwentyOne" ;; \
|
||||||
|
22) echo "TwentyTwo" ;; \
|
||||||
|
23) echo "TwentyThree" ;; \
|
||||||
|
24) echo "TwentyFour" ;; \
|
||||||
|
25) echo "TwentyFive" ;; \
|
||||||
|
*) echo "Unknown" ;; \
|
||||||
|
esac); \
|
||||||
|
mkdir -p internal/$$YEAR/Day$$DAY_NAME; \
|
||||||
|
mkdir -p internal/data/$$YEAR/Day$$DAY_NAME; \
|
||||||
|
touch internal/$$YEAR/Day$$DAY_NAME/code.go; \
|
||||||
|
touch internal/$$YEAR/Day$$DAY_NAME/code_test.go; \
|
||||||
|
if [ -n "$$ADVENTOFCODE_SESSION" ]; then \
|
||||||
|
curl -s -H "Cookie: session=$$ADVENTOFCODE_SESSION" \
|
||||||
|
https://adventofcode.com/$$YEAR/day/$$DAY_NUM/input \
|
||||||
|
| perl -pe 'chomp if eof' > internal/data/$$YEAR/Day$$DAY_NAME/input.txt; \
|
||||||
|
else \
|
||||||
|
touch internal/data/$$YEAR/Day$$DAY_NAME/input.txt; \
|
||||||
|
fi; \
|
||||||
|
echo "$$DAY_ARG ready to be solved."; \
|
||||||
|
fi
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -13,38 +13,64 @@ Ultimately, my goal is to complete all the years of Advent of Code here.
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Go 1.25
|
- Go 1.25
|
||||||
- Puzzle input
|
- Puzzle input (or your session cookie to have it downloaded automatically)
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
├── cmd/
|
├── cmd/
|
||||||
│ └── aoc/
|
│ └── aoc/
|
||||||
│ └── main.go # CLI entry point that runs solutions
|
│ └── main.go # CLI entry point for running puzzles
|
||||||
└── internal/
|
└── internal/
|
||||||
├── 2020/
|
├── 2020/
|
||||||
|
│ ├── register.go # Aggregates import for main.go
|
||||||
│ ├── DayOne/
|
│ ├── DayOne/
|
||||||
│ │ ├── code.go # Go solution for Day 1 (2020)
|
│ │ ├── code.go # Day 1 solution for 2020
|
||||||
│ │ └── code_test.go # Unit tests for Day 1
|
│ │ └── code_test.go # Unit tests for Day 1 solution
|
||||||
│ └── ...
|
│ └── ... # Other days for 2020
|
||||||
├── 2021/
|
├── 2021/
|
||||||
│ └── ... # Additional years and days
|
│ └── ... # 2021 days code and tests
|
||||||
├── registry/
|
├── registry/
|
||||||
│ └── registry.go # Central registry for day runners
|
│ └── registry.go # Central map/registry for finding and running days
|
||||||
└── data/
|
└── data/
|
||||||
├── 2020/
|
├── 2020/
|
||||||
│ ├── DayOne/
|
│ ├── DayOne/
|
||||||
│ │ └── input.txt # Puzzle input for Day 1 (2020)
|
│ │ └── input.txt # Puzzle input for Day 1 (2020)
|
||||||
│ └── ...
|
│ └── ... # Inputs for other days
|
||||||
└── ...
|
└── ... # Inputs for other years
|
||||||
```
|
```
|
||||||
|
|
||||||
Each day's solution is organized into its own folder, named according to the day number (e.g., `DayOne`, `DayTwo`). Inside each folder, you'll find a `code.go` file with the Go implementation of the solution and a `code_test.go` file containing corresponding tests. The input data for each puzzle is located in the `internal/data` directory, mirroring the code structure for easy access.
|
Each day's solution is organized into its own folder, named according to the day number (e.g., `DayOne`, `DayTwo`). Inside each folder, you'll find a `code.go` file with the Go implementation of the solution and a `code_test.go` file containing corresponding tests. The input data for each puzzle is located in the `internal/data` directory, mirroring the code structure for easy access.
|
||||||
|
|
||||||
To connect each solution to the CLI, the repository uses a central registry located in `internal/registry/registry.go`. This registry is essentially a map linking a day key (combining year and day number) to assign a "day runner" which is a struct that specifies the `ParseInput`, `PartOne` and `PartTwo` functions of the day's problem.
|
Each year has its own `register.go` file to aggregate imports. To connect each solution to the CLI, the repository uses a central registry located in `internal/registry/registry.go`. This registry is essentially a map linking a day key (combining year and day number) to assign a "day runner" which is a struct that specifies the `ParseInput`, `PartOne` and `PartTwo` functions of the day's problem.
|
||||||
|
|
||||||
When running a solution, the CLI (`cmd/aoc/main.go`) looks up the appropriate runner from the registry based on your command-line input, uses the parsing function to load the input data, and then runs the desired part (or both parts) using the registered solution functions.
|
When running a solution, the CLI (`cmd/aoc/main.go`) looks up the appropriate runner from the registry based on your command-line input, uses the parsing function to load the input data, and then runs the desired part (or both parts) using the registered solution functions.
|
||||||
|
|
||||||
|
### Starting a new day
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make 2020D1
|
||||||
|
```
|
||||||
|
|
||||||
|
If the `ADVENTOFCODE_SESSION` environment variable is set with your Advent of Code session cookie, the input will be downloaded from the AOC website right into the corresponding `internal/data` directory, else it will create an empty `input.txt` file.
|
||||||
|
|
||||||
|
Then, it will create the following files:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
internal/2020/DayOne/code.go
|
||||||
|
internal/2020/DayOne/code_test.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Handling string answers
|
||||||
|
|
||||||
|
Occasionally, a puzzle requires a string as the answer. However, my solution framework expects PartOne/PartTwo functions to return integers.
|
||||||
|
|
||||||
|
To work around this, I print the string result (see [2018D2P2](https://git.kharec.info/Kharec/advent-of-code/tree/main/internal/2018/DayTwo/code.go)) and return a dummy integer to satisfy the type requirements.
|
||||||
|
|
||||||
|
Meanwhile, the corresponding [test](https://git.kharec.info/Kharec/advent-of-code/tree/main/internal/2018/DayTwo/code_test.go) captures the output and verifies that the correct string is produced.
|
||||||
|
|
||||||
|
It's not the most elegant solution, but since only a handful of days across all Advent of Code years require a string result, this compromise keeps the rest of the code simple.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Build the CLI tool:
|
Build the CLI tool:
|
||||||
|
|||||||
@@ -1,25 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"advent-of-code/internal/registry"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
_ "advent-of-code/internal/2015/DayOne"
|
"advent-of-code/internal/registry"
|
||||||
_ "advent-of-code/internal/2020/DayEight"
|
|
||||||
_ "advent-of-code/internal/2020/DayFour"
|
_ "advent-of-code/internal/2015"
|
||||||
_ "advent-of-code/internal/2020/DayOne"
|
_ "advent-of-code/internal/2016"
|
||||||
_ "advent-of-code/internal/2020/DaySeven"
|
_ "advent-of-code/internal/2018"
|
||||||
_ "advent-of-code/internal/2020/DaySix"
|
_ "advent-of-code/internal/2020"
|
||||||
_ "advent-of-code/internal/2020/DayThree"
|
_ "advent-of-code/internal/2021"
|
||||||
_ "advent-of-code/internal/2020/DayTwo"
|
_ "advent-of-code/internal/2022"
|
||||||
_ "advent-of-code/internal/2021/DayOne"
|
_ "advent-of-code/internal/2025"
|
||||||
_ "advent-of-code/internal/2021/DayThree"
|
|
||||||
_ "advent-of-code/internal/2021/DayTwo"
|
|
||||||
_ "advent-of-code/internal/2022/DayOne"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func capitalize(day string) string {
|
func capitalize(day string) string {
|
||||||
|
|||||||
61
internal/2015/DayEight/code.go
Normal file
61
internal/2015/DayEight/code.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
package dayeight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D8", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
result := 0
|
||||||
|
for _, line := range data {
|
||||||
|
codeLength := len(line)
|
||||||
|
memoryLength := 0
|
||||||
|
for idx := 1; idx < len(line)-1; idx++ {
|
||||||
|
if line[idx] == '\\' && idx+1 < len(line)-1 {
|
||||||
|
switch line[idx+1] {
|
||||||
|
case '\\', '"':
|
||||||
|
memoryLength++
|
||||||
|
idx++
|
||||||
|
case 'x':
|
||||||
|
if idx+3 < len(line)-1 {
|
||||||
|
memoryLength++
|
||||||
|
idx += 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memoryLength++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += codeLength - memoryLength
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
result := 0
|
||||||
|
for _, line := range data {
|
||||||
|
originalLength := len(line)
|
||||||
|
encodedLength := 2
|
||||||
|
for _, char := range line {
|
||||||
|
switch char {
|
||||||
|
case '\\', '"':
|
||||||
|
encodedLength += 2
|
||||||
|
default:
|
||||||
|
encodedLength++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result += encodedLength - originalLength
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
26
internal/2015/DayEight/code_test.go
Normal file
26
internal/2015/DayEight/code_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package dayeight
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
`""`,
|
||||||
|
`"abc"`,
|
||||||
|
`"aaa\"aaa"`,
|
||||||
|
`"\x27"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 12
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 19
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
97
internal/2015/DayEleven/code.go
Normal file
97
internal/2015/DayEleven/code.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package dayeleven
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D11", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNextValidPassword(data string) string {
|
||||||
|
bytes := []byte(data)
|
||||||
|
|
||||||
|
increment := func() {
|
||||||
|
for idx := len(bytes) - 1; idx >= 0; idx-- {
|
||||||
|
if bytes[idx] == 'z' {
|
||||||
|
bytes[idx] = 'a'
|
||||||
|
} else {
|
||||||
|
bytes[idx]++
|
||||||
|
if bytes[idx] == 'i' || bytes[idx] == 'o' || bytes[idx] == 'l' {
|
||||||
|
bytes[idx]++
|
||||||
|
}
|
||||||
|
for j := idx + 1; j < len(bytes); j++ {
|
||||||
|
bytes[j] = 'a'
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
increment()
|
||||||
|
|
||||||
|
for {
|
||||||
|
forbiddenPosition := -1
|
||||||
|
for idx := range bytes {
|
||||||
|
if bytes[idx] == 'i' || bytes[idx] == 'o' || bytes[idx] == 'l' {
|
||||||
|
forbiddenPosition = idx
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if forbiddenPosition != -1 {
|
||||||
|
bytes[forbiddenPosition]++
|
||||||
|
if bytes[forbiddenPosition] == 'i' || bytes[forbiddenPosition] == 'o' || bytes[forbiddenPosition] == 'l' {
|
||||||
|
bytes[forbiddenPosition]++
|
||||||
|
}
|
||||||
|
for j := forbiddenPosition + 1; j < len(bytes); j++ {
|
||||||
|
bytes[j] = 'a'
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasStraight := false
|
||||||
|
for idx := 0; idx < len(bytes)-2; idx++ {
|
||||||
|
if bytes[idx+1] == bytes[idx]+1 && bytes[idx+2] == bytes[idx]+2 {
|
||||||
|
hasStraight = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pairChars := make(map[byte]bool)
|
||||||
|
for idx := 0; idx < len(bytes)-1; idx++ {
|
||||||
|
if bytes[idx] == bytes[idx+1] {
|
||||||
|
pairChars[bytes[idx]] = true
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasStraight && len(pairChars) >= 2 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
increment()
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data string) int {
|
||||||
|
result := findNextValidPassword(data)
|
||||||
|
println(result)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data string) int {
|
||||||
|
firstPassword := findNextValidPassword(data)
|
||||||
|
secondPassword := findNextValidPassword(firstPassword)
|
||||||
|
println(secondPassword)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
77
internal/2015/DayFive/code.go
Normal file
77
internal/2015/DayFive/code.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D5", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
count := 0
|
||||||
|
for _, line := range data {
|
||||||
|
vowelCount := strings.Count(line, "a") + strings.Count(line, "e") + strings.Count(line, "i") + strings.Count(line, "o") + strings.Count(line, "u")
|
||||||
|
if vowelCount < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasDoubleLetter := false
|
||||||
|
for i := 1; i < len(line); i++ {
|
||||||
|
if line[i] == line[i-1] {
|
||||||
|
hasDoubleLetter = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasDoubleLetter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(line, "ab") || strings.Contains(line, "cd") || strings.Contains(line, "pq") || strings.Contains(line, "xy") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
count := 0
|
||||||
|
for _, line := range data {
|
||||||
|
if len(line) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNonOverlappingPair := false
|
||||||
|
for i := 0; i < len(line)-1; i++ {
|
||||||
|
pair := line[i : i+2]
|
||||||
|
if strings.Contains(line[i+2:], pair) {
|
||||||
|
hasNonOverlappingPair = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasNonOverlappingPair {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hasRepeatingLetterWithGap := false
|
||||||
|
for i := 0; i < len(line)-2; i++ {
|
||||||
|
if line[i] == line[i+2] {
|
||||||
|
hasRepeatingLetterWithGap = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasRepeatingLetterWithGap {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
32
internal/2015/DayFive/code_test.go
Normal file
32
internal/2015/DayFive/code_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
input := []string{
|
||||||
|
"ugknbfddgicrmopn",
|
||||||
|
"aaa",
|
||||||
|
"jchzalrnumimnmhp",
|
||||||
|
"haegwjzuvuyypxyu",
|
||||||
|
"dvszwmarrgswjxmb",
|
||||||
|
}
|
||||||
|
expected := 2
|
||||||
|
got := PartOne(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
input := []string{
|
||||||
|
"qjhvhtzxzqqjkmpb",
|
||||||
|
"xxyxx",
|
||||||
|
"uurcxstgmygtbstg",
|
||||||
|
"ieodomkazucvgmuy",
|
||||||
|
}
|
||||||
|
expected := 2
|
||||||
|
got := PartTwo(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
46
internal/2015/DayFour/code.go
Normal file
46
internal/2015/DayFour/code.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []byte {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return bytes.TrimSpace(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findHashWithZeroes(data []byte, zeroes int) int {
|
||||||
|
buffer := make([]byte, len(data), len(data)+10)
|
||||||
|
copy(buffer, data)
|
||||||
|
for idx := 1; ; idx++ {
|
||||||
|
buffer = buffer[:len(data)]
|
||||||
|
buffer = strconv.AppendInt(buffer, int64(idx), 10)
|
||||||
|
hash := md5.Sum(buffer)
|
||||||
|
switch zeroes {
|
||||||
|
case 5:
|
||||||
|
if hash[0] == 0 && hash[1] == 0 && hash[2]&0xF0 == 0 {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
case 6:
|
||||||
|
if hash[0] == 0 && hash[1] == 0 && hash[2] == 0 {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []byte) int {
|
||||||
|
return findHashWithZeroes(data, 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []byte) int {
|
||||||
|
return findHashWithZeroes(data, 6)
|
||||||
|
}
|
||||||
45
internal/2015/DayFour/code_test.go
Normal file
45
internal/2015/DayFour/code_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"abcdef", []byte("abcdef"), 609043},
|
||||||
|
{"pqrstuv", []byte("pqrstuv"), 1048970},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartOne(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"abcdef", []byte("abcdef"), 6742839},
|
||||||
|
{"pqrstuv", []byte("pqrstuv"), 5714438},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartTwo(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
108
internal/2015/DayFourteen/code.go
Normal file
108
internal/2015/DayFourteen/code.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package dayfourteen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var reindeerPattern = regexp.MustCompile(`\w+ can fly (\d+) km/s for (\d+) seconds, but then must rest for (\d+) seconds\.`)
|
||||||
|
|
||||||
|
const raceTime = 2503
|
||||||
|
|
||||||
|
type Reindeer struct {
|
||||||
|
Speed int
|
||||||
|
FlyTime int
|
||||||
|
RestTime int
|
||||||
|
Points int
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D14", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseReindeer(line string) Reindeer {
|
||||||
|
matches := reindeerPattern.FindStringSubmatch(line)
|
||||||
|
speed, _ := strconv.Atoi(matches[1])
|
||||||
|
flyTime, _ := strconv.Atoi(matches[2])
|
||||||
|
restTime, _ := strconv.Atoi(matches[3])
|
||||||
|
return Reindeer{Speed: speed, FlyTime: flyTime, RestTime: restTime}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseReindeers(data []string) []Reindeer {
|
||||||
|
reindeers := make([]Reindeer, 0, len(data))
|
||||||
|
for _, line := range data {
|
||||||
|
reindeers = append(reindeers, parseReindeer(line))
|
||||||
|
}
|
||||||
|
return reindeers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (reindeer Reindeer) distanceAtTime(time int) int {
|
||||||
|
cycleTime := reindeer.FlyTime + reindeer.RestTime
|
||||||
|
fullCycles := time / cycleTime
|
||||||
|
distance := fullCycles * reindeer.Speed * reindeer.FlyTime
|
||||||
|
remainingTime := time % cycleTime
|
||||||
|
distance += reindeer.Speed * min(remainingTime, reindeer.FlyTime)
|
||||||
|
return distance
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateMaxDistance(data []string, time int) int {
|
||||||
|
reindeers := parseReindeers(data)
|
||||||
|
maxDistance := 0
|
||||||
|
|
||||||
|
for _, reindeer := range reindeers {
|
||||||
|
distance := reindeer.distanceAtTime(time)
|
||||||
|
if distance > maxDistance {
|
||||||
|
maxDistance = distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateMaxPoints(data []string, time int) int {
|
||||||
|
reindeers := parseReindeers(data)
|
||||||
|
|
||||||
|
for second := 1; second <= time; second++ {
|
||||||
|
maxDistance := 0
|
||||||
|
distances := make([]int, len(reindeers))
|
||||||
|
|
||||||
|
for idx := range reindeers {
|
||||||
|
distance := reindeers[idx].distanceAtTime(second)
|
||||||
|
distances[idx] = distance
|
||||||
|
if distance > maxDistance {
|
||||||
|
maxDistance = distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx := range reindeers {
|
||||||
|
if distances[idx] == maxDistance {
|
||||||
|
reindeers[idx].Points++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxPoints := 0
|
||||||
|
for idx := range reindeers {
|
||||||
|
if reindeers[idx].Points > maxPoints {
|
||||||
|
maxPoints = reindeers[idx].Points
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxPoints
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
return calculateMaxDistance(data, raceTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
return calculateMaxPoints(data, raceTime)
|
||||||
|
}
|
||||||
24
internal/2015/DayFourteen/code_test.go
Normal file
24
internal/2015/DayFourteen/code_test.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package dayfourteen
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds.",
|
||||||
|
"Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 1120
|
||||||
|
got := calculateMaxDistance(testInput, 1000)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("calculateMaxDistance(testInput, 1000) = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 689
|
||||||
|
got := calculateMaxPoints(testInput, 1000)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("calculateMaxPoints(testInput, 1000) = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
106
internal/2015/DayNine/code.go
Normal file
106
internal/2015/DayNine/code.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package daynine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D9", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildDistanceMap(data []string) map[string]map[string]int {
|
||||||
|
distances := make(map[string]map[string]int)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, " = ")
|
||||||
|
distance, _ := strconv.Atoi(parts[1])
|
||||||
|
route := strings.Split(parts[0], " to ")
|
||||||
|
from, to := route[0], route[1]
|
||||||
|
|
||||||
|
if distances[from] == nil {
|
||||||
|
distances[from] = make(map[string]int)
|
||||||
|
}
|
||||||
|
if distances[to] == nil {
|
||||||
|
distances[to] = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
distances[from][to] = distance
|
||||||
|
distances[to][from] = distance
|
||||||
|
}
|
||||||
|
|
||||||
|
return distances
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCities(distances map[string]map[string]int) []string {
|
||||||
|
cities := make([]string, 0, len(distances))
|
||||||
|
for city := range distances {
|
||||||
|
cities = append(cities, city)
|
||||||
|
}
|
||||||
|
return cities
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePermutations(cities []string) [][]string {
|
||||||
|
if len(cities) == 0 {
|
||||||
|
return [][]string{{}}
|
||||||
|
}
|
||||||
|
var result [][]string
|
||||||
|
for idx, city := range cities {
|
||||||
|
remaining := make([]string, len(cities)-1)
|
||||||
|
copy(remaining[:idx], cities[:idx])
|
||||||
|
copy(remaining[idx:], cities[idx+1:])
|
||||||
|
|
||||||
|
for _, permutations := range generatePermutations(remaining) {
|
||||||
|
result = append(result, append([]string{city}, permutations...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateRouteDistance(route []string, distances map[string]map[string]int) int {
|
||||||
|
total := 0
|
||||||
|
for idx := 0; idx < len(route)-1; idx++ {
|
||||||
|
total += distances[route[idx]][route[idx+1]]
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
distances := buildDistanceMap(data)
|
||||||
|
cities := getCities(distances)
|
||||||
|
permutations := generatePermutations(cities)
|
||||||
|
|
||||||
|
minimalDistance := int(^uint(0) >> 1)
|
||||||
|
for _, route := range permutations {
|
||||||
|
total := calculateRouteDistance(route, distances)
|
||||||
|
if total < minimalDistance {
|
||||||
|
minimalDistance = total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minimalDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
distances := buildDistanceMap(data)
|
||||||
|
cities := getCities(distances)
|
||||||
|
permutations := generatePermutations(cities)
|
||||||
|
|
||||||
|
maximalDistance := 0
|
||||||
|
for _, route := range permutations {
|
||||||
|
total := calculateRouteDistance(route, distances)
|
||||||
|
if total > maximalDistance {
|
||||||
|
maximalDistance = total
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maximalDistance
|
||||||
|
}
|
||||||
25
internal/2015/DayNine/code_test.go
Normal file
25
internal/2015/DayNine/code_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package daynine
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"London to Dublin = 464",
|
||||||
|
"London to Belfast = 518",
|
||||||
|
"Dublin to Belfast = 141",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 605
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 982
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,5 +28,17 @@ func PartOne(data string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func PartTwo(data string) int {
|
func PartTwo(data string) int {
|
||||||
|
floor := 0
|
||||||
|
for idx, char := range data {
|
||||||
|
switch char {
|
||||||
|
case '(':
|
||||||
|
floor++
|
||||||
|
case ')':
|
||||||
|
floor--
|
||||||
|
}
|
||||||
|
if floor == -1 {
|
||||||
|
return idx + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,27 @@ func TestPartOne(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
got := PartOne(tt.input)
|
got := PartOne(tt.input)
|
||||||
if got != tt.expected {
|
if got != tt.expected {
|
||||||
t.Errorf("PartOne(%q) = %d, want %d", tt.input, got, tt.expected)
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{")", ")", 1},
|
||||||
|
{"()())", "()())", 5},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartTwo(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, tt.expected)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
77
internal/2015/DaySeven/code.go
Normal file
77
internal/2015/DaySeven/code.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package dayseven
|
||||||
|
|
||||||
|
import (
|
||||||
|
"maps"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D7", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) map[string]string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
instructions := make(map[string]string)
|
||||||
|
for line := range strings.SplitSeq(string(content), "\n") {
|
||||||
|
if parts := strings.Split(line, " -> "); len(parts) == 2 {
|
||||||
|
instructions[parts[1]] = parts[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
func evaluateWire(wire string, instructions map[string]string) uint16 {
|
||||||
|
cache := make(map[string]uint16)
|
||||||
|
var evaluate func(string) uint16
|
||||||
|
evaluate = func(wire string) uint16 {
|
||||||
|
if value, ok := cache[wire]; ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if num, err := strconv.Atoi(wire); err == nil {
|
||||||
|
return uint16(num)
|
||||||
|
}
|
||||||
|
expression, exists := instructions[wire]
|
||||||
|
if !exists {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
parts := strings.Fields(expression)
|
||||||
|
var result uint16
|
||||||
|
switch {
|
||||||
|
case len(parts) == 1:
|
||||||
|
result = evaluate(parts[0])
|
||||||
|
case parts[0] == "NOT":
|
||||||
|
result = ^evaluate(parts[1])
|
||||||
|
default:
|
||||||
|
left, right := evaluate(parts[0]), evaluate(parts[2])
|
||||||
|
switch parts[1] {
|
||||||
|
case "AND":
|
||||||
|
result = left & right
|
||||||
|
case "OR":
|
||||||
|
result = left | right
|
||||||
|
case "LSHIFT":
|
||||||
|
result = left << right
|
||||||
|
case "RSHIFT":
|
||||||
|
result = left >> right
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache[wire] = result
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return evaluate(wire)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data map[string]string) int {
|
||||||
|
return int(evaluateWire("a", data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data map[string]string) int {
|
||||||
|
signalA := PartOne(data)
|
||||||
|
instructionsCopy := make(map[string]string, len(data))
|
||||||
|
maps.Copy(instructionsCopy, data)
|
||||||
|
instructionsCopy["b"] = strconv.Itoa(signalA)
|
||||||
|
return int(evaluateWire("a", instructionsCopy))
|
||||||
|
}
|
||||||
50
internal/2015/DaySeven/code_test.go
Normal file
50
internal/2015/DaySeven/code_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package dayseven
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testInput = map[string]string{
|
||||||
|
"x": "123",
|
||||||
|
"y": "456",
|
||||||
|
"d": "x AND y",
|
||||||
|
"e": "x OR y",
|
||||||
|
"f": "x LSHIFT 2",
|
||||||
|
"g": "y RSHIFT 2",
|
||||||
|
"h": "NOT x",
|
||||||
|
"i": "NOT y",
|
||||||
|
"a": "d",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 72
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
instructions := map[string]string{
|
||||||
|
"x": "10",
|
||||||
|
"y": "20",
|
||||||
|
"z": "x AND y",
|
||||||
|
"b": "z",
|
||||||
|
"w": "b LSHIFT 1",
|
||||||
|
"v": "NOT b",
|
||||||
|
"u": "w OR v",
|
||||||
|
"a": "u",
|
||||||
|
}
|
||||||
|
|
||||||
|
partOneResult := PartOne(instructions)
|
||||||
|
bValue := uint16(partOneResult)
|
||||||
|
w := bValue << 1
|
||||||
|
v := ^bValue
|
||||||
|
u := w | v
|
||||||
|
expected := int(u)
|
||||||
|
|
||||||
|
got := PartTwo(instructions)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d (PartOne result: %d)", got, expected, partOneResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
119
internal/2015/DaySix/code.go
Normal file
119
internal/2015/DaySix/code.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package daysix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type instruction struct {
|
||||||
|
operation string
|
||||||
|
x1, y1 int
|
||||||
|
x2, y2 int
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D6", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInstruction(line string) (instruction, bool) {
|
||||||
|
var op string
|
||||||
|
var remaining string
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(line, "turn on "):
|
||||||
|
op = "on"
|
||||||
|
remaining = strings.TrimPrefix(line, "turn on ")
|
||||||
|
case strings.HasPrefix(line, "turn off "):
|
||||||
|
op = "off"
|
||||||
|
remaining = strings.TrimPrefix(line, "turn off ")
|
||||||
|
case strings.HasPrefix(line, "toggle "):
|
||||||
|
op = "toggle"
|
||||||
|
remaining = strings.TrimPrefix(line, "toggle ")
|
||||||
|
default:
|
||||||
|
return instruction{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(remaining, " through ")
|
||||||
|
firstCoordinates := strings.Split(parts[0], ",")
|
||||||
|
secondCoordinates := strings.Split(parts[1], ",")
|
||||||
|
|
||||||
|
x1, _ := strconv.Atoi(firstCoordinates[0])
|
||||||
|
y1, _ := strconv.Atoi(firstCoordinates[1])
|
||||||
|
x2, _ := strconv.Atoi(secondCoordinates[0])
|
||||||
|
y2, _ := strconv.Atoi(secondCoordinates[1])
|
||||||
|
|
||||||
|
return instruction{operation: op, x1: x1, y1: y1, x2: x2, y2: y2}, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(strings.TrimSpace(string(content)), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
grid := make([]bool, 1000*1000)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
instruction, ok := parseInstruction(line)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := instruction.y1; y <= instruction.y2; y++ {
|
||||||
|
for x := instruction.x1; x <= instruction.x2; x++ {
|
||||||
|
idx := y*1000 + x
|
||||||
|
switch instruction.operation {
|
||||||
|
case "on":
|
||||||
|
grid[idx] = true
|
||||||
|
case "off":
|
||||||
|
grid[idx] = false
|
||||||
|
case "toggle":
|
||||||
|
grid[idx] = !grid[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, lit := range grid {
|
||||||
|
if lit {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
grid := make([]int, 1000*1000)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
instruction, ok := parseInstruction(line)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := instruction.y1; y <= instruction.y2; y++ {
|
||||||
|
for x := instruction.x1; x <= instruction.x2; x++ {
|
||||||
|
idx := y*1000 + x
|
||||||
|
switch instruction.operation {
|
||||||
|
case "on":
|
||||||
|
grid[idx]++
|
||||||
|
case "off":
|
||||||
|
if grid[idx] > 0 {
|
||||||
|
grid[idx]--
|
||||||
|
}
|
||||||
|
case "toggle":
|
||||||
|
grid[idx] += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
total := 0
|
||||||
|
for _, brightness := range grid {
|
||||||
|
total += brightness
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
25
internal/2015/DaySix/code_test.go
Normal file
25
internal/2015/DaySix/code_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package daysix
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"turn on 0,0 through 999,999",
|
||||||
|
"toggle 0,0 through 999,0",
|
||||||
|
"turn off 499,499 through 500,500",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 998996
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 1001996
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
45
internal/2015/DayTen/code.go
Normal file
45
internal/2015/DayTen/code.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package dayten
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D10", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyLookAndSay(data string, iterations int) int {
|
||||||
|
for range iterations {
|
||||||
|
var result strings.Builder
|
||||||
|
idx := 0
|
||||||
|
for idx < len(data) {
|
||||||
|
digit := data[idx]
|
||||||
|
count := 1
|
||||||
|
for idx+count < len(data) && data[idx+count] == digit {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
result.WriteString(strconv.Itoa(count))
|
||||||
|
result.WriteByte(digit)
|
||||||
|
idx += count
|
||||||
|
}
|
||||||
|
data = result.String()
|
||||||
|
}
|
||||||
|
return len(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data string) int {
|
||||||
|
return applyLookAndSay(data, 40)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data string) int {
|
||||||
|
return applyLookAndSay(data, 50)
|
||||||
|
}
|
||||||
141
internal/2015/DayThirteen/code.go
Normal file
141
internal/2015/DayThirteen/code.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package daythirteen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var inputPattern = regexp.MustCompile(`(\w+) would (gain|lose) (\d+) happiness units by sitting next to (\w+)\.?`)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D13", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildHappinessMap(data []string) map[string]map[string]int {
|
||||||
|
happinessMap := make(map[string]map[string]int)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
matches := inputPattern.FindStringSubmatch(line)
|
||||||
|
person := matches[1]
|
||||||
|
operation := matches[2]
|
||||||
|
value, _ := strconv.Atoi(matches[3])
|
||||||
|
neighbor := matches[4]
|
||||||
|
|
||||||
|
if happinessMap[person] == nil {
|
||||||
|
happinessMap[person] = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
if operation == "gain" {
|
||||||
|
happinessMap[person][neighbor] = value
|
||||||
|
} else {
|
||||||
|
happinessMap[person][neighbor] = -value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return happinessMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllPeople(happinessMap map[string]map[string]int) []string {
|
||||||
|
people := make([]string, 0, len(happinessMap))
|
||||||
|
for person := range happinessMap {
|
||||||
|
people = append(people, person)
|
||||||
|
}
|
||||||
|
return people
|
||||||
|
}
|
||||||
|
|
||||||
|
func generatePermutations(items []string) [][]string {
|
||||||
|
if len(items) == 0 {
|
||||||
|
return [][]string{{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result [][]string
|
||||||
|
for idx, item := range items {
|
||||||
|
remaining := make([]string, len(items)-1)
|
||||||
|
copy(remaining[:idx], items[:idx])
|
||||||
|
copy(remaining[idx:], items[idx+1:])
|
||||||
|
|
||||||
|
for _, permutation := range generatePermutations(remaining) {
|
||||||
|
result = append(result, append([]string{item}, permutation...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateTotalHappiness(arrangement []string, happinessMap map[string]map[string]int) int {
|
||||||
|
totalHappiness := 0
|
||||||
|
arrangementLength := len(arrangement)
|
||||||
|
|
||||||
|
for idx := range arrangementLength {
|
||||||
|
currentPerson := arrangement[idx]
|
||||||
|
leftNeighbor := arrangement[(idx-1+arrangementLength)%arrangementLength]
|
||||||
|
rightNeighbor := arrangement[(idx+1)%arrangementLength]
|
||||||
|
|
||||||
|
totalHappiness += happinessMap[currentPerson][leftNeighbor]
|
||||||
|
totalHappiness += happinessMap[currentPerson][rightNeighbor]
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalHappiness
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
happinessMap := buildHappinessMap(data)
|
||||||
|
allPeople := getAllPeople(happinessMap)
|
||||||
|
|
||||||
|
fixedPerson := allPeople[0]
|
||||||
|
remainingPeople := allPeople[1:]
|
||||||
|
permutations := generatePermutations(remainingPeople)
|
||||||
|
|
||||||
|
maxHappiness := math.MinInt
|
||||||
|
arrangement := make([]string, len(allPeople))
|
||||||
|
arrangement[0] = fixedPerson
|
||||||
|
|
||||||
|
for _, perm := range permutations {
|
||||||
|
copy(arrangement[1:], perm)
|
||||||
|
totalHappiness := calculateTotalHappiness(arrangement, happinessMap)
|
||||||
|
if totalHappiness > maxHappiness {
|
||||||
|
maxHappiness = totalHappiness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxHappiness
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
happinessMap := buildHappinessMap(data)
|
||||||
|
allPeople := getAllPeople(happinessMap)
|
||||||
|
|
||||||
|
me := "Me"
|
||||||
|
happinessMap[me] = make(map[string]int)
|
||||||
|
for _, person := range allPeople {
|
||||||
|
happinessMap[person][me] = 0
|
||||||
|
happinessMap[me][person] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
allPeople = append(allPeople, me)
|
||||||
|
fixedPerson := allPeople[0]
|
||||||
|
remainingPeople := allPeople[1:]
|
||||||
|
permutations := generatePermutations(remainingPeople)
|
||||||
|
|
||||||
|
maxTotalChange := math.MinInt
|
||||||
|
arrangement := make([]string, len(allPeople))
|
||||||
|
arrangement[0] = fixedPerson
|
||||||
|
|
||||||
|
for _, permutation := range permutations {
|
||||||
|
copy(arrangement[1:], permutation)
|
||||||
|
totalHappiness := calculateTotalHappiness(arrangement, happinessMap)
|
||||||
|
if totalHappiness > maxTotalChange {
|
||||||
|
maxTotalChange = totalHappiness
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxTotalChange
|
||||||
|
}
|
||||||
28
internal/2015/DayThirteen/code_test.go
Normal file
28
internal/2015/DayThirteen/code_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package daythirteen
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"Alice would gain 54 happiness units by sitting next to Bob",
|
||||||
|
"Alice would lose 79 happiness units by sitting next to Carol",
|
||||||
|
"Alice would lose 2 happiness units by sitting next to David",
|
||||||
|
"Bob would gain 83 happiness units by sitting next to Alice",
|
||||||
|
"Bob would lose 7 happiness units by sitting next to Carol",
|
||||||
|
"Bob would lose 63 happiness units by sitting next to David",
|
||||||
|
"Carol would lose 62 happiness units by sitting next to Alice",
|
||||||
|
"Carol would gain 60 happiness units by sitting next to Bob",
|
||||||
|
"Carol would gain 55 happiness units by sitting next to David",
|
||||||
|
"David would gain 46 happiness units by sitting next to Alice",
|
||||||
|
"David would lose 7 happiness units by sitting next to Bob",
|
||||||
|
"David would gain 41 happiness units by sitting next to Carol",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 330
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no test for part two
|
||||||
68
internal/2015/DayThree/code.go
Normal file
68
internal/2015/DayThree/code.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type coordinates struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D3", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data string) int {
|
||||||
|
houses := make(map[coordinates]int)
|
||||||
|
x, y := 0, 0
|
||||||
|
houses[coordinates{x, y}] = 1
|
||||||
|
for _, direction := range data {
|
||||||
|
switch direction {
|
||||||
|
case '>':
|
||||||
|
x++
|
||||||
|
case '<':
|
||||||
|
x--
|
||||||
|
case '^':
|
||||||
|
y++
|
||||||
|
case 'v':
|
||||||
|
y--
|
||||||
|
}
|
||||||
|
houses[coordinates{x, y}]++
|
||||||
|
}
|
||||||
|
return len(houses)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data string) int {
|
||||||
|
houses := make(map[coordinates]int)
|
||||||
|
santaX, santaY := 0, 0
|
||||||
|
robotX, robotY := 0, 0
|
||||||
|
houses[coordinates{0, 0}] = 2
|
||||||
|
|
||||||
|
for idx, direction := range data {
|
||||||
|
var x, y *int
|
||||||
|
if idx%2 == 0 {
|
||||||
|
x, y = &santaX, &santaY
|
||||||
|
} else {
|
||||||
|
x, y = &robotX, &robotY
|
||||||
|
}
|
||||||
|
|
||||||
|
switch direction {
|
||||||
|
case '>':
|
||||||
|
*x++
|
||||||
|
case '<':
|
||||||
|
*x--
|
||||||
|
case '^':
|
||||||
|
*y++
|
||||||
|
case 'v':
|
||||||
|
*y--
|
||||||
|
}
|
||||||
|
houses[coordinates{*x, *y}]++
|
||||||
|
}
|
||||||
|
return len(houses)
|
||||||
|
}
|
||||||
45
internal/2015/DayThree/code_test.go
Normal file
45
internal/2015/DayThree/code_test.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{">", ">", 2},
|
||||||
|
{"^>v<", "^>v<", 4},
|
||||||
|
{"^v^v^v^v^v", "^v^v^v^v^v", 2},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartOne(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"^v", "^v", 3},
|
||||||
|
{"^>v<", "^>v<", 3},
|
||||||
|
{"^v^v^v^v^v", "^v^v^v^v^v", 11},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartTwo(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
68
internal/2015/DayTwelve/code.go
Normal file
68
internal/2015/DayTwelve/code.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package daytwelve
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D12", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
re := regexp.MustCompile(`-?\d+`)
|
||||||
|
sum := 0
|
||||||
|
for _, line := range data {
|
||||||
|
matches := re.FindAllString(line, -1)
|
||||||
|
for _, match := range matches {
|
||||||
|
number, _ := strconv.Atoi(match)
|
||||||
|
sum += number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
var sumNumbers func(any) int
|
||||||
|
sumNumbers = func(v any) int {
|
||||||
|
switch value := v.(type) {
|
||||||
|
case float64:
|
||||||
|
return int(value)
|
||||||
|
case []any:
|
||||||
|
sum := 0
|
||||||
|
for _, item := range value {
|
||||||
|
sum += sumNumbers(item)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
case map[string]any:
|
||||||
|
sum := 0
|
||||||
|
for _, item := range value {
|
||||||
|
if str, ok := item.(string); ok && str == "red" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
sum += sumNumbers(item)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sum := 0
|
||||||
|
for _, line := range data {
|
||||||
|
var value any
|
||||||
|
_ = json.Unmarshal([]byte(line), &value)
|
||||||
|
sum += sumNumbers(value)
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
51
internal/2015/DayTwelve/code_test.go
Normal file
51
internal/2015/DayTwelve/code_test.go
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package daytwelve
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"[1,2,3]", "[1,2,3]", 6},
|
||||||
|
{"{\"a\":2,\"b\":4}", "{\"a\":2,\"b\":4}", 6},
|
||||||
|
{"[[[3]]]", "[[[3]]]", 3},
|
||||||
|
{"{\"a\":{\"b\":4},\"c\":-1}", "{\"a\":{\"b\":4},\"c\":-1}", 3},
|
||||||
|
{"{\"a\":[-1,1]}", "{\"a\":[-1,1]}", 0},
|
||||||
|
{"[-1,{\"a\":1}]", "[-1,{\"a\":1}]", 0},
|
||||||
|
{"[]", "[]", 0},
|
||||||
|
{"{}", "{}", 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartOne([]string{tt.input})
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"[1,2,3]", "[1,2,3]", 6},
|
||||||
|
{"[1,{\"c\":\"red\",\"b\":2},3]", "[1,{\"c\":\"red\",\"b\":2},3]", 4},
|
||||||
|
{"{\"d\":\"red\",\"e\":[1,2,3,4],\"f\":5}", "{\"d\":\"red\",\"e\":[1,2,3,4],\"f\":5}", 0},
|
||||||
|
{"[1,\"red\",5]", "[1,\"red\",5]", 6},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartTwo([]string{tt.input})
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
48
internal/2015/DayTwo/code.go
Normal file
48
internal/2015/DayTwo/code.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2015D2", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
total := 0
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, "x")
|
||||||
|
length, _ := strconv.Atoi(parts[0])
|
||||||
|
width, _ := strconv.Atoi(parts[1])
|
||||||
|
height, _ := strconv.Atoi(parts[2])
|
||||||
|
total += 2*length*width + 2*width*height + 2*height*length + min(length*width, width*height, height*length)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
total := 0
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, "x")
|
||||||
|
length, _ := strconv.Atoi(parts[0])
|
||||||
|
width, _ := strconv.Atoi(parts[1])
|
||||||
|
height, _ := strconv.Atoi(parts[2])
|
||||||
|
|
||||||
|
smallest := min(length, width, height)
|
||||||
|
middle := length + width + height - smallest - max(length, width, height)
|
||||||
|
|
||||||
|
perimeter := 2 * (smallest + middle)
|
||||||
|
volume := length * width * height
|
||||||
|
|
||||||
|
total += perimeter + volume
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
43
internal/2015/DayTwo/code_test.go
Normal file
43
internal/2015/DayTwo/code_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"2x3x4", "2x3x4", 58},
|
||||||
|
{"1x1x10", "1x1x10", 43},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartOne([]string{tt.input})
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{"2x3x4", "2x3x4", 34},
|
||||||
|
{"1x1x10", "1x1x10", 14},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartTwo([]string{tt.input})
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
18
internal/2015/register.go
Normal file
18
internal/2015/register.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
package year2015
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2015/DayEight"
|
||||||
|
_ "advent-of-code/internal/2015/DayEleven"
|
||||||
|
_ "advent-of-code/internal/2015/DayFive"
|
||||||
|
_ "advent-of-code/internal/2015/DayFour"
|
||||||
|
_ "advent-of-code/internal/2015/DayFourteen"
|
||||||
|
_ "advent-of-code/internal/2015/DayNine"
|
||||||
|
_ "advent-of-code/internal/2015/DayOne"
|
||||||
|
_ "advent-of-code/internal/2015/DaySeven"
|
||||||
|
_ "advent-of-code/internal/2015/DaySix"
|
||||||
|
_ "advent-of-code/internal/2015/DayTen"
|
||||||
|
_ "advent-of-code/internal/2015/DayThirteen"
|
||||||
|
_ "advent-of-code/internal/2015/DayThree"
|
||||||
|
_ "advent-of-code/internal/2015/DayTwelve"
|
||||||
|
_ "advent-of-code/internal/2015/DayTwo"
|
||||||
|
)
|
||||||
73
internal/2016/DayFive/code.go
Normal file
73
internal/2016/DayFive/code.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2016D5", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data string) int {
|
||||||
|
doorIDBytes := []byte(data)
|
||||||
|
doorIDLen := len(doorIDBytes)
|
||||||
|
input := make([]byte, doorIDLen, doorIDLen+20)
|
||||||
|
copy(input, doorIDBytes)
|
||||||
|
password := make([]byte, 0, 8)
|
||||||
|
index := 0
|
||||||
|
hexChars := "0123456789abcdef"
|
||||||
|
|
||||||
|
for len(password) < 8 {
|
||||||
|
indexBytes := strconv.AppendInt(input[:doorIDLen], int64(index), 10)
|
||||||
|
hash := md5.Sum(indexBytes)
|
||||||
|
|
||||||
|
if hash[0] == 0 && hash[1] == 0 && hash[2] < 16 {
|
||||||
|
char := hexChars[hash[2]&0x0F]
|
||||||
|
password = append(password, char)
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(password))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data string) int {
|
||||||
|
doorIDBytes := []byte(data)
|
||||||
|
doorIDLen := len(doorIDBytes)
|
||||||
|
input := make([]byte, doorIDLen, doorIDLen+20)
|
||||||
|
copy(input, doorIDBytes)
|
||||||
|
password := make([]byte, 8)
|
||||||
|
filled := make([]bool, 8)
|
||||||
|
filledCount := 0
|
||||||
|
index := 0
|
||||||
|
hexChars := "0123456789abcdef"
|
||||||
|
|
||||||
|
for filledCount < 8 {
|
||||||
|
indexBytes := strconv.AppendInt(input[:doorIDLen], int64(index), 10)
|
||||||
|
hash := md5.Sum(indexBytes)
|
||||||
|
|
||||||
|
if hash[0] == 0 && hash[1] == 0 && hash[2] < 16 {
|
||||||
|
position := int(hash[2] & 0x0F)
|
||||||
|
if position < 8 && !filled[position] {
|
||||||
|
char := hexChars[hash[3]>>4]
|
||||||
|
password[position] = char
|
||||||
|
filled[position] = true
|
||||||
|
filledCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(password))
|
||||||
|
return 0
|
||||||
|
}
|
||||||
58
internal/2016/DayFive/code_test.go
Normal file
58
internal/2016/DayFive/code_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testInput = "abc"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := "18f47a30"
|
||||||
|
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
PartOne(testInput)
|
||||||
|
|
||||||
|
_ = w.Close()
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
_, _ = buffer.ReadFrom(r)
|
||||||
|
got := strings.TrimSpace(buffer.String())
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() printed %q, want %q", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := "05ace8e3"
|
||||||
|
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
PartTwo(testInput)
|
||||||
|
|
||||||
|
_ = w.Close()
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
_, _ = buffer.ReadFrom(r)
|
||||||
|
got := strings.TrimSpace(buffer.String())
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() printed %q, want %q", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
119
internal/2016/DayFour/code.go
Normal file
119
internal/2016/DayFour/code.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2016D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidRoom(encryptedName string, expectedChecksum string) bool {
|
||||||
|
letterFrequency := [26]int{}
|
||||||
|
|
||||||
|
for _, character := range encryptedName {
|
||||||
|
if character >= 'a' && character <= 'z' {
|
||||||
|
letterFrequency[character-'a']++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type letterFrequencyPair struct {
|
||||||
|
letter rune
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
letterFrequencyPairs := make([]letterFrequencyPair, 0, 26)
|
||||||
|
for index, frequency := range letterFrequency {
|
||||||
|
if frequency > 0 {
|
||||||
|
letterFrequencyPairs = append(letterFrequencyPairs, letterFrequencyPair{
|
||||||
|
letter: rune('a' + index),
|
||||||
|
count: frequency,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(letterFrequencyPairs, func(i, j int) bool {
|
||||||
|
if letterFrequencyPairs[i].count != letterFrequencyPairs[j].count {
|
||||||
|
return letterFrequencyPairs[i].count > letterFrequencyPairs[j].count
|
||||||
|
}
|
||||||
|
return letterFrequencyPairs[i].letter < letterFrequencyPairs[j].letter
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(letterFrequencyPairs) < 5 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := range 5 {
|
||||||
|
if letterFrequencyPairs[index].letter != rune(expectedChecksum[index]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptRoomName(encryptedName string, sectorID int) string {
|
||||||
|
result := strings.Builder{}
|
||||||
|
result.Grow(len(encryptedName))
|
||||||
|
shift := sectorID % 26
|
||||||
|
for _, char := range encryptedName {
|
||||||
|
if char == '-' {
|
||||||
|
result.WriteByte(' ')
|
||||||
|
} else if char >= 'a' && char <= 'z' {
|
||||||
|
shifted := ((int(char-'a') + shift) % 26) + 'a'
|
||||||
|
result.WriteByte(byte(shifted))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var roomPattern = regexp.MustCompile(`^(.+)-(\d+)(?:\[([a-z]{5})\])?$`)
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
sum := 0
|
||||||
|
for _, line := range data {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matches := roomPattern.FindStringSubmatch(line)
|
||||||
|
if len(matches) < 4 || matches[3] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
encryptedName := matches[1]
|
||||||
|
sectorIdentifier, _ := strconv.Atoi(matches[2])
|
||||||
|
actualChecksum := matches[3]
|
||||||
|
if isValidRoom(encryptedName, actualChecksum) {
|
||||||
|
sum += sectorIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
for _, line := range data {
|
||||||
|
matches := roomPattern.FindStringSubmatch(line)
|
||||||
|
encryptedName := matches[1]
|
||||||
|
sectorIdentifier, _ := strconv.Atoi(matches[2])
|
||||||
|
checksum := matches[3]
|
||||||
|
|
||||||
|
if !isValidRoom(encryptedName, checksum) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted := decryptRoomName(encryptedName, sectorIdentifier)
|
||||||
|
if strings.Contains(decrypted, "northpole") {
|
||||||
|
return sectorIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
29
internal/2016/DayFour/code_test.go
Normal file
29
internal/2016/DayFour/code_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"aaaaa-bbb-z-y-x-123[abxyz]",
|
||||||
|
"a-b-c-d-e-f-g-h-987[abcde]",
|
||||||
|
"not-a-real-room-404[oarel]",
|
||||||
|
"totally-real-room-200[decoy]",
|
||||||
|
"ijmockjgz-storage-5[gjoac]",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 1519
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 5
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
91
internal/2016/DayOne/code.go
Normal file
91
internal/2016/DayOne/code.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2016D1", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
type position struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(n int) int {
|
||||||
|
if n < 0 {
|
||||||
|
return -n
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
parts := strings.Split(string(content), ",")
|
||||||
|
for i, p := range parts {
|
||||||
|
parts[i] = strings.TrimSpace(p)
|
||||||
|
}
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
x, y := 0, 0
|
||||||
|
directions := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
|
||||||
|
currentDirection := 0
|
||||||
|
|
||||||
|
for _, instruction := range data {
|
||||||
|
turn := instruction[0]
|
||||||
|
distance, _ := strconv.Atoi(instruction[1:])
|
||||||
|
|
||||||
|
switch turn {
|
||||||
|
case 'R':
|
||||||
|
currentDirection = (currentDirection + 1) % 4
|
||||||
|
case 'L':
|
||||||
|
currentDirection = (currentDirection + 3) % 4
|
||||||
|
}
|
||||||
|
|
||||||
|
x += directions[currentDirection][0] * distance
|
||||||
|
y += directions[currentDirection][1] * distance
|
||||||
|
}
|
||||||
|
|
||||||
|
return abs(x) + abs(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
x, y := 0, 0
|
||||||
|
directions := [][]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}
|
||||||
|
currentDirection := 0
|
||||||
|
visited := make(map[position]bool)
|
||||||
|
visited[position{0, 0}] = true
|
||||||
|
|
||||||
|
for _, instruction := range data {
|
||||||
|
turn := instruction[0]
|
||||||
|
distance, _ := strconv.Atoi(instruction[1:])
|
||||||
|
|
||||||
|
switch turn {
|
||||||
|
case 'R':
|
||||||
|
currentDirection = (currentDirection + 1) % 4
|
||||||
|
case 'L':
|
||||||
|
currentDirection = (currentDirection + 3) % 4
|
||||||
|
}
|
||||||
|
|
||||||
|
directionX := directions[currentDirection][0]
|
||||||
|
directionY := directions[currentDirection][1]
|
||||||
|
|
||||||
|
for range distance {
|
||||||
|
x += directionX
|
||||||
|
y += directionY
|
||||||
|
pos := position{x, y}
|
||||||
|
if visited[pos] {
|
||||||
|
return abs(x) + abs(y)
|
||||||
|
}
|
||||||
|
visited[pos] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
47
internal/2016/DayOne/code_test.go
Normal file
47
internal/2016/DayOne/code_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Example 1",
|
||||||
|
input: []string{"R2", "L3"},
|
||||||
|
expected: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Example 2",
|
||||||
|
input: []string{"R2", "R2", "R2"},
|
||||||
|
expected: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Example 3",
|
||||||
|
input: []string{"R5", "L5", "R5", "R3"},
|
||||||
|
expected: 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := PartOne(tt.input)
|
||||||
|
if got != tt.expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, tt.expected)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
input := []string{"R8", "R4", "R4", "R8"}
|
||||||
|
expected := 4
|
||||||
|
got := PartTwo(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
49
internal/2016/DayThree/code.go
Normal file
49
internal/2016/DayThree/code.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2016D3", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) [][3]int {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
var result [][3]int
|
||||||
|
for _, line := range lines {
|
||||||
|
var a, b, c int
|
||||||
|
fmt.Sscanf(line, "%d %d %d", &a, &b, &c)
|
||||||
|
result = append(result, [3]int{a, b, c})
|
||||||
|
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data [][3]int) int {
|
||||||
|
count := 0
|
||||||
|
for _, triangle := range data {
|
||||||
|
a, b, c := triangle[0], triangle[1], triangle[2]
|
||||||
|
if a+b > c && a+c > b && b+c > a {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data [][3]int) int {
|
||||||
|
count := 0
|
||||||
|
for idx := 0; idx < len(data)-2; idx += 3 {
|
||||||
|
for column := range 3 {
|
||||||
|
a, b, c := data[idx][column], data[idx+1][column], data[idx+2][column]
|
||||||
|
if a+b > c && a+c > b && b+c > a {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
28
internal/2016/DayThree/code_test.go
Normal file
28
internal/2016/DayThree/code_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
input := [][3]int{{5, 10, 25}}
|
||||||
|
expected := 0
|
||||||
|
got := PartOne(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
input := [][3]int{
|
||||||
|
{101, 301, 501},
|
||||||
|
{102, 302, 502},
|
||||||
|
{103, 303, 503},
|
||||||
|
{201, 401, 601},
|
||||||
|
{202, 402, 602},
|
||||||
|
{203, 403, 603},
|
||||||
|
}
|
||||||
|
expected := 6
|
||||||
|
got := PartTwo(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
101
internal/2016/DayTwo/code.go
Normal file
101
internal/2016/DayTwo/code.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2016D2", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(instructions []string) int {
|
||||||
|
row, column := 1, 1
|
||||||
|
|
||||||
|
keypad := [3][3]int{
|
||||||
|
{1, 2, 3},
|
||||||
|
{4, 5, 6},
|
||||||
|
{7, 8, 9},
|
||||||
|
}
|
||||||
|
code := 0
|
||||||
|
|
||||||
|
for _, line := range instructions {
|
||||||
|
for _, move := range line {
|
||||||
|
switch move {
|
||||||
|
case 'U':
|
||||||
|
if row > 0 {
|
||||||
|
row--
|
||||||
|
}
|
||||||
|
case 'D':
|
||||||
|
if row < 2 {
|
||||||
|
row++
|
||||||
|
}
|
||||||
|
case 'L':
|
||||||
|
if column > 0 {
|
||||||
|
column--
|
||||||
|
}
|
||||||
|
case 'R':
|
||||||
|
if column < 2 {
|
||||||
|
column++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code = code*10 + keypad[row][column]
|
||||||
|
}
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(instructions []string) int {
|
||||||
|
row, column := 2, 0
|
||||||
|
|
||||||
|
keypad := [5][5]int{
|
||||||
|
{0, 0, 1, 0, 0},
|
||||||
|
{0, 2, 3, 4, 0},
|
||||||
|
{5, 6, 7, 8, 9},
|
||||||
|
{0, 10, 11, 12, 0},
|
||||||
|
{0, 0, 13, 0, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
isValidPosition := func(r, c int) bool {
|
||||||
|
return r >= 0 && r < 5 && c >= 0 && c < 5 && keypad[r][c] != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var codeBuilder strings.Builder
|
||||||
|
|
||||||
|
for _, line := range instructions {
|
||||||
|
for _, move := range line {
|
||||||
|
newRow, newColumn := row, column
|
||||||
|
switch move {
|
||||||
|
case 'U':
|
||||||
|
newRow--
|
||||||
|
case 'D':
|
||||||
|
newRow++
|
||||||
|
case 'L':
|
||||||
|
newColumn--
|
||||||
|
case 'R':
|
||||||
|
newColumn++
|
||||||
|
}
|
||||||
|
if isValidPosition(newRow, newColumn) {
|
||||||
|
row, column = newRow, newColumn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value := keypad[row][column]
|
||||||
|
if value < 10 {
|
||||||
|
codeBuilder.WriteByte(byte('0' + value))
|
||||||
|
} else {
|
||||||
|
codeBuilder.WriteByte(byte('A' + value - 10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code := codeBuilder.String()
|
||||||
|
fmt.Println(code)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
42
internal/2016/DayTwo/code_test.go
Normal file
42
internal/2016/DayTwo/code_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testInput = []string{"ULL", "RRDDD", "LURDL", "UUUUD"}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 1985
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := "5DB3"
|
||||||
|
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
PartTwo(testInput)
|
||||||
|
|
||||||
|
_ = w.Close()
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
_, _ = buffer.ReadFrom(r)
|
||||||
|
got := strings.TrimSpace(buffer.String())
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() printed %q, want %q", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
internal/2016/register.go
Normal file
9
internal/2016/register.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package year2016
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2016/DayFive"
|
||||||
|
_ "advent-of-code/internal/2016/DayFour"
|
||||||
|
_ "advent-of-code/internal/2016/DayOne"
|
||||||
|
_ "advent-of-code/internal/2016/DayThree"
|
||||||
|
_ "advent-of-code/internal/2016/DayTwo"
|
||||||
|
)
|
||||||
64
internal/2018/DayFive/code.go
Normal file
64
internal/2018/DayFive/code.go
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2018D5", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return string(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reactPolymer(data string) int {
|
||||||
|
stack := []rune{}
|
||||||
|
|
||||||
|
for _, char := range data {
|
||||||
|
if len(stack) == 0 {
|
||||||
|
stack = append(stack, char)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
top := stack[len(stack)-1]
|
||||||
|
|
||||||
|
if unicode.ToLower(top) == unicode.ToLower(char) && top != char {
|
||||||
|
stack = stack[:len(stack)-1]
|
||||||
|
} else {
|
||||||
|
stack = append(stack, char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data string) int {
|
||||||
|
return reactPolymer(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data string) int {
|
||||||
|
unitTypes := make(map[rune]bool)
|
||||||
|
for _, char := range data {
|
||||||
|
unitTypes[unicode.ToLower(char)] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
shortestPolymerLength := len(data)
|
||||||
|
for unitType := range unitTypes {
|
||||||
|
filtered := make([]rune, 0, len(data))
|
||||||
|
for _, char := range data {
|
||||||
|
if unicode.ToLower(char) != unitType {
|
||||||
|
filtered = append(filtered, char)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
length := reactPolymer(string(filtered))
|
||||||
|
if length < shortestPolymerLength {
|
||||||
|
shortestPolymerLength = length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return shortestPolymerLength
|
||||||
|
}
|
||||||
21
internal/2018/DayFive/code_test.go
Normal file
21
internal/2018/DayFive/code_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = "dabAcCaCBAcCcaDA"
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 10
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 4
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
133
internal/2018/DayFour/code.go
Normal file
133
internal/2018/DayFour/code.go
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
guardRegex = regexp.MustCompile(`Guard #(\d+) begins shift`)
|
||||||
|
timeLayout = "2006-01-02 15:04"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2018D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
type record struct {
|
||||||
|
timestamp time.Time
|
||||||
|
action string
|
||||||
|
guardID int
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
records := make([]record, 0, len(data))
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
timeStr, action, _ := strings.Cut(line[1:], "] ")
|
||||||
|
timestamp, _ := time.Parse(timeLayout, timeStr)
|
||||||
|
guardID := -1
|
||||||
|
if matches := guardRegex.FindStringSubmatch(action); matches != nil {
|
||||||
|
guardID, _ = strconv.Atoi(matches[1])
|
||||||
|
}
|
||||||
|
records = append(records, record{timestamp, action, guardID})
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(records, func(a, b record) int {
|
||||||
|
return a.timestamp.Compare(b.timestamp)
|
||||||
|
})
|
||||||
|
|
||||||
|
guardSleepMinutes := make(map[int]int)
|
||||||
|
guardMinuteCount := make(map[int]map[int]int)
|
||||||
|
currentGuard := -1
|
||||||
|
|
||||||
|
for i := range records {
|
||||||
|
if records[i].guardID != -1 {
|
||||||
|
currentGuard = records[i].guardID
|
||||||
|
if guardMinuteCount[currentGuard] == nil {
|
||||||
|
guardMinuteCount[currentGuard] = make(map[int]int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if records[i].action == "falls asleep" && i+1 < len(records) {
|
||||||
|
start, end := records[i].timestamp.Minute(), records[i+1].timestamp.Minute()
|
||||||
|
guardSleepMinutes[currentGuard] += end - start
|
||||||
|
for m := start; m < end; m++ {
|
||||||
|
guardMinuteCount[currentGuard][m]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxGuard, maxMinutes := -1, 0
|
||||||
|
for id, minutes := range guardSleepMinutes {
|
||||||
|
if minutes > maxMinutes {
|
||||||
|
maxGuard, maxMinutes = id, minutes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxMinute, maxCount := -1, 0
|
||||||
|
for m, count := range guardMinuteCount[maxGuard] {
|
||||||
|
if count > maxCount {
|
||||||
|
maxMinute, maxCount = m, count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxGuard * maxMinute
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
records := make([]record, 0, len(data))
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
timeStr, action, _ := strings.Cut(line[1:], "] ")
|
||||||
|
timestamp, _ := time.Parse(timeLayout, timeStr)
|
||||||
|
guardID := -1
|
||||||
|
if matches := guardRegex.FindStringSubmatch(action); matches != nil {
|
||||||
|
guardID, _ = strconv.Atoi(matches[1])
|
||||||
|
}
|
||||||
|
records = append(records, record{timestamp, action, guardID})
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(records, func(a, b record) int {
|
||||||
|
return a.timestamp.Compare(b.timestamp)
|
||||||
|
})
|
||||||
|
|
||||||
|
guardMinuteCount := make(map[int]map[int]int)
|
||||||
|
currentGuard := -1
|
||||||
|
|
||||||
|
for idx := range records {
|
||||||
|
if records[idx].guardID != -1 {
|
||||||
|
currentGuard = records[idx].guardID
|
||||||
|
if guardMinuteCount[currentGuard] == nil {
|
||||||
|
guardMinuteCount[currentGuard] = make(map[int]int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if records[idx].action == "falls asleep" && idx+1 < len(records) {
|
||||||
|
start, end := records[idx].timestamp.Minute(), records[idx+1].timestamp.Minute()
|
||||||
|
for m := start; m < end; m++ {
|
||||||
|
guardMinuteCount[currentGuard][m]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxGuard, maxMinute, maxCount := -1, -1, 0
|
||||||
|
for guardID, minuteCounts := range guardMinuteCount {
|
||||||
|
for minute, count := range minuteCounts {
|
||||||
|
if count > maxCount {
|
||||||
|
maxGuard, maxMinute, maxCount = guardID, minute, count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxGuard * maxMinute
|
||||||
|
}
|
||||||
39
internal/2018/DayFour/code_test.go
Normal file
39
internal/2018/DayFour/code_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"[1518-11-01 00:00] Guard #10 begins shift",
|
||||||
|
"[1518-11-01 00:05] falls asleep",
|
||||||
|
"[1518-11-01 00:25] wakes up",
|
||||||
|
"[1518-11-01 00:30] falls asleep",
|
||||||
|
"[1518-11-01 00:55] wakes up",
|
||||||
|
"[1518-11-01 23:58] Guard #99 begins shift",
|
||||||
|
"[1518-11-02 00:40] falls asleep",
|
||||||
|
"[1518-11-02 00:50] wakes up",
|
||||||
|
"[1518-11-03 00:05] Guard #10 begins shift",
|
||||||
|
"[1518-11-03 00:24] falls asleep",
|
||||||
|
"[1518-11-03 00:29] wakes up",
|
||||||
|
"[1518-11-04 00:02] Guard #99 begins shift",
|
||||||
|
"[1518-11-04 00:36] falls asleep",
|
||||||
|
"[1518-11-04 00:46] wakes up",
|
||||||
|
"[1518-11-05 00:03] Guard #99 begins shift",
|
||||||
|
"[1518-11-05 00:45] falls asleep",
|
||||||
|
"[1518-11-05 00:55] wakes up",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 240
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 4455
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
45
internal/2018/DayOne/code.go
Normal file
45
internal/2018/DayOne/code.go
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2018D1", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []int {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
var data []int
|
||||||
|
for line := range strings.SplitSeq(string(content), "\n") {
|
||||||
|
num, _ := strconv.Atoi(line)
|
||||||
|
data = append(data, num)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []int) int {
|
||||||
|
sum := 0
|
||||||
|
for _, num := range data {
|
||||||
|
sum += num
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []int) int {
|
||||||
|
seen := make(map[int]bool)
|
||||||
|
sum := 0
|
||||||
|
seen[0] = true
|
||||||
|
for {
|
||||||
|
for _, num := range data {
|
||||||
|
sum += num
|
||||||
|
if seen[sum] {
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
seen[sum] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
internal/2018/DayOne/code_test.go
Normal file
26
internal/2018/DayOne/code_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []int{
|
||||||
|
+1,
|
||||||
|
-2,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 3
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 2
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
101
internal/2018/DayThree/code.go
Normal file
101
internal/2018/DayThree/code.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2018D3", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
coverage := make(map[string]int)
|
||||||
|
re := regexp.MustCompile(`#\d+ @ (\d+),(\d+): (\d+)x(\d+)`)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
matches := re.FindStringSubmatch(line)
|
||||||
|
|
||||||
|
left, _ := strconv.Atoi(matches[1])
|
||||||
|
top, _ := strconv.Atoi(matches[2])
|
||||||
|
width, _ := strconv.Atoi(matches[3])
|
||||||
|
height, _ := strconv.Atoi(matches[4])
|
||||||
|
|
||||||
|
for x := left; x < left+width; x++ {
|
||||||
|
for y := top; y < top+height; y++ {
|
||||||
|
key := fmt.Sprintf("%d,%d", x, y)
|
||||||
|
coverage[key]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overlapCount := 0
|
||||||
|
for _, count := range coverage {
|
||||||
|
if count >= 2 {
|
||||||
|
overlapCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlapCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
coverage := make(map[string]int)
|
||||||
|
re := regexp.MustCompile(`#\d+ @ (\d+),(\d+): (\d+)x(\d+)`)
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
matches := re.FindStringSubmatch(line)
|
||||||
|
|
||||||
|
left, _ := strconv.Atoi(matches[1])
|
||||||
|
top, _ := strconv.Atoi(matches[2])
|
||||||
|
width, _ := strconv.Atoi(matches[3])
|
||||||
|
height, _ := strconv.Atoi(matches[4])
|
||||||
|
|
||||||
|
for x := left; x < left+width; x++ {
|
||||||
|
for y := top; y < top+height; y++ {
|
||||||
|
key := fmt.Sprintf("%d,%d", x, y)
|
||||||
|
coverage[key]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reWithID := regexp.MustCompile(`#(\d+) @ (\d+),(\d+): (\d+)x(\d+)`)
|
||||||
|
for _, line := range data {
|
||||||
|
matches := reWithID.FindStringSubmatch(line)
|
||||||
|
if len(matches) != 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
id, _ := strconv.Atoi(matches[1])
|
||||||
|
left, _ := strconv.Atoi(matches[2])
|
||||||
|
top, _ := strconv.Atoi(matches[3])
|
||||||
|
width, _ := strconv.Atoi(matches[4])
|
||||||
|
height, _ := strconv.Atoi(matches[5])
|
||||||
|
|
||||||
|
overlaps := false
|
||||||
|
for x := left; x < left+width && !overlaps; x++ {
|
||||||
|
for y := top; y < top+height; y++ {
|
||||||
|
key := fmt.Sprintf("%d,%d", x, y)
|
||||||
|
if coverage[key] > 1 {
|
||||||
|
overlaps = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !overlaps {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
25
internal/2018/DayThree/code_test.go
Normal file
25
internal/2018/DayThree/code_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"#1 @ 1,3: 4x4",
|
||||||
|
"#2 @ 3,1: 4x4",
|
||||||
|
"#3 @ 5,5: 2x2",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 4
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 3
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
81
internal/2018/DayTwo/code.go
Normal file
81
internal/2018/DayTwo/code.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2018D2", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
countWithTwo := 0
|
||||||
|
countWithThree := 0
|
||||||
|
|
||||||
|
for _, boxID := range data {
|
||||||
|
charCounts := make(map[rune]int)
|
||||||
|
for _, char := range boxID {
|
||||||
|
charCounts[char]++
|
||||||
|
}
|
||||||
|
|
||||||
|
hasExactlyTwo := false
|
||||||
|
hasExactlyThree := false
|
||||||
|
|
||||||
|
for _, count := range charCounts {
|
||||||
|
switch count {
|
||||||
|
case 2:
|
||||||
|
hasExactlyTwo = true
|
||||||
|
case 3:
|
||||||
|
hasExactlyThree = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasExactlyTwo {
|
||||||
|
countWithTwo++
|
||||||
|
}
|
||||||
|
if hasExactlyThree {
|
||||||
|
countWithThree++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return countWithTwo * countWithThree
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
for idx := range data {
|
||||||
|
for otherIdx := idx + 1; otherIdx < len(data); otherIdx++ {
|
||||||
|
if data[idx] == "" || data[otherIdx] == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
differenceCount := 0
|
||||||
|
differingPosition := -1
|
||||||
|
|
||||||
|
if len(data[idx]) != len(data[otherIdx]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for position := 0; position < len(data[idx]); position++ {
|
||||||
|
if data[idx][position] != data[otherIdx][position] {
|
||||||
|
differenceCount++
|
||||||
|
differingPosition = position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if differenceCount == 1 {
|
||||||
|
common := data[idx][:differingPosition] + data[idx][differingPosition+1:]
|
||||||
|
fmt.Println(common)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
58
internal/2018/DayTwo/code_test.go
Normal file
58
internal/2018/DayTwo/code_test.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
input := []string{
|
||||||
|
"abcdef",
|
||||||
|
"bababc",
|
||||||
|
"abbcde",
|
||||||
|
"abcccd",
|
||||||
|
"aabcdd",
|
||||||
|
"abcdee",
|
||||||
|
"ababab",
|
||||||
|
}
|
||||||
|
expected := 12
|
||||||
|
got := PartOne(input)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
input := []string{
|
||||||
|
"abcde",
|
||||||
|
"fghij",
|
||||||
|
"klmno",
|
||||||
|
"pqrst",
|
||||||
|
"fguij",
|
||||||
|
"axcye",
|
||||||
|
"wvxyz",
|
||||||
|
}
|
||||||
|
expected := "fgij"
|
||||||
|
|
||||||
|
oldStdout := os.Stdout
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create pipe: %v", err)
|
||||||
|
}
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
PartTwo(input)
|
||||||
|
|
||||||
|
_ = w.Close()
|
||||||
|
os.Stdout = oldStdout
|
||||||
|
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
_, _ = buffer.ReadFrom(r)
|
||||||
|
got := strings.TrimSpace(buffer.String())
|
||||||
|
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() printed %q, want %q", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
9
internal/2018/register.go
Normal file
9
internal/2018/register.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package year2018
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2018/DayFive"
|
||||||
|
_ "advent-of-code/internal/2018/DayFour"
|
||||||
|
_ "advent-of-code/internal/2018/DayOne"
|
||||||
|
_ "advent-of-code/internal/2018/DayThree"
|
||||||
|
_ "advent-of-code/internal/2018/DayTwo"
|
||||||
|
)
|
||||||
@@ -91,7 +91,7 @@ func PartTwo(data []string) int {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, c := range value[1:] {
|
for _, c := range value[1:] {
|
||||||
if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')) {
|
if (c < '0' || c > '9') && (c < 'a' || c > 'f') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,8 @@ func init() {
|
|||||||
func ParseInput(filepath string) []string {
|
func ParseInput(filepath string) []string {
|
||||||
content, _ := os.ReadFile(filepath)
|
content, _ := os.ReadFile(filepath)
|
||||||
lines := strings.Split(string(content), "\n")
|
lines := strings.Split(string(content), "\n")
|
||||||
var data []string
|
data := make([]string, 0, len(lines))
|
||||||
for _, line := range lines {
|
return append(data, lines...)
|
||||||
data = append(data, line)
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func PartOne(data []string) int {
|
func PartOne(data []string) int {
|
||||||
|
|||||||
12
internal/2020/register.go
Normal file
12
internal/2020/register.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package year2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2020/DayEight"
|
||||||
|
_ "advent-of-code/internal/2020/DayFive"
|
||||||
|
_ "advent-of-code/internal/2020/DayFour"
|
||||||
|
_ "advent-of-code/internal/2020/DayOne"
|
||||||
|
_ "advent-of-code/internal/2020/DaySeven"
|
||||||
|
_ "advent-of-code/internal/2020/DaySix"
|
||||||
|
_ "advent-of-code/internal/2020/DayThree"
|
||||||
|
_ "advent-of-code/internal/2020/DayTwo"
|
||||||
|
)
|
||||||
155
internal/2021/DayFour/code.go
Normal file
155
internal/2021/DayFour/code.go
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
const boardSize = 5
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2021D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(strings.TrimSpace(string(content)), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
type board struct {
|
||||||
|
numbers [boardSize][boardSize]int
|
||||||
|
positionMap map[int][2]int
|
||||||
|
rowCounts [boardSize]int
|
||||||
|
columnCounts [boardSize]int
|
||||||
|
marked map[int]bool
|
||||||
|
won bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBoard() board {
|
||||||
|
return board{
|
||||||
|
positionMap: make(map[int][2]int, boardSize*boardSize),
|
||||||
|
marked: make(map[int]bool),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseBoards(lines []string) []board {
|
||||||
|
var boards []board
|
||||||
|
current := newBoard()
|
||||||
|
row := 0
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
if row == boardSize {
|
||||||
|
boards = append(boards, current)
|
||||||
|
current = newBoard()
|
||||||
|
row = 0
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
for column, field := range fields {
|
||||||
|
number, _ := strconv.Atoi(field)
|
||||||
|
current.numbers[row][column] = number
|
||||||
|
current.positionMap[number] = [2]int{row, column}
|
||||||
|
}
|
||||||
|
row++
|
||||||
|
}
|
||||||
|
|
||||||
|
if row == boardSize {
|
||||||
|
boards = append(boards, current)
|
||||||
|
}
|
||||||
|
|
||||||
|
return boards
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *board) mark(number int) bool {
|
||||||
|
if b.won {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
position, exists := b.positionMap[number]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
b.marked[number] = true
|
||||||
|
row, column := position[0], position[1]
|
||||||
|
b.rowCounts[row]++
|
||||||
|
b.columnCounts[column]++
|
||||||
|
|
||||||
|
if b.rowCounts[row] == boardSize || b.columnCounts[column] == boardSize {
|
||||||
|
b.won = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *board) sumUnmarked() int {
|
||||||
|
sum := 0
|
||||||
|
for row := range boardSize {
|
||||||
|
for column := range boardSize {
|
||||||
|
number := b.numbers[row][column]
|
||||||
|
if !b.marked[number] {
|
||||||
|
sum += number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumbers(line string) []int {
|
||||||
|
numbersStr := strings.Split(line, ",")
|
||||||
|
numbers := make([]int, 0, len(numbersStr))
|
||||||
|
for _, numStr := range numbersStr {
|
||||||
|
num, _ := strconv.Atoi(numStr)
|
||||||
|
numbers = append(numbers, num)
|
||||||
|
}
|
||||||
|
return numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
numbers := parseNumbers(data[0])
|
||||||
|
boards := parseBoards(data[1:])
|
||||||
|
|
||||||
|
for _, number := range numbers {
|
||||||
|
for idx := range boards {
|
||||||
|
if boards[idx].mark(number) {
|
||||||
|
return boards[idx].sumUnmarked() * number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
numbers := parseNumbers(data[0])
|
||||||
|
boards := parseBoards(data[1:])
|
||||||
|
|
||||||
|
wonCount := 0
|
||||||
|
totalBoards := len(boards)
|
||||||
|
var lastWinner *board
|
||||||
|
var lastNumber int
|
||||||
|
|
||||||
|
for _, number := range numbers {
|
||||||
|
for idx := range boards {
|
||||||
|
if boards[idx].mark(number) {
|
||||||
|
wonCount++
|
||||||
|
lastWinner = &boards[idx]
|
||||||
|
lastNumber = number
|
||||||
|
|
||||||
|
if wonCount == totalBoards {
|
||||||
|
return lastWinner.sumUnmarked() * lastNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
41
internal/2021/DayFour/code_test.go
Normal file
41
internal/2021/DayFour/code_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1",
|
||||||
|
"",
|
||||||
|
"22 13 17 11 0",
|
||||||
|
" 8 2 23 4 24",
|
||||||
|
"21 9 14 16 7",
|
||||||
|
" 6 10 3 18 5",
|
||||||
|
" 1 12 20 15 19",
|
||||||
|
"",
|
||||||
|
" 3 15 0 2 22",
|
||||||
|
" 9 18 13 17 5",
|
||||||
|
"19 8 7 25 23",
|
||||||
|
"20 11 10 24 4",
|
||||||
|
"14 21 16 12 6",
|
||||||
|
"",
|
||||||
|
"14 21 17 24 4",
|
||||||
|
"10 16 15 9 19",
|
||||||
|
"18 8 23 26 20",
|
||||||
|
"22 11 13 6 5",
|
||||||
|
" 2 0 12 3 7",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 4512
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 1924
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
internal/2021/register.go
Normal file
8
internal/2021/register.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package year2021
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2021/DayFour"
|
||||||
|
_ "advent-of-code/internal/2021/DayOne"
|
||||||
|
_ "advent-of-code/internal/2021/DayThree"
|
||||||
|
_ "advent-of-code/internal/2021/DayTwo"
|
||||||
|
)
|
||||||
54
internal/2022/DayFour/code.go
Normal file
54
internal/2022/DayFour/code.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2022D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
count := 0
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, ",")
|
||||||
|
|
||||||
|
var firstRangeStart, firstRangeEnd, secondRangeStart, secondRangeEnd int
|
||||||
|
fmt.Sscanf(parts[0], "%d-%d", &firstRangeStart, &firstRangeEnd)
|
||||||
|
fmt.Sscanf(parts[1], "%d-%d", &secondRangeStart, &secondRangeEnd)
|
||||||
|
|
||||||
|
minStart := min(firstRangeStart, secondRangeStart)
|
||||||
|
maxEnd := max(firstRangeEnd, secondRangeEnd)
|
||||||
|
|
||||||
|
if (minStart == firstRangeStart && maxEnd == firstRangeEnd) || (minStart == secondRangeStart && maxEnd == secondRangeEnd) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
count := 0
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, ",")
|
||||||
|
|
||||||
|
var firstRangeStart, firstRangeEnd, secondRangeStart, secondRangeEnd int
|
||||||
|
fmt.Sscanf(parts[0], "%d-%d", &firstRangeStart, &firstRangeEnd)
|
||||||
|
fmt.Sscanf(parts[1], "%d-%d", &secondRangeStart, &secondRangeEnd)
|
||||||
|
|
||||||
|
if firstRangeStart <= secondRangeEnd && secondRangeStart <= firstRangeEnd {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
28
internal/2022/DayFour/code_test.go
Normal file
28
internal/2022/DayFour/code_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"2-4,6-8",
|
||||||
|
"2-3,4-5",
|
||||||
|
"5-7,7-9",
|
||||||
|
"2-8,3-7",
|
||||||
|
"6-6,4-6",
|
||||||
|
"2-6,4-8",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 2
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 4
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
87
internal/2022/DayThree/code.go
Normal file
87
internal/2022/DayThree/code.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2022D3", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildItemSet(items string) map[rune]bool {
|
||||||
|
itemSet := make(map[rune]bool)
|
||||||
|
for _, item := range items {
|
||||||
|
itemSet[item] = true
|
||||||
|
}
|
||||||
|
return itemSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func findCommonItem(itemSet map[rune]bool, items string) rune {
|
||||||
|
for _, item := range items {
|
||||||
|
if itemSet[item] {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func findIntersection(itemSet map[rune]bool, items string) map[rune]bool {
|
||||||
|
intersection := make(map[rune]bool)
|
||||||
|
for _, item := range items {
|
||||||
|
if itemSet[item] {
|
||||||
|
intersection[item] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return intersection
|
||||||
|
}
|
||||||
|
|
||||||
|
func getItemPriority(item rune) int {
|
||||||
|
if item >= 'a' && item <= 'z' {
|
||||||
|
return int(item - 'a' + 1)
|
||||||
|
}
|
||||||
|
if item >= 'A' && item <= 'Z' {
|
||||||
|
return int(item - 'A' + 27)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
totalPriority := 0
|
||||||
|
for _, rucksack := range data {
|
||||||
|
compartmentSize := len(rucksack) / 2
|
||||||
|
firstCompartment := rucksack[:compartmentSize]
|
||||||
|
secondCompartment := rucksack[compartmentSize:]
|
||||||
|
|
||||||
|
firstCompartmentItems := buildItemSet(firstCompartment)
|
||||||
|
commonItem := findCommonItem(firstCompartmentItems, secondCompartment)
|
||||||
|
totalPriority += getItemPriority(commonItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPriority
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
totalPriority := 0
|
||||||
|
for i := 0; i < len(data); i += 3 {
|
||||||
|
if i+2 >= len(data) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
firstRucksack := data[i]
|
||||||
|
secondRucksack := data[i+1]
|
||||||
|
thirdRucksack := data[i+2]
|
||||||
|
|
||||||
|
firstRucksackItems := buildItemSet(firstRucksack)
|
||||||
|
commonInFirstTwo := findIntersection(firstRucksackItems, secondRucksack)
|
||||||
|
badge := findCommonItem(commonInFirstTwo, thirdRucksack)
|
||||||
|
totalPriority += getItemPriority(badge)
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalPriority
|
||||||
|
}
|
||||||
28
internal/2022/DayThree/code_test.go
Normal file
28
internal/2022/DayThree/code_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"vJrwpWtwJgWrhcsFMMfFFhFp",
|
||||||
|
"jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL",
|
||||||
|
"PmmdzqPrVvPwwTWBwg",
|
||||||
|
"wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn",
|
||||||
|
"ttgJtRGJQctTZtZT",
|
||||||
|
"CrZsJsPPZsGzwwsLwLmpwMDw",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 157
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 70
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
62
internal/2022/DayTwo/code.go
Normal file
62
internal/2022/DayTwo/code.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2022D2", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(strings.Trim(string(content), "\n"), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
totalScore := 0
|
||||||
|
for _, line := range data {
|
||||||
|
opponent := line[0]
|
||||||
|
me := line[2]
|
||||||
|
|
||||||
|
shapeScore := int(me - 'X' + 1)
|
||||||
|
|
||||||
|
var outcomeScore int
|
||||||
|
if (opponent == 'A' && me == 'Y') || (opponent == 'B' && me == 'Z') || (opponent == 'C' && me == 'X') {
|
||||||
|
outcomeScore = 6
|
||||||
|
} else if (opponent == 'A' && me == 'X') || (opponent == 'B' && me == 'Y') || (opponent == 'C' && me == 'Z') {
|
||||||
|
outcomeScore = 3
|
||||||
|
} else {
|
||||||
|
outcomeScore = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
totalScore += shapeScore + outcomeScore
|
||||||
|
}
|
||||||
|
return totalScore
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
totalScore := 0
|
||||||
|
for _, line := range data {
|
||||||
|
opponent := line[0]
|
||||||
|
outcome := line[2]
|
||||||
|
|
||||||
|
var me byte
|
||||||
|
switch outcome {
|
||||||
|
case 'Y':
|
||||||
|
me = 'X' + (opponent - 'A')
|
||||||
|
case 'Z':
|
||||||
|
me = 'X' + ((opponent-'A')+1)%3
|
||||||
|
default:
|
||||||
|
me = 'X' + ((opponent-'A')+2)%3
|
||||||
|
}
|
||||||
|
|
||||||
|
shapeScore := int(me - 'X' + 1)
|
||||||
|
outcomeScore := int(outcome-'X') * 3
|
||||||
|
|
||||||
|
totalScore += shapeScore + outcomeScore
|
||||||
|
}
|
||||||
|
return totalScore
|
||||||
|
}
|
||||||
25
internal/2022/DayTwo/code_test.go
Normal file
25
internal/2022/DayTwo/code_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"A Y",
|
||||||
|
"B X",
|
||||||
|
"C Z",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 15
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 12
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
internal/2022/register.go
Normal file
8
internal/2022/register.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package year2022
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2022/DayFour"
|
||||||
|
_ "advent-of-code/internal/2022/DayOne"
|
||||||
|
_ "advent-of-code/internal/2022/DayThree"
|
||||||
|
_ "advent-of-code/internal/2022/DayTwo"
|
||||||
|
)
|
||||||
210
internal/2025/DayEight/code.go
Normal file
210
internal/2025/DayEight/code.go
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
package dayeight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmp"
|
||||||
|
"container/heap"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D8", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
type JunctionBox struct {
|
||||||
|
X, Y, Z int
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionFind struct {
|
||||||
|
parent []int
|
||||||
|
size []int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection struct {
|
||||||
|
firstJunctionBox, secondJunctionBox int
|
||||||
|
squaredDistance int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnectionHeap []Connection
|
||||||
|
|
||||||
|
func (h ConnectionHeap) Len() int { return len(h) }
|
||||||
|
func (h ConnectionHeap) Less(i, j int) bool { return h[i].squaredDistance > h[j].squaredDistance }
|
||||||
|
func (h ConnectionHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||||
|
func (h *ConnectionHeap) Push(x any) { *h = append(*h, x.(Connection)) }
|
||||||
|
func (h *ConnectionHeap) Pop() any {
|
||||||
|
old := *h
|
||||||
|
n := len(old)
|
||||||
|
x := old[n-1]
|
||||||
|
*h = old[0 : n-1]
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnionFind(count int) *UnionFind {
|
||||||
|
parent := make([]int, count)
|
||||||
|
size := make([]int, count)
|
||||||
|
for idx := range parent {
|
||||||
|
parent[idx] = idx
|
||||||
|
size[idx] = 1
|
||||||
|
}
|
||||||
|
return &UnionFind{parent: parent, size: size}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uf *UnionFind) Find(junctionBox int) int {
|
||||||
|
if uf.parent[junctionBox] != junctionBox {
|
||||||
|
uf.parent[junctionBox] = uf.Find(uf.parent[junctionBox])
|
||||||
|
}
|
||||||
|
return uf.parent[junctionBox]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uf *UnionFind) Union(junctionBox1, junctionBox2 int) (bool, int) {
|
||||||
|
root1 := uf.Find(junctionBox1)
|
||||||
|
root2 := uf.Find(junctionBox2)
|
||||||
|
if root1 == root2 {
|
||||||
|
return false, root1
|
||||||
|
}
|
||||||
|
if uf.size[root1] < uf.size[root2] {
|
||||||
|
root1, root2 = root2, root1
|
||||||
|
}
|
||||||
|
uf.parent[root2] = root1
|
||||||
|
uf.size[root1] += uf.size[root2]
|
||||||
|
return true, root1
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJunctionBoxes(data []string) []JunctionBox {
|
||||||
|
var junctionBoxes []JunctionBox
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, ",")
|
||||||
|
x, _ := strconv.Atoi(parts[0])
|
||||||
|
y, _ := strconv.Atoi(parts[1])
|
||||||
|
z, _ := strconv.Atoi(parts[2])
|
||||||
|
junctionBoxes = append(junctionBoxes, JunctionBox{X: x, Y: y, Z: z})
|
||||||
|
}
|
||||||
|
return junctionBoxes
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateAllConnections(junctionBoxes []JunctionBox) []Connection {
|
||||||
|
junctionBoxCount := len(junctionBoxes)
|
||||||
|
connections := make([]Connection, 0, junctionBoxCount*(junctionBoxCount-1)/2)
|
||||||
|
|
||||||
|
for i := range junctionBoxCount {
|
||||||
|
for j := i + 1; j < junctionBoxCount; j++ {
|
||||||
|
dx := junctionBoxes[i].X - junctionBoxes[j].X
|
||||||
|
dy := junctionBoxes[i].Y - junctionBoxes[j].Y
|
||||||
|
dz := junctionBoxes[i].Z - junctionBoxes[j].Z
|
||||||
|
squaredDistance := dx*dx + dy*dy + dz*dz
|
||||||
|
|
||||||
|
connections = append(connections, Connection{
|
||||||
|
firstJunctionBox: i,
|
||||||
|
secondJunctionBox: j,
|
||||||
|
squaredDistance: squaredDistance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(connections, func(a, b Connection) int {
|
||||||
|
return cmp.Compare(a.squaredDistance, b.squaredDistance)
|
||||||
|
})
|
||||||
|
|
||||||
|
return connections
|
||||||
|
}
|
||||||
|
|
||||||
|
func findKSmallestConnections(junctionBoxes []JunctionBox, k int) []Connection {
|
||||||
|
junctionBoxCount := len(junctionBoxes)
|
||||||
|
connectionHeap := make(ConnectionHeap, 0, k)
|
||||||
|
heap.Init(&connectionHeap)
|
||||||
|
|
||||||
|
for i := range junctionBoxCount {
|
||||||
|
for j := i + 1; j < junctionBoxCount; j++ {
|
||||||
|
dx := junctionBoxes[i].X - junctionBoxes[j].X
|
||||||
|
dy := junctionBoxes[i].Y - junctionBoxes[j].Y
|
||||||
|
dz := junctionBoxes[i].Z - junctionBoxes[j].Z
|
||||||
|
squaredDistance := dx*dx + dy*dy + dz*dz
|
||||||
|
|
||||||
|
connection := Connection{
|
||||||
|
firstJunctionBox: i,
|
||||||
|
secondJunctionBox: j,
|
||||||
|
squaredDistance: squaredDistance,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(connectionHeap) < k {
|
||||||
|
heap.Push(&connectionHeap, connection)
|
||||||
|
} else if connection.squaredDistance < connectionHeap[0].squaredDistance {
|
||||||
|
heap.Pop(&connectionHeap)
|
||||||
|
heap.Push(&connectionHeap, connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connections := make([]Connection, 0, len(connectionHeap))
|
||||||
|
for connectionHeap.Len() > 0 {
|
||||||
|
connections = append(connections, heap.Pop(&connectionHeap).(Connection))
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(connections, func(a, b Connection) int {
|
||||||
|
return cmp.Compare(a.squaredDistance, b.squaredDistance)
|
||||||
|
})
|
||||||
|
|
||||||
|
return connections
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
junctionBoxes := parseJunctionBoxes(data)
|
||||||
|
junctionBoxCount := len(junctionBoxes)
|
||||||
|
|
||||||
|
targetPairs := 1000
|
||||||
|
if junctionBoxCount <= 20 {
|
||||||
|
targetPairs = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
connections := findKSmallestConnections(junctionBoxes, targetPairs)
|
||||||
|
|
||||||
|
uf := NewUnionFind(junctionBoxCount)
|
||||||
|
for _, connection := range connections {
|
||||||
|
uf.Union(connection.firstJunctionBox, connection.secondJunctionBox)
|
||||||
|
}
|
||||||
|
|
||||||
|
circuitSizes := make(map[int]int)
|
||||||
|
for idx := range junctionBoxes {
|
||||||
|
root := uf.Find(idx)
|
||||||
|
circuitSizes[root] = uf.size[root]
|
||||||
|
}
|
||||||
|
|
||||||
|
sizes := make([]int, 0, len(circuitSizes))
|
||||||
|
for _, size := range circuitSizes {
|
||||||
|
sizes = append(sizes, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.Sort(sizes)
|
||||||
|
slices.Reverse(sizes)
|
||||||
|
|
||||||
|
return sizes[0] * sizes[1] * sizes[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
junctionBoxes := parseJunctionBoxes(data)
|
||||||
|
connections := generateAllConnections(junctionBoxes)
|
||||||
|
|
||||||
|
uf := NewUnionFind(len(junctionBoxes))
|
||||||
|
var lastConnection Connection
|
||||||
|
|
||||||
|
for _, connection := range connections {
|
||||||
|
merged, root := uf.Union(connection.firstJunctionBox, connection.secondJunctionBox)
|
||||||
|
if merged {
|
||||||
|
lastConnection = connection
|
||||||
|
if uf.size[root] == len(junctionBoxes) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return junctionBoxes[lastConnection.firstJunctionBox].X * junctionBoxes[lastConnection.secondJunctionBox].X
|
||||||
|
}
|
||||||
42
internal/2025/DayEight/code_test.go
Normal file
42
internal/2025/DayEight/code_test.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package dayeight
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"162,817,812",
|
||||||
|
"57,618,57",
|
||||||
|
"906,360,560",
|
||||||
|
"592,479,940",
|
||||||
|
"352,342,300",
|
||||||
|
"466,668,158",
|
||||||
|
"542,29,236",
|
||||||
|
"431,825,988",
|
||||||
|
"739,650,466",
|
||||||
|
"52,470,668",
|
||||||
|
"216,146,977",
|
||||||
|
"819,987,18",
|
||||||
|
"117,168,530",
|
||||||
|
"805,96,715",
|
||||||
|
"346,949,466",
|
||||||
|
"970,615,88",
|
||||||
|
"941,993,340",
|
||||||
|
"862,61,35",
|
||||||
|
"984,92,344",
|
||||||
|
"425,690,689",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 40
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 25272
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
125
internal/2025/DayFive/code.go
Normal file
125
internal/2025/DayFive/code.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type freshRange struct {
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D5", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
var freshRanges []freshRange
|
||||||
|
var ingredientIDs []int
|
||||||
|
separatorFound := false
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
if line == "" {
|
||||||
|
separatorFound = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !separatorFound {
|
||||||
|
startStr, endStr, _ := strings.Cut(line, "-")
|
||||||
|
start, _ := strconv.Atoi(startStr)
|
||||||
|
end, _ := strconv.Atoi(endStr)
|
||||||
|
freshRanges = append(freshRanges, freshRange{start: start, end: end})
|
||||||
|
} else {
|
||||||
|
id, _ := strconv.Atoi(line)
|
||||||
|
ingredientIDs = append(ingredientIDs, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(freshRanges, func(a, b freshRange) int {
|
||||||
|
switch {
|
||||||
|
case a.start < b.start:
|
||||||
|
return -1
|
||||||
|
case a.start > b.start:
|
||||||
|
return 1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
freshCount := 0
|
||||||
|
for _, id := range ingredientIDs {
|
||||||
|
idx := sort.Search(len(freshRanges), func(idx int) bool {
|
||||||
|
return freshRanges[idx].start > id
|
||||||
|
})
|
||||||
|
|
||||||
|
for i := idx - 1; i >= 0 && freshRanges[i].start <= id; i-- {
|
||||||
|
if id <= freshRanges[i].end {
|
||||||
|
freshCount++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return freshCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
var freshRanges []freshRange
|
||||||
|
|
||||||
|
for _, line := range data {
|
||||||
|
if line == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
startStr, endStr, _ := strings.Cut(line, "-")
|
||||||
|
start, _ := strconv.Atoi(startStr)
|
||||||
|
end, _ := strconv.Atoi(endStr)
|
||||||
|
freshRanges = append(freshRanges, freshRange{start: start, end: end})
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(freshRanges, func(a, b freshRange) int {
|
||||||
|
switch {
|
||||||
|
case a.start < b.start:
|
||||||
|
return -1
|
||||||
|
case a.start > b.start:
|
||||||
|
return 1
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
var mergedRanges []freshRange
|
||||||
|
for _, r := range freshRanges {
|
||||||
|
if len(mergedRanges) == 0 {
|
||||||
|
mergedRanges = append(mergedRanges, r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
lastRange := &mergedRanges[len(mergedRanges)-1]
|
||||||
|
if r.start <= lastRange.end+1 {
|
||||||
|
if r.end > lastRange.end {
|
||||||
|
lastRange.end = r.end
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mergedRanges = append(mergedRanges, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalFreshIDs := 0
|
||||||
|
for _, r := range mergedRanges {
|
||||||
|
totalFreshIDs += r.end - r.start + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalFreshIDs
|
||||||
|
}
|
||||||
33
internal/2025/DayFive/code_test.go
Normal file
33
internal/2025/DayFive/code_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package dayfive
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"3-5",
|
||||||
|
"10-14",
|
||||||
|
"16-20",
|
||||||
|
"12-18",
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"5",
|
||||||
|
"8",
|
||||||
|
"11",
|
||||||
|
"17",
|
||||||
|
"32",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 3
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 14
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
104
internal/2025/DayFour/code.go
Normal file
104
internal/2025/DayFour/code.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
type position struct {
|
||||||
|
row, column int
|
||||||
|
}
|
||||||
|
|
||||||
|
var directions = []position{
|
||||||
|
{-1, -1},
|
||||||
|
{-1, 0},
|
||||||
|
{-1, 1},
|
||||||
|
{0, -1},
|
||||||
|
{0, 1},
|
||||||
|
{1, -1},
|
||||||
|
{1, 0},
|
||||||
|
{1, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D4", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
isRoll := make(map[position]bool)
|
||||||
|
|
||||||
|
for row := range data {
|
||||||
|
for column := 0; column < len(data[row]); column++ {
|
||||||
|
if data[row][column] == '@' {
|
||||||
|
isRoll[position{row, column}] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accessibleRolls := 0
|
||||||
|
for pos := range isRoll {
|
||||||
|
adjacentRolls := 0
|
||||||
|
for _, direction := range directions {
|
||||||
|
neighbor := position{pos.row + direction.row, pos.column + direction.column}
|
||||||
|
if isRoll[neighbor] {
|
||||||
|
adjacentRolls++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjacentRolls < 4 {
|
||||||
|
accessibleRolls++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accessibleRolls
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
isRoll := make(map[position]bool)
|
||||||
|
|
||||||
|
for row := range data {
|
||||||
|
for column := 0; column < len(data[row]); column++ {
|
||||||
|
if data[row][column] == '@' {
|
||||||
|
isRoll[position{row, column}] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalRemoved := 0
|
||||||
|
|
||||||
|
for {
|
||||||
|
accessibleRolls := make(map[position]bool)
|
||||||
|
|
||||||
|
for pos := range isRoll {
|
||||||
|
adjacentRolls := 0
|
||||||
|
for _, direction := range directions {
|
||||||
|
neighbor := position{pos.row + direction.row, pos.column + direction.column}
|
||||||
|
if isRoll[neighbor] {
|
||||||
|
adjacentRolls++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if adjacentRolls < 4 {
|
||||||
|
accessibleRolls[pos] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(accessibleRolls) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos := range accessibleRolls {
|
||||||
|
delete(isRoll, pos)
|
||||||
|
totalRemoved++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalRemoved
|
||||||
|
}
|
||||||
32
internal/2025/DayFour/code_test.go
Normal file
32
internal/2025/DayFour/code_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package dayfour
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"..@@.@@@@.",
|
||||||
|
"@@@.@.@.@@",
|
||||||
|
"@@@@@.@.@@",
|
||||||
|
"@.@@@@..@.",
|
||||||
|
"@@.@@@@.@@",
|
||||||
|
".@@@@@@@.@",
|
||||||
|
".@.@.@.@@@",
|
||||||
|
"@.@@@.@@@@",
|
||||||
|
".@@@@@@@@.",
|
||||||
|
"@.@.@@@.@.",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 13
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 43
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
234
internal/2025/DayNine/code.go
Normal file
234
internal/2025/DayNine/code.go
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
package daynine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D9", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
x, y int
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePoints(data []string) []Point {
|
||||||
|
points := make([]Point, 0, len(data))
|
||||||
|
for _, line := range data {
|
||||||
|
parts := strings.Split(line, ",")
|
||||||
|
x, _ := strconv.Atoi(parts[0])
|
||||||
|
y, _ := strconv.Atoi(parts[1])
|
||||||
|
points = append(points, Point{x: x, y: y})
|
||||||
|
}
|
||||||
|
return points
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(x int) int {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func minMax(a, b int) (int, int) {
|
||||||
|
if a > b {
|
||||||
|
return b, a
|
||||||
|
}
|
||||||
|
return a, b
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressCoordinates(points []Point) ([]int, []int, map[int]int, map[int]int) {
|
||||||
|
xs := make(map[int]bool)
|
||||||
|
ys := make(map[int]bool)
|
||||||
|
for _, point := range points {
|
||||||
|
xs[point.x] = true
|
||||||
|
ys[point.y] = true
|
||||||
|
}
|
||||||
|
sortedX := make([]int, 0, len(xs))
|
||||||
|
sortedY := make([]int, 0, len(ys))
|
||||||
|
for x := range xs {
|
||||||
|
sortedX = append(sortedX, x)
|
||||||
|
}
|
||||||
|
for y := range ys {
|
||||||
|
sortedY = append(sortedY, y)
|
||||||
|
}
|
||||||
|
slices.Sort(sortedX)
|
||||||
|
slices.Sort(sortedY)
|
||||||
|
|
||||||
|
mapX := make(map[int]int, len(sortedX))
|
||||||
|
mapY := make(map[int]int, len(sortedY))
|
||||||
|
for idx, x := range sortedX {
|
||||||
|
mapX[x] = 2 * idx
|
||||||
|
}
|
||||||
|
for idx, y := range sortedY {
|
||||||
|
mapY[y] = 2 * idx
|
||||||
|
}
|
||||||
|
return sortedX, sortedY, mapX, mapY
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawBoundary(grid [][]bool, points []Point, mapX, mapY map[int]int) {
|
||||||
|
for idx := range points {
|
||||||
|
current, next := points[idx], points[(idx+1)%len(points)]
|
||||||
|
cx1, cy1 := mapX[current.x], mapY[current.y]
|
||||||
|
cx2, cy2 := mapX[next.x], mapY[next.y]
|
||||||
|
|
||||||
|
if cx1 == cx2 {
|
||||||
|
minY, maxY := minMax(cy1, cy2)
|
||||||
|
for y := minY; y <= maxY; y++ {
|
||||||
|
grid[cx1][y] = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
minX, maxX := minMax(cx1, cx2)
|
||||||
|
for x := minX; x <= maxX; x++ {
|
||||||
|
grid[x][cy1] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanLineFill(grid [][]bool, points []Point, mapX, mapY map[int]int, _, height int) {
|
||||||
|
type edge struct {
|
||||||
|
x, yMin, yMax int
|
||||||
|
}
|
||||||
|
edges := make([]edge, 0, len(points))
|
||||||
|
for idx := range points {
|
||||||
|
current, next := points[idx], points[(idx+1)%len(points)]
|
||||||
|
if current.x == next.x {
|
||||||
|
minY, maxY := minMax(mapY[current.y], mapY[next.y])
|
||||||
|
edges = append(edges, edge{mapX[current.x], minY, maxY})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := range height {
|
||||||
|
crossings := make([]int, 0, len(edges))
|
||||||
|
for _, e := range edges {
|
||||||
|
if e.yMin <= y && y < e.yMax {
|
||||||
|
crossings = append(crossings, e.x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slices.Sort(crossings)
|
||||||
|
for i := 0; i < len(crossings)-1; i += 2 {
|
||||||
|
for x := crossings[i]; x <= crossings[i+1]; x++ {
|
||||||
|
grid[x][y] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func forceEmptyGaps(grid [][]bool, sortedX, sortedY []int, width, height int) {
|
||||||
|
for idx := 0; idx < len(sortedX)-1; idx++ {
|
||||||
|
if sortedX[idx+1] == sortedX[idx]+1 {
|
||||||
|
cx := 2*idx + 1
|
||||||
|
for y := range height {
|
||||||
|
grid[cx][y] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for idx := 0; idx < len(sortedY)-1; idx++ {
|
||||||
|
if sortedY[idx+1] == sortedY[idx]+1 {
|
||||||
|
cy := 2*idx + 1
|
||||||
|
for x := range width {
|
||||||
|
grid[x][cy] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildPrefixSum(grid [][]bool, width, height int) [][]int {
|
||||||
|
sum := make([][]int, width+1)
|
||||||
|
for idx := range sum {
|
||||||
|
sum[idx] = make([]int, height+1)
|
||||||
|
}
|
||||||
|
for i := range width {
|
||||||
|
for j := range height {
|
||||||
|
value := 0
|
||||||
|
if grid[i][j] {
|
||||||
|
value = 1
|
||||||
|
}
|
||||||
|
sum[i+1][j+1] = value + sum[i][j+1] + sum[i+1][j] - sum[i][j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSum(sum [][]int, x1, y1, x2, y2 int) int {
|
||||||
|
return sum[x2+1][y2+1] - sum[x1][y2+1] - sum[x2+1][y1] + sum[x1][y1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
points := parsePoints(data)
|
||||||
|
maxArea := 0
|
||||||
|
|
||||||
|
for i := range points {
|
||||||
|
for j := i + 1; j < len(points); j++ {
|
||||||
|
dx := abs(points[i].x - points[j].x)
|
||||||
|
dy := abs(points[i].y - points[j].y)
|
||||||
|
|
||||||
|
if area := (dx + 1) * (dy + 1); area > maxArea {
|
||||||
|
maxArea = area
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxArea
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
points := parsePoints(data)
|
||||||
|
sortedX, sortedY, mapX, mapY := compressCoordinates(points)
|
||||||
|
|
||||||
|
width, height := 2*len(sortedX), 2*len(sortedY)
|
||||||
|
grid := make([][]bool, width)
|
||||||
|
for idx := range grid {
|
||||||
|
grid[idx] = make([]bool, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
drawBoundary(grid, points, mapX, mapY)
|
||||||
|
scanLineFill(grid, points, mapX, mapY, width, height)
|
||||||
|
forceEmptyGaps(grid, sortedX, sortedY, width, height)
|
||||||
|
|
||||||
|
sum := buildPrefixSum(grid, width, height)
|
||||||
|
|
||||||
|
compressedPoints := make([]Point, len(points))
|
||||||
|
for idx, point := range points {
|
||||||
|
compressedPoints[idx] = Point{mapX[point.x], mapY[point.y]}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxArea := 0
|
||||||
|
for i := range points {
|
||||||
|
for j := i + 1; j < len(points); j++ {
|
||||||
|
p1, p2 := points[i], points[j]
|
||||||
|
dx := abs(p1.x - p2.x)
|
||||||
|
dy := abs(p1.y - p2.y)
|
||||||
|
|
||||||
|
if dx == 0 || dy == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
area := (dx + 1) * (dy + 1)
|
||||||
|
if area <= maxArea {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cx1, cy1 := compressedPoints[i].x, compressedPoints[i].y
|
||||||
|
cx2, cy2 := compressedPoints[j].x, compressedPoints[j].y
|
||||||
|
minCX, maxCX := minMax(cx1, cx2)
|
||||||
|
minCY, maxCY := minMax(cy1, cy2)
|
||||||
|
|
||||||
|
totalCells := (maxCX - minCX + 1) * (maxCY - minCY + 1)
|
||||||
|
if getSum(sum, minCX, minCY, maxCX, maxCY) == totalCells {
|
||||||
|
maxArea = area
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxArea
|
||||||
|
}
|
||||||
30
internal/2025/DayNine/code_test.go
Normal file
30
internal/2025/DayNine/code_test.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package daynine
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"7,1",
|
||||||
|
"11,1",
|
||||||
|
"11,7",
|
||||||
|
"9,7",
|
||||||
|
"9,5",
|
||||||
|
"2,5",
|
||||||
|
"2,3",
|
||||||
|
"7,3",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 50
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 24
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
internal/2025/DayOne/code.go
Normal file
63
internal/2025/DayOne/code.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D1", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
position := 50
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for _, rotation := range data {
|
||||||
|
direction := rotation[0]
|
||||||
|
distance, _ := strconv.Atoi(rotation[1:])
|
||||||
|
|
||||||
|
if direction == 'L' {
|
||||||
|
position = (position - distance + 100) % 100
|
||||||
|
} else {
|
||||||
|
position = (position + distance) % 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if position == 0 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
position := 50
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
for _, rotation := range data {
|
||||||
|
direction := rotation[0]
|
||||||
|
distance, _ := strconv.Atoi(rotation[1:])
|
||||||
|
|
||||||
|
for range distance {
|
||||||
|
if direction == 'L' {
|
||||||
|
position = (position - 1 + 100) % 100
|
||||||
|
} else {
|
||||||
|
position = (position + 1) % 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if position == 0 {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
32
internal/2025/DayOne/code_test.go
Normal file
32
internal/2025/DayOne/code_test.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package dayone
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"L68",
|
||||||
|
"L30",
|
||||||
|
"R48",
|
||||||
|
"L5",
|
||||||
|
"R60",
|
||||||
|
"L55",
|
||||||
|
"L1",
|
||||||
|
"L99",
|
||||||
|
"R14",
|
||||||
|
"L82",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 3
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 6
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
106
internal/2025/DaySeven/code.go
Normal file
106
internal/2025/DaySeven/code.go
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package dayseven
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D7", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Position struct {
|
||||||
|
row int
|
||||||
|
column int
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func findStart(data []string) (int, int) {
|
||||||
|
for row := range data {
|
||||||
|
if column := strings.IndexByte(data[row], 'S'); column != -1 {
|
||||||
|
return row, column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
rows, columns := len(data), len(data[0])
|
||||||
|
startRow, startColumn := findStart(data)
|
||||||
|
|
||||||
|
queue := []Position{{startRow, startColumn}}
|
||||||
|
activated := make(map[Position]bool)
|
||||||
|
splits := 0
|
||||||
|
|
||||||
|
for len(queue) > 0 {
|
||||||
|
current := queue[0]
|
||||||
|
queue = queue[1:]
|
||||||
|
|
||||||
|
row := current.row + 1
|
||||||
|
column := current.column
|
||||||
|
|
||||||
|
for row < rows && data[row][column] != '^' {
|
||||||
|
row++
|
||||||
|
}
|
||||||
|
|
||||||
|
if row < rows {
|
||||||
|
splitPosition := Position{row, column}
|
||||||
|
if !activated[splitPosition] {
|
||||||
|
activated[splitPosition] = true
|
||||||
|
splits++
|
||||||
|
|
||||||
|
if column > 0 {
|
||||||
|
queue = append(queue, Position{row, column - 1})
|
||||||
|
}
|
||||||
|
if column < columns-1 {
|
||||||
|
queue = append(queue, Position{row, column + 1})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return splits
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(input []string) int {
|
||||||
|
rows, columns := len(input), len(input[0])
|
||||||
|
startRow, startColumn := findStart(input)
|
||||||
|
|
||||||
|
cache := make(map[Position]int)
|
||||||
|
|
||||||
|
var countTimelines func(row, column int) int
|
||||||
|
countTimelines = func(row, column int) int {
|
||||||
|
position := Position{row, column}
|
||||||
|
if count, exists := cache[position]; exists {
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
r, c := row+1, column
|
||||||
|
for r < rows && input[r][c] != '^' {
|
||||||
|
r++
|
||||||
|
}
|
||||||
|
|
||||||
|
if r == rows {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
total := 0
|
||||||
|
if c > 0 {
|
||||||
|
total += countTimelines(r, c-1)
|
||||||
|
}
|
||||||
|
if c < columns-1 {
|
||||||
|
total += countTimelines(r, c+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache[position] = total
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
return countTimelines(startRow, startColumn)
|
||||||
|
}
|
||||||
38
internal/2025/DaySeven/code_test.go
Normal file
38
internal/2025/DaySeven/code_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package dayseven
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
".......S.......",
|
||||||
|
"...............",
|
||||||
|
".......^.......",
|
||||||
|
"...............",
|
||||||
|
"......^.^......",
|
||||||
|
"...............",
|
||||||
|
".....^.^.^.....",
|
||||||
|
"...............",
|
||||||
|
"....^.^...^....",
|
||||||
|
"...............",
|
||||||
|
"...^.^...^.^...",
|
||||||
|
"...............",
|
||||||
|
"..^...^.....^..",
|
||||||
|
"...............",
|
||||||
|
".^.^.^.^.^...^.",
|
||||||
|
"...............",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 21
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 40
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
174
internal/2025/DaySix/code.go
Normal file
174
internal/2025/DaySix/code.go
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
package daysix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D6", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(strings.TrimRight(string(content), "\n"), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func transpose(lines []string) []string {
|
||||||
|
maxWidth := 0
|
||||||
|
for _, line := range lines {
|
||||||
|
if len(line) > maxWidth {
|
||||||
|
maxWidth = len(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transposed := make([]string, maxWidth)
|
||||||
|
for column := 0; column < maxWidth; column++ {
|
||||||
|
var builder strings.Builder
|
||||||
|
for _, line := range lines {
|
||||||
|
if column < len(line) {
|
||||||
|
builder.WriteByte(line[column])
|
||||||
|
} else {
|
||||||
|
builder.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transposed[column] = builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return transposed
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpaceColumn(column string) bool {
|
||||||
|
return strings.TrimSpace(column) == ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractRowFromColumns(transposed []string, row, startColumn, endColumn int) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
for column := startColumn; column <= endColumn && column < len(transposed); column++ {
|
||||||
|
if row < len(transposed[column]) {
|
||||||
|
builder.WriteByte(transposed[column][row])
|
||||||
|
} else {
|
||||||
|
builder.WriteByte(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractNumbers(transposed []string, startColumn, endColumn, operationRow int) []int {
|
||||||
|
numbers := make([]int, 0)
|
||||||
|
for row := range operationRow {
|
||||||
|
rowStr := extractRowFromColumns(transposed, row, startColumn, endColumn)
|
||||||
|
trimmed := strings.TrimSpace(rowStr)
|
||||||
|
fields := strings.FieldsSeq(trimmed)
|
||||||
|
for field := range fields {
|
||||||
|
if number, err := strconv.Atoi(field); err == nil {
|
||||||
|
numbers = append(numbers, number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractNumberFromColumn(column string, operationRow int) (int, bool) {
|
||||||
|
numberStr := strings.Map(func(r rune) rune {
|
||||||
|
if r >= '0' && r <= '9' {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}, column[:min(operationRow, len(column))])
|
||||||
|
number, _ := strconv.Atoi(numberStr)
|
||||||
|
return number, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractOperation(segment string) byte {
|
||||||
|
if idx := strings.IndexAny(segment, "+*"); idx != -1 {
|
||||||
|
return segment[idx]
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func applyOperation(numbers []int, operation byte) int {
|
||||||
|
result := numbers[0]
|
||||||
|
if operation == '+' {
|
||||||
|
for idx := 1; idx < len(numbers); idx++ {
|
||||||
|
result += numbers[idx]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for idx := 1; idx < len(numbers); idx++ {
|
||||||
|
result *= numbers[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
operationRow := len(data) - 1
|
||||||
|
transposed := transpose(data)
|
||||||
|
|
||||||
|
total := 0
|
||||||
|
column := 0
|
||||||
|
for column < len(transposed) {
|
||||||
|
if isSpaceColumn(transposed[column]) {
|
||||||
|
column++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
startColumn := column
|
||||||
|
for column < len(transposed) && !isSpaceColumn(transposed[column]) {
|
||||||
|
column++
|
||||||
|
}
|
||||||
|
endColumn := column - 1
|
||||||
|
|
||||||
|
numbers := extractNumbers(transposed, startColumn, endColumn, operationRow)
|
||||||
|
operationRowStr := extractRowFromColumns(transposed, operationRow, startColumn, endColumn)
|
||||||
|
operation := extractOperation(operationRowStr)
|
||||||
|
|
||||||
|
if len(numbers) > 0 && operation != 0 {
|
||||||
|
result := applyOperation(numbers, operation)
|
||||||
|
total += result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
operationRow := len(data) - 1
|
||||||
|
transposed := transpose(data)
|
||||||
|
|
||||||
|
total := 0
|
||||||
|
column := len(transposed) - 1
|
||||||
|
|
||||||
|
for column >= 0 {
|
||||||
|
if isSpaceColumn(transposed[column]) {
|
||||||
|
column--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
endColumn := column
|
||||||
|
for column >= 0 && !isSpaceColumn(transposed[column]) {
|
||||||
|
column--
|
||||||
|
}
|
||||||
|
startColumn := column + 1
|
||||||
|
|
||||||
|
numbers := make([]int, 0)
|
||||||
|
for column := startColumn; column <= endColumn && column < len(transposed); column++ {
|
||||||
|
if number, ok := extractNumberFromColumn(transposed[column], operationRow); ok {
|
||||||
|
numbers = append(numbers, number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operationRowStr := extractRowFromColumns(transposed, operationRow, startColumn, endColumn)
|
||||||
|
operation := extractOperation(operationRowStr)
|
||||||
|
|
||||||
|
if len(numbers) > 0 && operation != 0 {
|
||||||
|
result := applyOperation(numbers, operation)
|
||||||
|
total += result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
26
internal/2025/DaySix/code_test.go
Normal file
26
internal/2025/DaySix/code_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package daysix
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"123 328 51 64 ",
|
||||||
|
" 45 64 387 23 ",
|
||||||
|
" 6 98 215 314",
|
||||||
|
"* + * + ",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 4277556
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 3263827
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
91
internal/2025/DayTen/code.go
Normal file
91
internal/2025/DayTen/code.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package dayten
|
||||||
|
|
||||||
|
import (
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D10", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(string(content), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseMachine(line string) ([]bool, [][]int) {
|
||||||
|
bracketRegex := regexp.MustCompile(`\[([.#]+)\]`)
|
||||||
|
parenthesisRegex := regexp.MustCompile(`\(([^)]+)\)`)
|
||||||
|
|
||||||
|
bracketMatch := bracketRegex.FindStringSubmatch(line)
|
||||||
|
targetString := bracketMatch[1]
|
||||||
|
target := make([]bool, len(targetString))
|
||||||
|
for idx, char := range targetString {
|
||||||
|
target[idx] = char == '#'
|
||||||
|
}
|
||||||
|
|
||||||
|
parenthesisMatches := parenthesisRegex.FindAllStringSubmatch(line, -1)
|
||||||
|
buttons := make([][]int, 0, len(parenthesisMatches))
|
||||||
|
|
||||||
|
for _, match := range parenthesisMatches {
|
||||||
|
buttonString := match[1]
|
||||||
|
parts := strings.Split(buttonString, ",")
|
||||||
|
button := make([]int, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
light, _ := strconv.Atoi(part)
|
||||||
|
button = append(button, light)
|
||||||
|
}
|
||||||
|
buttons = append(buttons, button)
|
||||||
|
}
|
||||||
|
|
||||||
|
return target, buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
total := 0
|
||||||
|
for _, line := range data {
|
||||||
|
target, buttons := parseMachine(line)
|
||||||
|
numberOfLights := len(target)
|
||||||
|
numberOfButtons := len(buttons)
|
||||||
|
minimumPresses := numberOfButtons + 1
|
||||||
|
|
||||||
|
for mask := 0; mask < (1 << numberOfButtons); mask++ {
|
||||||
|
lights := make([]bool, numberOfLights)
|
||||||
|
presses := 0
|
||||||
|
|
||||||
|
for buttonIdx := range numberOfButtons {
|
||||||
|
if mask&(1<<buttonIdx) != 0 {
|
||||||
|
presses++
|
||||||
|
for _, light := range buttons[buttonIdx] {
|
||||||
|
if light < numberOfLights {
|
||||||
|
lights[light] = !lights[light]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches := true
|
||||||
|
for idx := range numberOfLights {
|
||||||
|
if lights[idx] != target[idx] {
|
||||||
|
matches = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches && presses < minimumPresses {
|
||||||
|
minimumPresses = presses
|
||||||
|
}
|
||||||
|
}
|
||||||
|
total += minimumPresses
|
||||||
|
}
|
||||||
|
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
25
internal/2025/DayTen/code_test.go
Normal file
25
internal/2025/DayTen/code_test.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package dayten
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}",
|
||||||
|
"[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}",
|
||||||
|
"[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 7
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 33
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
72
internal/2025/DayThree/code.go
Normal file
72
internal/2025/DayThree/code.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D3", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) [][]int {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
var data [][]int
|
||||||
|
for _, line := range lines {
|
||||||
|
var bank []int
|
||||||
|
for _, char := range line {
|
||||||
|
bank = append(bank, int(char-'0'))
|
||||||
|
}
|
||||||
|
data = append(data, bank)
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data [][]int) int {
|
||||||
|
total := 0
|
||||||
|
for _, bank := range data {
|
||||||
|
maxAfter := make([]int, len(bank))
|
||||||
|
|
||||||
|
for idx := len(bank) - 2; idx >= 0; idx-- {
|
||||||
|
maxAfter[idx] = max(maxAfter[idx+1], bank[idx+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
maxJoltage := 0
|
||||||
|
|
||||||
|
for idx := 0; idx < len(bank)-1; idx++ {
|
||||||
|
joltage := bank[idx]*10 + maxAfter[idx]
|
||||||
|
maxJoltage = max(maxJoltage, joltage)
|
||||||
|
}
|
||||||
|
total += maxJoltage
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data [][]int) int {
|
||||||
|
total := 0
|
||||||
|
for _, bank := range data {
|
||||||
|
var joltage int
|
||||||
|
start := 0
|
||||||
|
|
||||||
|
for position := range 12 {
|
||||||
|
remaining := 12 - position - 1
|
||||||
|
maxIdx := start
|
||||||
|
maxValue := bank[start]
|
||||||
|
|
||||||
|
for idx := start; idx < len(bank)-remaining; idx++ {
|
||||||
|
if bank[idx] > maxValue {
|
||||||
|
maxValue = bank[idx]
|
||||||
|
maxIdx = idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joltage = joltage*10 + maxValue
|
||||||
|
start = maxIdx + 1
|
||||||
|
}
|
||||||
|
total += joltage
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
26
internal/2025/DayThree/code_test.go
Normal file
26
internal/2025/DayThree/code_test.go
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
package daythree
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = [][]int{
|
||||||
|
{9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1},
|
||||||
|
{8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9},
|
||||||
|
{2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 7, 8},
|
||||||
|
{8, 1, 8, 1, 8, 1, 9, 1, 1, 1, 1, 2, 1, 1, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 357
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 3121910778619
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
87
internal/2025/DayTwo/code.go
Normal file
87
internal/2025/DayTwo/code.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"advent-of-code/internal/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.Register("2025D2", ParseInput, PartOne, PartTwo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInput(filepath string) []string {
|
||||||
|
content, _ := os.ReadFile(filepath)
|
||||||
|
return strings.Split(strings.TrimSpace(string(content)), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartOne(data []string) int {
|
||||||
|
sum := 0
|
||||||
|
for _, line := range data {
|
||||||
|
for newRange := range strings.SplitSeq(line, ",") {
|
||||||
|
parts := strings.Split(newRange, "-")
|
||||||
|
start, _ := strconv.Atoi(parts[0])
|
||||||
|
end, _ := strconv.Atoi(parts[1])
|
||||||
|
for id := start; id <= end; id++ {
|
||||||
|
sID := strconv.Itoa(id)
|
||||||
|
if len(sID)%2 == 0 {
|
||||||
|
half := len(sID) / 2
|
||||||
|
if sID[:half] == sID[half:] {
|
||||||
|
sum += id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func PartTwo(data []string) int {
|
||||||
|
totalSum := 0
|
||||||
|
for _, line := range data {
|
||||||
|
for rangeSpec := range strings.SplitSeq(line, ",") {
|
||||||
|
rangeStartStr, rangeEndStr, _ := strings.Cut(rangeSpec, "-")
|
||||||
|
rangeStart, _ := strconv.Atoi(rangeStartStr)
|
||||||
|
rangeEnd, _ := strconv.Atoi(rangeEndStr)
|
||||||
|
|
||||||
|
seenNumbers := make(map[int]bool)
|
||||||
|
maxDigits := len(rangeEndStr)
|
||||||
|
|
||||||
|
var generatePatterns func(string, int, int)
|
||||||
|
generatePatterns = func(currentPattern string, targetPatternLength, currentDepth int) {
|
||||||
|
if currentDepth == targetPatternLength {
|
||||||
|
repeated := currentPattern + currentPattern
|
||||||
|
for len(repeated) <= maxDigits {
|
||||||
|
number, err := strconv.Atoi(repeated)
|
||||||
|
if err != nil || number > rangeEnd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if number >= rangeStart && !seenNumbers[number] {
|
||||||
|
totalSum += number
|
||||||
|
seenNumbers[number] = true
|
||||||
|
}
|
||||||
|
repeated += currentPattern
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
minDigit := 0
|
||||||
|
if currentDepth == 0 {
|
||||||
|
minDigit = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for digit := minDigit; digit <= 9; digit++ {
|
||||||
|
extendedPattern := currentPattern + strconv.Itoa(digit)
|
||||||
|
generatePatterns(extendedPattern, targetPatternLength, currentDepth+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for patternLength := 1; patternLength <= maxDigits/2; patternLength++ {
|
||||||
|
generatePatterns("", patternLength, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return totalSum
|
||||||
|
}
|
||||||
33
internal/2025/DayTwo/code_test.go
Normal file
33
internal/2025/DayTwo/code_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package daytwo
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
var testInput = []string{
|
||||||
|
"11-22",
|
||||||
|
"95-115",
|
||||||
|
"998-1012",
|
||||||
|
"1188511880-1188511890",
|
||||||
|
"222220-222224",
|
||||||
|
"1698522-1698528",
|
||||||
|
"446443-446449",
|
||||||
|
"38593856-38593862",
|
||||||
|
"565653-565659",
|
||||||
|
"824824821-824824827",
|
||||||
|
"2121212118-2121212124",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartOne(t *testing.T) {
|
||||||
|
expected := 1227775554
|
||||||
|
got := PartOne(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartOne() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPartTwo(t *testing.T) {
|
||||||
|
expected := 4174379265
|
||||||
|
got := PartTwo(testInput)
|
||||||
|
if got != expected {
|
||||||
|
t.Errorf("PartTwo() = %d, want %d", got, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
internal/2025/register.go
Normal file
14
internal/2025/register.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package year2025
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "advent-of-code/internal/2025/DayEight"
|
||||||
|
_ "advent-of-code/internal/2025/DayFive"
|
||||||
|
_ "advent-of-code/internal/2025/DayFour"
|
||||||
|
_ "advent-of-code/internal/2025/DayNine"
|
||||||
|
_ "advent-of-code/internal/2025/DayOne"
|
||||||
|
_ "advent-of-code/internal/2025/DaySeven"
|
||||||
|
_ "advent-of-code/internal/2025/DaySix"
|
||||||
|
_ "advent-of-code/internal/2025/DayTen"
|
||||||
|
_ "advent-of-code/internal/2025/DayThree"
|
||||||
|
_ "advent-of-code/internal/2025/DayTwo"
|
||||||
|
)
|
||||||
300
internal/data/2015/DayEight/input.txt
Normal file
300
internal/data/2015/DayEight/input.txt
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
"\xa8br\x8bjr\""
|
||||||
|
"nq"
|
||||||
|
"zjrfcpbktjmrzgsz\xcaqsc\x03n\"huqab"
|
||||||
|
"daz\\zyyxddpwk"
|
||||||
|
"draes\xa2n\\g\x27ek\"lj\"\\viqych"
|
||||||
|
"nnx\\krnrfomdnt\x2flbl\xd2xpo\"cp\"k"
|
||||||
|
"kwdaapalq"
|
||||||
|
"u\"ptk"
|
||||||
|
"ckhorczuiudfjmmcc\\u\"wozqxibsfjma"
|
||||||
|
"ydctdrxat\"pd\"lwi\"bjesevfw\xe8"
|
||||||
|
"v\"\xa8rrzep\"\"r"
|
||||||
|
"nbydghkfvmq\\\xe0\"lfsrsvlsj\"i\x61liif"
|
||||||
|
"jsas\"u"
|
||||||
|
"odipikxlo"
|
||||||
|
"\"rnubsgwltqkbsu\"pcpcs"
|
||||||
|
"eitk\\f\\mhcqqoym\\ji"
|
||||||
|
"vnedc"
|
||||||
|
"\"lhcaurdqzyjyu"
|
||||||
|
"haxzsa\"zcn\"y\"foclgtjfcnv\"m\x68krc"
|
||||||
|
"\"eoeggg\"tmiydvcay\"vfavc"
|
||||||
|
"snqvyqoncwxcvwbdktoywch"
|
||||||
|
"rnfgjsyr\xd5wacy"
|
||||||
|
"ik\"hebrpvsts"
|
||||||
|
"txw"
|
||||||
|
"\x15pxtdkogd\"urbm\"gevhh\"nxr\x3erxtk"
|
||||||
|
"cetqtcy"
|
||||||
|
"inleep\\mgl"
|
||||||
|
"uflwbxvww\x2cxzezqnaply\"yh\"qlllzk"
|
||||||
|
"eepak\"xqtedzt"
|
||||||
|
"na\x61qzfieafvyrsnwkssznohjmc"
|
||||||
|
"yceaonylz\xc1\\jrlbbkzwsidfi"
|
||||||
|
"ybqafngkcqpbp"
|
||||||
|
"\xaft"
|
||||||
|
"yidjpaobqydso"
|
||||||
|
"ju\\ldxig\\lrdrhjcmm\x77rc"
|
||||||
|
"tylacqeslnwj\x48ds\"tjxa"
|
||||||
|
"efbfm"
|
||||||
|
"\\fxkgoprgdcjgyajykg\\dtbrz"
|
||||||
|
"eujvva"
|
||||||
|
"h\x7acwfpikme\\vwthyvrqdnx\""
|
||||||
|
"rbpbrxm\\\"\"\"voxx"
|
||||||
|
"ykiw\"tkb\\lforu\"rsf\\tf\"x\"rqti"
|
||||||
|
"e\\wh\x77aqeugiq\\ihhfqfuaij"
|
||||||
|
"g\"t\\o"
|
||||||
|
"nxzo\"hf\\xp"
|
||||||
|
"dxiaqfo\xea"
|
||||||
|
"kali\\zczhiqkqzybjj\"fgdjnik"
|
||||||
|
"zdkgrqmdv"
|
||||||
|
"bimxim\xb6lrwsaj\"ui\"a"
|
||||||
|
"\"rrznitibgx\\olpsjmjqzctxaubdifsq"
|
||||||
|
"zb\"khzixaacmhuzmlymoformipdzml"
|
||||||
|
"qfwi"
|
||||||
|
"hjwsxfpphttjy\"\"zixais\xbblgnqfto"
|
||||||
|
"puj\\qmyu\"nqgaqfthbwjokbmrpbhpi"
|
||||||
|
"cyxdpkh\\\""
|
||||||
|
"q"
|
||||||
|
"m"
|
||||||
|
"tbxdzzllarlo"
|
||||||
|
"gbtys"
|
||||||
|
"gytilk\\vlqxvcuutjunrqc"
|
||||||
|
"uugkvcuzan\\eyhb"
|
||||||
|
"yaxr\"genlbgw\"\\uc"
|
||||||
|
"nrgecjeip\\sjdvgqaqxwsqactopu"
|
||||||
|
"pu\"r\"txpyrkfny\\zmwfneyvwmnkkdipv"
|
||||||
|
"jm\xa3bhwvq"
|
||||||
|
"qxojmnml\"w\x9airr"
|
||||||
|
"xbzsuihs\x4dcedy\xaclrhgii\\\""
|
||||||
|
"drgjirusrekrwmvxllwdm"
|
||||||
|
"\x28hfxnfpycmpnkku\"csuf\xaarxlqyg\"x"
|
||||||
|
"\"zvz\\rmg\"\\sxxoifffyqfyn\"iq\"ps"
|
||||||
|
"\"z"
|
||||||
|
"zbwkmk\"sgzos\x93gtc\""
|
||||||
|
"bvm\x28aa\\\\\"pywuhaniox\\z\\hbp\xd7mold"
|
||||||
|
"aszgvsyna"
|
||||||
|
"qf\"vdwuss"
|
||||||
|
"lnohni\"qwiacjsjegstlbfq\\kyjhyd"
|
||||||
|
"c\\naawulxlqplnacvytspry\xf5ytxxqq"
|
||||||
|
"razwqmsqgbaaxcd\\f"
|
||||||
|
"radggyrjrg\"zx"
|
||||||
|
"\"pu\x11t\\ajcjuieinlkvya"
|
||||||
|
"veggiskh"
|
||||||
|
"eglfhjxiet\"kouqfskwsy\"hpthsldel"
|
||||||
|
"mv\xc1b\"f\\shrssnjwcpmurepdxdlcj"
|
||||||
|
"dlayjd\"suvzotgdtc"
|
||||||
|
"\xa9pvxeopn"
|
||||||
|
"lpplsaxy\"oiwaq"
|
||||||
|
"hqwh\\lusv"
|
||||||
|
"hykykwlx\"\xa5atkgh\\d\x63dff"
|
||||||
|
"vfktanpjy\"xxetc"
|
||||||
|
"dnhwkgjnsmsswfuelvihvjl\"jtf"
|
||||||
|
"x\"dwvzra\"nbbsewftehczgbvfzd\"rau"
|
||||||
|
"csfi\"mzejnjqkqupwadrgti\"von"
|
||||||
|
"xckf\xf7xsm\\pgvlpetjndpyblais\\z"
|
||||||
|
"yecy\x6fuj\x58bwpgeuiw\"mdu"
|
||||||
|
"fgb"
|
||||||
|
"c\\lx\x3efthet\xfdelgvwvpem"
|
||||||
|
"kgyrmarvfwjinlowt"
|
||||||
|
"yzte"
|
||||||
|
"vc\"z"
|
||||||
|
"sxevqfzmmdwsuu\""
|
||||||
|
"fxbaercmcy\xb6md"
|
||||||
|
"f"
|
||||||
|
"m\x44gqbcppho\\b"
|
||||||
|
"gtafr\x57m\x11jy\"\"erwmmpiwjkbckuw"
|
||||||
|
"ufdjt\"kssprzxqixzxmq\x58q"
|
||||||
|
"yzbyo\"lfdbyaxexyfbnyv\\\xe8xmre"
|
||||||
|
"u\x43ntr\\\\byyfjr\"iveujvnwsqbnpuvrta"
|
||||||
|
"us\xf6bai"
|
||||||
|
"c\\edh"
|
||||||
|
"tzckolphexfq\\\x23\xfbdqv\\\"m"
|
||||||
|
"yjafhbvhhj\x1b\"bplb"
|
||||||
|
"\"o"
|
||||||
|
"rubahvmp\""
|
||||||
|
"qmkukrnrmqumh"
|
||||||
|
"wdpxyvyidhwjf\\nabbijwhr\xc5bksvy\"p"
|
||||||
|
"u\"prlpg\""
|
||||||
|
"nsvcquyxbwilsxxemf\xd9leq"
|
||||||
|
"y\xcetxuafl"
|
||||||
|
"it"
|
||||||
|
"kwdlysf\\xjpelae"
|
||||||
|
"viwh\x58wpjjlnvryuti\x2chngrx\\nhtkui"
|
||||||
|
"vhn\x9ehre\xc3ncsqbozms\"nl"
|
||||||
|
"ytc\xa3mgeeogjcqavmmmd"
|
||||||
|
"xzlexlitseozoxtpzzutfq"
|
||||||
|
"cish\x07lmovj"
|
||||||
|
"ekbflwqzaiivdr\"pq\\azrfbntqwkn"
|
||||||
|
"uc\"xdbegmlmhksofzohavtrnxf"
|
||||||
|
"xfdnrdqdrcjzbe"
|
||||||
|
"ndg\"ckgrpisib\"rg\"p\\lmpfzlssnvk"
|
||||||
|
"witfjwpbyyzlop"
|
||||||
|
"zonlww\"emrbcsgdtrg\"rjzy\x64zqntlw"
|
||||||
|
"dvgb\"zn\\vrbzema\"ckmd"
|
||||||
|
"\\vdlmxhlvldk\"pmzazeip"
|
||||||
|
"\"\"r"
|
||||||
|
"rsntinv"
|
||||||
|
"iy"
|
||||||
|
"lr\x20efh"
|
||||||
|
"csgexlb\"zqdavlxxhtdbh\"\"\x0fkpvhiphm"
|
||||||
|
"ouwhp\"ogbft"
|
||||||
|
"cm\\ckltng\"dw\x8brf\xf0eppgckd"
|
||||||
|
"zmnlsgalhpkejsizfsbtnfliu\"nhc"
|
||||||
|
"pnrkaayqvwpdjbhcrbb\"yfeq\"aq"
|
||||||
|
"ozh\\hoxow\x2csrtr\\r\""
|
||||||
|
"bqxabj\"u\\s"
|
||||||
|
"cpsjti\"gy"
|
||||||
|
"aa\"p\\nki\\ajijkqev"
|
||||||
|
"q\"\"lfdentjgd\\"
|
||||||
|
"bmokvpoebutfki"
|
||||||
|
"pielvcbne\xf6efvzxn"
|
||||||
|
"kx"
|
||||||
|
"zlgmqagcrbhrwtwtmmg"
|
||||||
|
"aiyhmntcqjbpv\xb5hhswxbryoedvos"
|
||||||
|
"tdpaxrb"
|
||||||
|
"fu\"\x7dttkyvhrlwko"
|
||||||
|
"oirc\\\"cqlnqffjqt\\k"
|
||||||
|
"edxlia\\tcyby"
|
||||||
|
"jpeybgwfayerfrfbvfog\"ol"
|
||||||
|
"ysr"
|
||||||
|
"bzwzilgwfugjk"
|
||||||
|
"tlcc\x75nukvwjgftetjcs\xaecwc"
|
||||||
|
"dsqssa\"vzrf\"sewbp\\ahhlmhbeihlh"
|
||||||
|
"qtgmjck\"n\"guki\"gmdivwqxismqj"
|
||||||
|
"\"f"
|
||||||
|
"wuorvlovucngbzdszqpikyk"
|
||||||
|
"dfrdsacoukmgvhbq\"\"iwto"
|
||||||
|
"\"ey\"ch\\wcgioe\\\"ouvligmsw"
|
||||||
|
"ciqlszzgs"
|
||||||
|
"\\tzyrkaoi\"sopjaq"
|
||||||
|
"lmtnv"
|
||||||
|
"ar\"fqoroigiertjjlm\"ymgi\\kkjewsxd"
|
||||||
|
"wehcimlvudpxtamdn\"rwy"
|
||||||
|
"hr\"zvrwthr\"vruzqfrldn\"b"
|
||||||
|
"sggekodkiwvym\"mhsco"
|
||||||
|
"ltlkfbrrdvk\\"
|
||||||
|
"uut\"sfjnz\"\\ef"
|
||||||
|
"hxilg\\"
|
||||||
|
"zsredsiwlzrpedibn"
|
||||||
|
"vtfi"
|
||||||
|
"\\h"
|
||||||
|
"qekfrc\xf6wduodbwrguqcng\\n"
|
||||||
|
"\"lljlfdrxftwidn\\pkv\xd9ij"
|
||||||
|
"mrvgqynpehkliuijlpp"
|
||||||
|
"gikjph"
|
||||||
|
"yoxcdrdt\"wbaurnyhoyxoihu"
|
||||||
|
"onmomwuxuammbzxe"
|
||||||
|
"rnrr\\twviz\x61gqaljr\x0dmtw"
|
||||||
|
"r\"vupaoi"
|
||||||
|
"l"
|
||||||
|
"sei"
|
||||||
|
"jwxtdtbkd\\kxd"
|
||||||
|
"\x22v\\"
|
||||||
|
"ahd"
|
||||||
|
"j\"bjqxs"
|
||||||
|
"\\i\x24gglxub\\nzsajokt"
|
||||||
|
"lviwpu\"uxdlh\\zuy\"xqy\"ytdzlx\"r"
|
||||||
|
"kptfmys"
|
||||||
|
"fwxzikfhczkjwyjszqdbkepaeellc"
|
||||||
|
"nlqpsvbrbd\\ns"
|
||||||
|
"qryuwkjiodw\"\"vaqyq\"dmyifm"
|
||||||
|
"tw\x15kdmaudjl\\zorhp\"alwh"
|
||||||
|
"aatrvczesykekkjfyb\"kb"
|
||||||
|
"usqcutbqbxxhucwxo\xc1ltb\"j\"bghjcvws"
|
||||||
|
"ilhsrnzxkz"
|
||||||
|
"bianqfdfdhvw"
|
||||||
|
"hqibqs\x7ax\"qoxqoaqtcsz"
|
||||||
|
"htxtoojbbauztwxuiq\\ngyfy\\obzc"
|
||||||
|
"rxn\\moxlj"
|
||||||
|
"mtus\x84erh\"dbe"
|
||||||
|
"asx\x50huvsitcxadt"
|
||||||
|
"\"bugggtnrc\"\"kl\"hmpu\x83hqrvhpo"
|
||||||
|
"ewisbp\"\"vuzf\\w\x5fvalszdhl"
|
||||||
|
"scusplpwxfnxu\x57\"zynpn\x99xerc\\ri"
|
||||||
|
"m\\kinmkke\x0cl"
|
||||||
|
"xhuzit\x7fd"
|
||||||
|
"kfbo\x04\x50ruqirn"
|
||||||
|
"t\"\"xpbdscmdoug"
|
||||||
|
"punvpsgnbgyxe\"sptmpz"
|
||||||
|
"bxukkazijr"
|
||||||
|
"nxyrcdaoo\"rjkk\"wntehcvcip\"vrd"
|
||||||
|
"rdpvqskmihqaw"
|
||||||
|
"p\\gwdhtqnpgthod"
|
||||||
|
"nwnuf\"\"yebycearom\"nqym\"\xd4sii\xccle"
|
||||||
|
"alda\"ptspo\"wkkv\"zoi\"hkb\"vnntyd"
|
||||||
|
"ixpgpfzbqv"
|
||||||
|
"znui\"\\fzn\x03qozabh\"rva\"pv\x67"
|
||||||
|
"e\"zswmwuk"
|
||||||
|
"hcccygwfa"
|
||||||
|
"ngmace\\rtyllolr\"\x68bw"
|
||||||
|
"\\c\"jyufbry\"ryo\"xpo\x26ecninfeckh\\s"
|
||||||
|
"hdnpngtuc\"dzbvvosn\x31fwtpzbrt"
|
||||||
|
"hesbpd\xd4"
|
||||||
|
"dsdbstuzrdfmrnyntufs\"dmv"
|
||||||
|
"d\xeeibcwhcvkt"
|
||||||
|
"fvzwrsfjdqdmy\"\"v"
|
||||||
|
"ns\"dqafz\\lkyoflnazv\"mn\x37\"o\"yj\"e"
|
||||||
|
"dypilgbwzccayxa\"bnmuernx"
|
||||||
|
"q\xa9ztqrhreb\"\"kxfeyodqb"
|
||||||
|
"iz\xa5qjxqulaawuwz\"rqmpcj\\yel"
|
||||||
|
"z\"\\pq\"\"y\x67zpjtn"
|
||||||
|
"ifxqvivp\"kiiftdoe"
|
||||||
|
"jxzebj\"\x35\"qr\"ecglcutuoyywqumcs\"kk"
|
||||||
|
"q"
|
||||||
|
"yob\x85qmpuwexptczbkrl"
|
||||||
|
"cjiavv\"uudpozvibyycnmxhxpxmpjoz"
|
||||||
|
"xro\\uiqyrcid"
|
||||||
|
"nod\\k"
|
||||||
|
"d\"neiec"
|
||||||
|
"tqyrqvwyvmz\\pzgzzcqsqsrgbqbtapoz"
|
||||||
|
"r\"xvocpeuxfxslgueb\x05kzyyie\"aoec"
|
||||||
|
"\"du\\uirlhcbgv\\cjqhfreqnvn"
|
||||||
|
"zp\x04\x15\"pbjwhrjtmiba"
|
||||||
|
"\\cv\""
|
||||||
|
"k\"rwnb\\hiu\"rqd\"rc\\nyakrhly"
|
||||||
|
"klrmafjzandiddodgz"
|
||||||
|
"xipzhqzhvlpykzcuppx"
|
||||||
|
"zdvrvn\xd0mtfvpylbn\\\\sxcznrzugwznl"
|
||||||
|
"ody\\pvm\"kpjiudzhxazirgxzvumeat\"o"
|
||||||
|
"kllvhdp\"prjikzrrc\"adgpegc\\\"gk"
|
||||||
|
"sqtpug\xbcaauxaamw"
|
||||||
|
"wegxxrrxdvpivrqievfeokmnojsk"
|
||||||
|
"\\bo"
|
||||||
|
"gijhz"
|
||||||
|
"ylowluvabwrigssdgtxdwsiorxev\xdd"
|
||||||
|
"\""
|
||||||
|
"ghnsrnsqtxpygikahkrl"
|
||||||
|
"\"rcfqkbjf\"sgxg\"vnd\\rotn"
|
||||||
|
"ap\"smgsuexjrbuqs\"mpbstogj\"x"
|
||||||
|
"koaunz\\sgt\"opv"
|
||||||
|
"yialiuzwix"
|
||||||
|
"yp\"ndxgwzml\"bt"
|
||||||
|
"lpcjxmggfsy\\szbxccarjkqzasqkb\xcfd\x0c"
|
||||||
|
"x"
|
||||||
|
"mgakc"
|
||||||
|
"vjieunoh\x73fjwx"
|
||||||
|
"erbvv\"qulsd"
|
||||||
|
"mimycrbfhqkarmz"
|
||||||
|
"tihfbgcszuej\"c\xfbvoqskkhbgpaddioo"
|
||||||
|
"mziavkwrmekriqghw"
|
||||||
|
"izk\\tnjd\\ed\\emokvjoc"
|
||||||
|
"c\"nhbqzndro\\g"
|
||||||
|
"usfngdo"
|
||||||
|
"aypljdftvptt"
|
||||||
|
"ym\"afvq\xbcc"
|
||||||
|
"zabi\"wjpvugwhl"
|
||||||
|
"ebvptcjqjhc\"n\"p\"dxrphegr\\"
|
||||||
|
"mzlqqxokhye\xd9\\rffhnzs"
|
||||||
|
"hnipqknwpsjakanuewe"
|
||||||
|
"rqgbfcjdrmiz\"h"
|
||||||
|
"kzzp\\z\\txmkwaouxictybwx"
|
||||||
|
"yzmspjkqrteiydswlvb"
|
||||||
|
"gjpxklgpzv\"txri\\hotpuiukzzzd"
|
||||||
|
"p\"rxergtbsxmjmkeeqwvoagnki\""
|
||||||
|
"santipvuiq"
|
||||||
|
"\"ihjqlhtwbuy\"hdkiv\"mtiqacnf\\"
|
||||||
|
"oliaggtqyyx"
|
||||||
|
"fwwnpmbb"
|
||||||
|
"yrtdrieazfxyyneo"
|
||||||
|
"nywbv\\"
|
||||||
|
"twc\\ehfqxhgomgrgwpxyzmnkioj"
|
||||||
|
"qludrkkvljljd\\xvdeum\x4e"
|
||||||
1
internal/data/2015/DayEleven/input.txt
Normal file
1
internal/data/2015/DayEleven/input.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
hxbxwxba
|
||||||
1000
internal/data/2015/DayFive/input.txt
Normal file
1000
internal/data/2015/DayFive/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
1
internal/data/2015/DayFour/input.txt
Normal file
1
internal/data/2015/DayFour/input.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
iwrupvqb
|
||||||
9
internal/data/2015/DayFourteen/input.txt
Normal file
9
internal/data/2015/DayFourteen/input.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Vixen can fly 19 km/s for 7 seconds, but then must rest for 124 seconds.
|
||||||
|
Rudolph can fly 3 km/s for 15 seconds, but then must rest for 28 seconds.
|
||||||
|
Donner can fly 19 km/s for 9 seconds, but then must rest for 164 seconds.
|
||||||
|
Blitzen can fly 19 km/s for 9 seconds, but then must rest for 158 seconds.
|
||||||
|
Comet can fly 13 km/s for 7 seconds, but then must rest for 82 seconds.
|
||||||
|
Cupid can fly 25 km/s for 6 seconds, but then must rest for 145 seconds.
|
||||||
|
Dasher can fly 14 km/s for 3 seconds, but then must rest for 38 seconds.
|
||||||
|
Dancer can fly 3 km/s for 16 seconds, but then must rest for 37 seconds.
|
||||||
|
Prancer can fly 25 km/s for 6 seconds, but then must rest for 143 seconds.
|
||||||
28
internal/data/2015/DayNine/input.txt
Normal file
28
internal/data/2015/DayNine/input.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
Tristram to AlphaCentauri = 34
|
||||||
|
Tristram to Snowdin = 100
|
||||||
|
Tristram to Tambi = 63
|
||||||
|
Tristram to Faerun = 108
|
||||||
|
Tristram to Norrath = 111
|
||||||
|
Tristram to Straylight = 89
|
||||||
|
Tristram to Arbre = 132
|
||||||
|
AlphaCentauri to Snowdin = 4
|
||||||
|
AlphaCentauri to Tambi = 79
|
||||||
|
AlphaCentauri to Faerun = 44
|
||||||
|
AlphaCentauri to Norrath = 147
|
||||||
|
AlphaCentauri to Straylight = 133
|
||||||
|
AlphaCentauri to Arbre = 74
|
||||||
|
Snowdin to Tambi = 105
|
||||||
|
Snowdin to Faerun = 95
|
||||||
|
Snowdin to Norrath = 48
|
||||||
|
Snowdin to Straylight = 88
|
||||||
|
Snowdin to Arbre = 7
|
||||||
|
Tambi to Faerun = 68
|
||||||
|
Tambi to Norrath = 134
|
||||||
|
Tambi to Straylight = 107
|
||||||
|
Tambi to Arbre = 40
|
||||||
|
Faerun to Norrath = 11
|
||||||
|
Faerun to Straylight = 66
|
||||||
|
Faerun to Arbre = 144
|
||||||
|
Norrath to Straylight = 115
|
||||||
|
Norrath to Arbre = 135
|
||||||
|
Straylight to Arbre = 127
|
||||||
339
internal/data/2015/DaySeven/input.txt
Normal file
339
internal/data/2015/DaySeven/input.txt
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
af AND ah -> ai
|
||||||
|
NOT lk -> ll
|
||||||
|
hz RSHIFT 1 -> is
|
||||||
|
NOT go -> gp
|
||||||
|
du OR dt -> dv
|
||||||
|
x RSHIFT 5 -> aa
|
||||||
|
at OR az -> ba
|
||||||
|
eo LSHIFT 15 -> es
|
||||||
|
ci OR ct -> cu
|
||||||
|
b RSHIFT 5 -> f
|
||||||
|
fm OR fn -> fo
|
||||||
|
NOT ag -> ah
|
||||||
|
v OR w -> x
|
||||||
|
g AND i -> j
|
||||||
|
an LSHIFT 15 -> ar
|
||||||
|
1 AND cx -> cy
|
||||||
|
jq AND jw -> jy
|
||||||
|
iu RSHIFT 5 -> ix
|
||||||
|
gl AND gm -> go
|
||||||
|
NOT bw -> bx
|
||||||
|
jp RSHIFT 3 -> jr
|
||||||
|
hg AND hh -> hj
|
||||||
|
bv AND bx -> by
|
||||||
|
er OR es -> et
|
||||||
|
kl OR kr -> ks
|
||||||
|
et RSHIFT 1 -> fm
|
||||||
|
e AND f -> h
|
||||||
|
u LSHIFT 1 -> ao
|
||||||
|
he RSHIFT 1 -> hx
|
||||||
|
eg AND ei -> ej
|
||||||
|
bo AND bu -> bw
|
||||||
|
dz OR ef -> eg
|
||||||
|
dy RSHIFT 3 -> ea
|
||||||
|
gl OR gm -> gn
|
||||||
|
da LSHIFT 1 -> du
|
||||||
|
au OR av -> aw
|
||||||
|
gj OR gu -> gv
|
||||||
|
eu OR fa -> fb
|
||||||
|
lg OR lm -> ln
|
||||||
|
e OR f -> g
|
||||||
|
NOT dm -> dn
|
||||||
|
NOT l -> m
|
||||||
|
aq OR ar -> as
|
||||||
|
gj RSHIFT 5 -> gm
|
||||||
|
hm AND ho -> hp
|
||||||
|
ge LSHIFT 15 -> gi
|
||||||
|
jp RSHIFT 1 -> ki
|
||||||
|
hg OR hh -> hi
|
||||||
|
lc LSHIFT 1 -> lw
|
||||||
|
km OR kn -> ko
|
||||||
|
eq LSHIFT 1 -> fk
|
||||||
|
1 AND am -> an
|
||||||
|
gj RSHIFT 1 -> hc
|
||||||
|
aj AND al -> am
|
||||||
|
gj AND gu -> gw
|
||||||
|
ko AND kq -> kr
|
||||||
|
ha OR gz -> hb
|
||||||
|
bn OR by -> bz
|
||||||
|
iv OR jb -> jc
|
||||||
|
NOT ac -> ad
|
||||||
|
bo OR bu -> bv
|
||||||
|
d AND j -> l
|
||||||
|
bk LSHIFT 1 -> ce
|
||||||
|
de OR dk -> dl
|
||||||
|
dd RSHIFT 1 -> dw
|
||||||
|
hz AND ik -> im
|
||||||
|
NOT jd -> je
|
||||||
|
fo RSHIFT 2 -> fp
|
||||||
|
hb LSHIFT 1 -> hv
|
||||||
|
lf RSHIFT 2 -> lg
|
||||||
|
gj RSHIFT 3 -> gl
|
||||||
|
ki OR kj -> kk
|
||||||
|
NOT ak -> al
|
||||||
|
ld OR le -> lf
|
||||||
|
ci RSHIFT 3 -> ck
|
||||||
|
1 AND cc -> cd
|
||||||
|
NOT kx -> ky
|
||||||
|
fp OR fv -> fw
|
||||||
|
ev AND ew -> ey
|
||||||
|
dt LSHIFT 15 -> dx
|
||||||
|
NOT ax -> ay
|
||||||
|
bp AND bq -> bs
|
||||||
|
NOT ii -> ij
|
||||||
|
ci AND ct -> cv
|
||||||
|
iq OR ip -> ir
|
||||||
|
x RSHIFT 2 -> y
|
||||||
|
fq OR fr -> fs
|
||||||
|
bn RSHIFT 5 -> bq
|
||||||
|
0 -> c
|
||||||
|
14146 -> b
|
||||||
|
d OR j -> k
|
||||||
|
z OR aa -> ab
|
||||||
|
gf OR ge -> gg
|
||||||
|
df OR dg -> dh
|
||||||
|
NOT hj -> hk
|
||||||
|
NOT di -> dj
|
||||||
|
fj LSHIFT 15 -> fn
|
||||||
|
lf RSHIFT 1 -> ly
|
||||||
|
b AND n -> p
|
||||||
|
jq OR jw -> jx
|
||||||
|
gn AND gp -> gq
|
||||||
|
x RSHIFT 1 -> aq
|
||||||
|
ex AND ez -> fa
|
||||||
|
NOT fc -> fd
|
||||||
|
bj OR bi -> bk
|
||||||
|
as RSHIFT 5 -> av
|
||||||
|
hu LSHIFT 15 -> hy
|
||||||
|
NOT gs -> gt
|
||||||
|
fs AND fu -> fv
|
||||||
|
dh AND dj -> dk
|
||||||
|
bz AND cb -> cc
|
||||||
|
dy RSHIFT 1 -> er
|
||||||
|
hc OR hd -> he
|
||||||
|
fo OR fz -> ga
|
||||||
|
t OR s -> u
|
||||||
|
b RSHIFT 2 -> d
|
||||||
|
NOT jy -> jz
|
||||||
|
hz RSHIFT 2 -> ia
|
||||||
|
kk AND kv -> kx
|
||||||
|
ga AND gc -> gd
|
||||||
|
fl LSHIFT 1 -> gf
|
||||||
|
bn AND by -> ca
|
||||||
|
NOT hr -> hs
|
||||||
|
NOT bs -> bt
|
||||||
|
lf RSHIFT 3 -> lh
|
||||||
|
au AND av -> ax
|
||||||
|
1 AND gd -> ge
|
||||||
|
jr OR js -> jt
|
||||||
|
fw AND fy -> fz
|
||||||
|
NOT iz -> ja
|
||||||
|
c LSHIFT 1 -> t
|
||||||
|
dy RSHIFT 5 -> eb
|
||||||
|
bp OR bq -> br
|
||||||
|
NOT h -> i
|
||||||
|
1 AND ds -> dt
|
||||||
|
ab AND ad -> ae
|
||||||
|
ap LSHIFT 1 -> bj
|
||||||
|
br AND bt -> bu
|
||||||
|
NOT ca -> cb
|
||||||
|
NOT el -> em
|
||||||
|
s LSHIFT 15 -> w
|
||||||
|
gk OR gq -> gr
|
||||||
|
ff AND fh -> fi
|
||||||
|
kf LSHIFT 15 -> kj
|
||||||
|
fp AND fv -> fx
|
||||||
|
lh OR li -> lj
|
||||||
|
bn RSHIFT 3 -> bp
|
||||||
|
jp OR ka -> kb
|
||||||
|
lw OR lv -> lx
|
||||||
|
iy AND ja -> jb
|
||||||
|
dy OR ej -> ek
|
||||||
|
1 AND bh -> bi
|
||||||
|
NOT kt -> ku
|
||||||
|
ao OR an -> ap
|
||||||
|
ia AND ig -> ii
|
||||||
|
NOT ey -> ez
|
||||||
|
bn RSHIFT 1 -> cg
|
||||||
|
fk OR fj -> fl
|
||||||
|
ce OR cd -> cf
|
||||||
|
eu AND fa -> fc
|
||||||
|
kg OR kf -> kh
|
||||||
|
jr AND js -> ju
|
||||||
|
iu RSHIFT 3 -> iw
|
||||||
|
df AND dg -> di
|
||||||
|
dl AND dn -> do
|
||||||
|
la LSHIFT 15 -> le
|
||||||
|
fo RSHIFT 1 -> gh
|
||||||
|
NOT gw -> gx
|
||||||
|
NOT gb -> gc
|
||||||
|
ir LSHIFT 1 -> jl
|
||||||
|
x AND ai -> ak
|
||||||
|
he RSHIFT 5 -> hh
|
||||||
|
1 AND lu -> lv
|
||||||
|
NOT ft -> fu
|
||||||
|
gh OR gi -> gj
|
||||||
|
lf RSHIFT 5 -> li
|
||||||
|
x RSHIFT 3 -> z
|
||||||
|
b RSHIFT 3 -> e
|
||||||
|
he RSHIFT 2 -> hf
|
||||||
|
NOT fx -> fy
|
||||||
|
jt AND jv -> jw
|
||||||
|
hx OR hy -> hz
|
||||||
|
jp AND ka -> kc
|
||||||
|
fb AND fd -> fe
|
||||||
|
hz OR ik -> il
|
||||||
|
ci RSHIFT 1 -> db
|
||||||
|
fo AND fz -> gb
|
||||||
|
fq AND fr -> ft
|
||||||
|
gj RSHIFT 2 -> gk
|
||||||
|
cg OR ch -> ci
|
||||||
|
cd LSHIFT 15 -> ch
|
||||||
|
jm LSHIFT 1 -> kg
|
||||||
|
ih AND ij -> ik
|
||||||
|
fo RSHIFT 3 -> fq
|
||||||
|
fo RSHIFT 5 -> fr
|
||||||
|
1 AND fi -> fj
|
||||||
|
1 AND kz -> la
|
||||||
|
iu AND jf -> jh
|
||||||
|
cq AND cs -> ct
|
||||||
|
dv LSHIFT 1 -> ep
|
||||||
|
hf OR hl -> hm
|
||||||
|
km AND kn -> kp
|
||||||
|
de AND dk -> dm
|
||||||
|
dd RSHIFT 5 -> dg
|
||||||
|
NOT lo -> lp
|
||||||
|
NOT ju -> jv
|
||||||
|
NOT fg -> fh
|
||||||
|
cm AND co -> cp
|
||||||
|
ea AND eb -> ed
|
||||||
|
dd RSHIFT 3 -> df
|
||||||
|
gr AND gt -> gu
|
||||||
|
ep OR eo -> eq
|
||||||
|
cj AND cp -> cr
|
||||||
|
lf OR lq -> lr
|
||||||
|
gg LSHIFT 1 -> ha
|
||||||
|
et RSHIFT 2 -> eu
|
||||||
|
NOT jh -> ji
|
||||||
|
ek AND em -> en
|
||||||
|
jk LSHIFT 15 -> jo
|
||||||
|
ia OR ig -> ih
|
||||||
|
gv AND gx -> gy
|
||||||
|
et AND fe -> fg
|
||||||
|
lh AND li -> lk
|
||||||
|
1 AND io -> ip
|
||||||
|
kb AND kd -> ke
|
||||||
|
kk RSHIFT 5 -> kn
|
||||||
|
id AND if -> ig
|
||||||
|
NOT ls -> lt
|
||||||
|
dw OR dx -> dy
|
||||||
|
dd AND do -> dq
|
||||||
|
lf AND lq -> ls
|
||||||
|
NOT kc -> kd
|
||||||
|
dy AND ej -> el
|
||||||
|
1 AND ke -> kf
|
||||||
|
et OR fe -> ff
|
||||||
|
hz RSHIFT 5 -> ic
|
||||||
|
dd OR do -> dp
|
||||||
|
cj OR cp -> cq
|
||||||
|
NOT dq -> dr
|
||||||
|
kk RSHIFT 1 -> ld
|
||||||
|
jg AND ji -> jj
|
||||||
|
he OR hp -> hq
|
||||||
|
hi AND hk -> hl
|
||||||
|
dp AND dr -> ds
|
||||||
|
dz AND ef -> eh
|
||||||
|
hz RSHIFT 3 -> ib
|
||||||
|
db OR dc -> dd
|
||||||
|
hw LSHIFT 1 -> iq
|
||||||
|
he AND hp -> hr
|
||||||
|
NOT cr -> cs
|
||||||
|
lg AND lm -> lo
|
||||||
|
hv OR hu -> hw
|
||||||
|
il AND in -> io
|
||||||
|
NOT eh -> ei
|
||||||
|
gz LSHIFT 15 -> hd
|
||||||
|
gk AND gq -> gs
|
||||||
|
1 AND en -> eo
|
||||||
|
NOT kp -> kq
|
||||||
|
et RSHIFT 5 -> ew
|
||||||
|
lj AND ll -> lm
|
||||||
|
he RSHIFT 3 -> hg
|
||||||
|
et RSHIFT 3 -> ev
|
||||||
|
as AND bd -> bf
|
||||||
|
cu AND cw -> cx
|
||||||
|
jx AND jz -> ka
|
||||||
|
b OR n -> o
|
||||||
|
be AND bg -> bh
|
||||||
|
1 AND ht -> hu
|
||||||
|
1 AND gy -> gz
|
||||||
|
NOT hn -> ho
|
||||||
|
ck OR cl -> cm
|
||||||
|
ec AND ee -> ef
|
||||||
|
lv LSHIFT 15 -> lz
|
||||||
|
ks AND ku -> kv
|
||||||
|
NOT ie -> if
|
||||||
|
hf AND hl -> hn
|
||||||
|
1 AND r -> s
|
||||||
|
ib AND ic -> ie
|
||||||
|
hq AND hs -> ht
|
||||||
|
y AND ae -> ag
|
||||||
|
NOT ed -> ee
|
||||||
|
bi LSHIFT 15 -> bm
|
||||||
|
dy RSHIFT 2 -> dz
|
||||||
|
ci RSHIFT 2 -> cj
|
||||||
|
NOT bf -> bg
|
||||||
|
NOT im -> in
|
||||||
|
ev OR ew -> ex
|
||||||
|
ib OR ic -> id
|
||||||
|
bn RSHIFT 2 -> bo
|
||||||
|
dd RSHIFT 2 -> de
|
||||||
|
bl OR bm -> bn
|
||||||
|
as RSHIFT 1 -> bl
|
||||||
|
ea OR eb -> ec
|
||||||
|
ln AND lp -> lq
|
||||||
|
kk RSHIFT 3 -> km
|
||||||
|
is OR it -> iu
|
||||||
|
iu RSHIFT 2 -> iv
|
||||||
|
as OR bd -> be
|
||||||
|
ip LSHIFT 15 -> it
|
||||||
|
iw OR ix -> iy
|
||||||
|
kk RSHIFT 2 -> kl
|
||||||
|
NOT bb -> bc
|
||||||
|
ci RSHIFT 5 -> cl
|
||||||
|
ly OR lz -> ma
|
||||||
|
z AND aa -> ac
|
||||||
|
iu RSHIFT 1 -> jn
|
||||||
|
cy LSHIFT 15 -> dc
|
||||||
|
cf LSHIFT 1 -> cz
|
||||||
|
as RSHIFT 3 -> au
|
||||||
|
cz OR cy -> da
|
||||||
|
kw AND ky -> kz
|
||||||
|
lx -> a
|
||||||
|
iw AND ix -> iz
|
||||||
|
lr AND lt -> lu
|
||||||
|
jp RSHIFT 5 -> js
|
||||||
|
aw AND ay -> az
|
||||||
|
jc AND je -> jf
|
||||||
|
lb OR la -> lc
|
||||||
|
NOT cn -> co
|
||||||
|
kh LSHIFT 1 -> lb
|
||||||
|
1 AND jj -> jk
|
||||||
|
y OR ae -> af
|
||||||
|
ck AND cl -> cn
|
||||||
|
kk OR kv -> kw
|
||||||
|
NOT cv -> cw
|
||||||
|
kl AND kr -> kt
|
||||||
|
iu OR jf -> jg
|
||||||
|
at AND az -> bb
|
||||||
|
jp RSHIFT 2 -> jq
|
||||||
|
iv AND jb -> jd
|
||||||
|
jn OR jo -> jp
|
||||||
|
x OR ai -> aj
|
||||||
|
ba AND bc -> bd
|
||||||
|
jl OR jk -> jm
|
||||||
|
b RSHIFT 1 -> v
|
||||||
|
o AND q -> r
|
||||||
|
NOT p -> q
|
||||||
|
k AND m -> n
|
||||||
|
as RSHIFT 2 -> at
|
||||||
300
internal/data/2015/DaySix/input.txt
Normal file
300
internal/data/2015/DaySix/input.txt
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
turn on 489,959 through 759,964
|
||||||
|
turn off 820,516 through 871,914
|
||||||
|
turn off 427,423 through 929,502
|
||||||
|
turn on 774,14 through 977,877
|
||||||
|
turn on 410,146 through 864,337
|
||||||
|
turn on 931,331 through 939,812
|
||||||
|
turn off 756,53 through 923,339
|
||||||
|
turn off 313,787 through 545,979
|
||||||
|
turn off 12,823 through 102,934
|
||||||
|
toggle 756,965 through 812,992
|
||||||
|
turn off 743,684 through 789,958
|
||||||
|
toggle 120,314 through 745,489
|
||||||
|
toggle 692,845 through 866,994
|
||||||
|
turn off 587,176 through 850,273
|
||||||
|
turn off 674,321 through 793,388
|
||||||
|
toggle 749,672 through 973,965
|
||||||
|
turn on 943,30 through 990,907
|
||||||
|
turn on 296,50 through 729,664
|
||||||
|
turn on 212,957 through 490,987
|
||||||
|
toggle 171,31 through 688,88
|
||||||
|
turn off 991,989 through 994,998
|
||||||
|
turn off 913,943 through 958,953
|
||||||
|
turn off 278,258 through 367,386
|
||||||
|
toggle 275,796 through 493,971
|
||||||
|
turn off 70,873 through 798,923
|
||||||
|
toggle 258,985 through 663,998
|
||||||
|
turn on 601,259 through 831,486
|
||||||
|
turn off 914,94 through 941,102
|
||||||
|
turn off 558,161 through 994,647
|
||||||
|
turn on 119,662 through 760,838
|
||||||
|
toggle 378,775 through 526,852
|
||||||
|
turn off 384,670 through 674,972
|
||||||
|
turn off 249,41 through 270,936
|
||||||
|
turn on 614,742 through 769,780
|
||||||
|
turn on 427,70 through 575,441
|
||||||
|
turn on 410,478 through 985,753
|
||||||
|
turn off 619,46 through 931,342
|
||||||
|
turn on 284,55 through 768,922
|
||||||
|
turn off 40,592 through 728,685
|
||||||
|
turn on 825,291 through 956,950
|
||||||
|
turn on 147,843 through 592,909
|
||||||
|
turn off 218,675 through 972,911
|
||||||
|
toggle 249,291 through 350,960
|
||||||
|
turn off 556,80 through 967,675
|
||||||
|
toggle 609,148 through 968,279
|
||||||
|
toggle 217,605 through 961,862
|
||||||
|
toggle 407,177 through 548,910
|
||||||
|
toggle 400,936 through 599,938
|
||||||
|
turn off 721,101 through 925,455
|
||||||
|
turn on 268,631 through 735,814
|
||||||
|
toggle 549,969 through 612,991
|
||||||
|
toggle 553,268 through 689,432
|
||||||
|
turn off 817,668 through 889,897
|
||||||
|
toggle 801,544 through 858,556
|
||||||
|
toggle 615,729 through 832,951
|
||||||
|
turn off 427,477 through 958,948
|
||||||
|
turn on 164,49 through 852,946
|
||||||
|
turn on 542,449 through 774,776
|
||||||
|
turn off 923,196 through 980,446
|
||||||
|
toggle 90,310 through 718,846
|
||||||
|
turn off 657,215 through 744,252
|
||||||
|
turn off 800,239 through 811,712
|
||||||
|
turn on 502,90 through 619,760
|
||||||
|
toggle 649,512 through 862,844
|
||||||
|
turn off 334,903 through 823,935
|
||||||
|
turn off 630,233 through 839,445
|
||||||
|
turn on 713,67 through 839,865
|
||||||
|
turn on 932,50 through 982,411
|
||||||
|
turn off 480,729 through 984,910
|
||||||
|
turn on 100,219 through 796,395
|
||||||
|
turn on 758,108 through 850,950
|
||||||
|
turn off 427,276 through 439,938
|
||||||
|
turn on 178,284 through 670,536
|
||||||
|
toggle 540,27 through 625,102
|
||||||
|
turn off 906,722 through 936,948
|
||||||
|
toggle 345,418 through 859,627
|
||||||
|
toggle 175,775 through 580,781
|
||||||
|
toggle 863,28 through 929,735
|
||||||
|
turn off 824,858 through 905,973
|
||||||
|
toggle 752,312 through 863,425
|
||||||
|
turn on 985,716 through 988,852
|
||||||
|
turn off 68,504 through 763,745
|
||||||
|
toggle 76,209 through 810,720
|
||||||
|
turn off 657,607 through 676,664
|
||||||
|
toggle 596,869 through 896,921
|
||||||
|
turn off 915,411 through 968,945
|
||||||
|
turn off 368,39 through 902,986
|
||||||
|
turn on 11,549 through 393,597
|
||||||
|
turn off 842,893 through 976,911
|
||||||
|
toggle 274,106 through 581,329
|
||||||
|
toggle 406,403 through 780,950
|
||||||
|
toggle 408,988 through 500,994
|
||||||
|
toggle 217,73 through 826,951
|
||||||
|
turn on 917,872 through 961,911
|
||||||
|
toggle 394,34 through 510,572
|
||||||
|
toggle 424,603 through 583,626
|
||||||
|
toggle 106,159 through 755,738
|
||||||
|
turn off 244,610 through 472,709
|
||||||
|
turn on 350,265 through 884,690
|
||||||
|
turn on 688,184 through 928,280
|
||||||
|
toggle 279,443 through 720,797
|
||||||
|
turn off 615,493 through 888,610
|
||||||
|
toggle 118,413 through 736,632
|
||||||
|
turn on 798,782 through 829,813
|
||||||
|
turn off 250,934 through 442,972
|
||||||
|
turn on 68,503 through 400,949
|
||||||
|
toggle 297,482 through 313,871
|
||||||
|
toggle 710,3 through 839,859
|
||||||
|
turn on 125,300 through 546,888
|
||||||
|
toggle 482,39 through 584,159
|
||||||
|
turn off 536,89 through 765,962
|
||||||
|
turn on 530,518 through 843,676
|
||||||
|
turn on 994,467 through 994,676
|
||||||
|
turn on 623,628 through 744,927
|
||||||
|
toggle 704,912 through 837,983
|
||||||
|
turn on 154,364 through 517,412
|
||||||
|
toggle 344,409 through 780,524
|
||||||
|
turn off 578,740 through 725,879
|
||||||
|
turn on 251,933 through 632,957
|
||||||
|
turn on 827,705 through 971,789
|
||||||
|
toggle 191,282 through 470,929
|
||||||
|
toggle 324,525 through 446,867
|
||||||
|
toggle 534,343 through 874,971
|
||||||
|
toggle 550,650 through 633,980
|
||||||
|
toggle 837,404 through 881,915
|
||||||
|
toggle 338,881 through 845,905
|
||||||
|
turn on 469,462 through 750,696
|
||||||
|
turn on 741,703 through 892,870
|
||||||
|
turn off 570,215 through 733,562
|
||||||
|
turn on 445,576 through 870,775
|
||||||
|
turn on 466,747 through 554,878
|
||||||
|
turn off 820,453 through 868,712
|
||||||
|
turn off 892,706 through 938,792
|
||||||
|
turn off 300,238 through 894,746
|
||||||
|
turn off 306,44 through 457,444
|
||||||
|
turn off 912,569 through 967,963
|
||||||
|
toggle 109,756 through 297,867
|
||||||
|
turn on 37,546 through 41,951
|
||||||
|
turn on 321,637 through 790,910
|
||||||
|
toggle 66,50 through 579,301
|
||||||
|
toggle 933,221 through 933,791
|
||||||
|
turn on 486,676 through 878,797
|
||||||
|
turn on 417,231 through 556,317
|
||||||
|
toggle 904,468 through 981,873
|
||||||
|
turn on 417,675 through 749,712
|
||||||
|
turn on 692,371 through 821,842
|
||||||
|
toggle 324,73 through 830,543
|
||||||
|
turn on 912,490 through 977,757
|
||||||
|
turn off 634,872 through 902,949
|
||||||
|
toggle 266,779 through 870,798
|
||||||
|
turn on 772,982 through 990,996
|
||||||
|
turn off 607,46 through 798,559
|
||||||
|
turn on 295,602 through 963,987
|
||||||
|
turn on 657,86 through 944,742
|
||||||
|
turn off 334,639 through 456,821
|
||||||
|
turn off 997,667 through 997,670
|
||||||
|
turn off 725,832 through 951,945
|
||||||
|
turn off 30,120 through 952,984
|
||||||
|
turn on 860,965 through 917,976
|
||||||
|
toggle 471,997 through 840,998
|
||||||
|
turn off 319,307 through 928,504
|
||||||
|
toggle 823,631 through 940,908
|
||||||
|
toggle 969,984 through 981,993
|
||||||
|
turn off 691,319 through 865,954
|
||||||
|
toggle 911,926 through 938,929
|
||||||
|
turn on 953,937 through 968,991
|
||||||
|
toggle 914,643 through 975,840
|
||||||
|
turn on 266,982 through 436,996
|
||||||
|
turn off 101,896 through 321,932
|
||||||
|
turn off 193,852 through 751,885
|
||||||
|
turn off 576,532 through 863,684
|
||||||
|
turn on 761,456 through 940,783
|
||||||
|
turn on 20,290 through 398,933
|
||||||
|
turn off 435,335 through 644,652
|
||||||
|
turn on 830,569 through 905,770
|
||||||
|
turn off 630,517 through 905,654
|
||||||
|
turn on 664,53 through 886,976
|
||||||
|
toggle 275,416 through 408,719
|
||||||
|
turn on 370,621 through 515,793
|
||||||
|
turn on 483,373 through 654,749
|
||||||
|
turn on 656,786 through 847,928
|
||||||
|
turn off 532,752 through 945,974
|
||||||
|
toggle 301,150 through 880,792
|
||||||
|
turn off 951,488 through 958,952
|
||||||
|
turn on 207,729 through 882,828
|
||||||
|
toggle 694,532 through 973,961
|
||||||
|
toggle 676,639 through 891,802
|
||||||
|
turn off 653,6 through 905,519
|
||||||
|
toggle 391,109 through 418,312
|
||||||
|
turn on 877,423 through 957,932
|
||||||
|
turn on 340,145 through 563,522
|
||||||
|
turn off 978,467 through 988,895
|
||||||
|
turn off 396,418 through 420,885
|
||||||
|
turn off 31,308 through 816,316
|
||||||
|
turn on 107,675 through 758,824
|
||||||
|
turn on 61,82 through 789,876
|
||||||
|
turn on 750,743 through 754,760
|
||||||
|
toggle 88,733 through 736,968
|
||||||
|
turn off 754,349 through 849,897
|
||||||
|
toggle 157,50 through 975,781
|
||||||
|
turn off 230,231 through 865,842
|
||||||
|
turn off 516,317 through 630,329
|
||||||
|
turn off 697,820 through 829,903
|
||||||
|
turn on 218,250 through 271,732
|
||||||
|
toggle 56,167 through 404,431
|
||||||
|
toggle 626,891 through 680,927
|
||||||
|
toggle 370,207 through 791,514
|
||||||
|
toggle 860,74 through 949,888
|
||||||
|
turn on 416,527 through 616,541
|
||||||
|
turn off 745,449 through 786,908
|
||||||
|
turn on 485,554 through 689,689
|
||||||
|
turn on 586,62 through 693,141
|
||||||
|
toggle 506,759 through 768,829
|
||||||
|
turn on 473,109 through 929,166
|
||||||
|
turn on 760,617 through 773,789
|
||||||
|
toggle 595,683 through 618,789
|
||||||
|
turn off 210,775 through 825,972
|
||||||
|
toggle 12,426 through 179,982
|
||||||
|
turn on 774,539 through 778,786
|
||||||
|
turn on 102,498 through 121,807
|
||||||
|
turn off 706,897 through 834,965
|
||||||
|
turn off 678,529 through 824,627
|
||||||
|
turn on 7,765 through 615,870
|
||||||
|
turn off 730,872 through 974,943
|
||||||
|
turn off 595,626 through 836,711
|
||||||
|
turn off 215,424 through 841,959
|
||||||
|
toggle 341,780 through 861,813
|
||||||
|
toggle 507,503 through 568,822
|
||||||
|
turn on 252,603 through 349,655
|
||||||
|
toggle 93,521 through 154,834
|
||||||
|
turn on 565,682 through 951,954
|
||||||
|
turn on 544,318 through 703,418
|
||||||
|
toggle 756,953 through 891,964
|
||||||
|
turn on 531,123 through 856,991
|
||||||
|
turn on 148,315 through 776,559
|
||||||
|
turn off 925,835 through 963,971
|
||||||
|
turn on 895,944 through 967,964
|
||||||
|
turn off 102,527 through 650,747
|
||||||
|
toggle 626,105 through 738,720
|
||||||
|
turn off 160,75 through 384,922
|
||||||
|
toggle 813,724 through 903,941
|
||||||
|
turn on 207,107 through 982,849
|
||||||
|
toggle 750,505 through 961,697
|
||||||
|
toggle 105,410 through 885,819
|
||||||
|
turn on 226,104 through 298,283
|
||||||
|
turn off 224,604 through 508,762
|
||||||
|
turn on 477,368 through 523,506
|
||||||
|
turn off 477,901 through 627,936
|
||||||
|
turn off 887,131 through 889,670
|
||||||
|
turn on 896,994 through 938,999
|
||||||
|
toggle 401,580 through 493,728
|
||||||
|
toggle 987,184 through 991,205
|
||||||
|
turn on 821,643 through 882,674
|
||||||
|
toggle 784,940 through 968,959
|
||||||
|
turn off 251,293 through 274,632
|
||||||
|
turn off 339,840 through 341,844
|
||||||
|
turn off 675,351 through 675,836
|
||||||
|
toggle 918,857 through 944,886
|
||||||
|
toggle 70,253 through 918,736
|
||||||
|
turn off 612,604 through 772,680
|
||||||
|
turn off 277,40 through 828,348
|
||||||
|
toggle 692,139 through 698,880
|
||||||
|
toggle 124,446 through 883,453
|
||||||
|
toggle 969,932 through 990,945
|
||||||
|
toggle 855,692 through 993,693
|
||||||
|
toggle 722,472 through 887,899
|
||||||
|
toggle 978,149 through 985,442
|
||||||
|
toggle 837,540 through 916,889
|
||||||
|
turn off 612,2 through 835,82
|
||||||
|
toggle 560,767 through 878,856
|
||||||
|
turn on 461,734 through 524,991
|
||||||
|
toggle 206,824 through 976,912
|
||||||
|
turn on 826,610 through 879,892
|
||||||
|
turn on 577,699 through 956,933
|
||||||
|
turn off 9,250 through 50,529
|
||||||
|
turn off 77,657 through 817,677
|
||||||
|
turn on 68,419 through 86,426
|
||||||
|
turn on 991,720 through 992,784
|
||||||
|
turn on 668,20 through 935,470
|
||||||
|
turn off 133,418 through 613,458
|
||||||
|
turn off 487,286 through 540,328
|
||||||
|
toggle 247,874 through 840,955
|
||||||
|
toggle 301,808 through 754,970
|
||||||
|
turn off 34,194 through 578,203
|
||||||
|
turn off 451,49 through 492,921
|
||||||
|
turn on 907,256 through 912,737
|
||||||
|
turn off 479,305 through 702,587
|
||||||
|
turn on 545,583 through 732,749
|
||||||
|
toggle 11,16 through 725,868
|
||||||
|
turn on 965,343 through 986,908
|
||||||
|
turn on 674,953 through 820,965
|
||||||
|
toggle 398,147 through 504,583
|
||||||
|
turn off 778,194 through 898,298
|
||||||
|
turn on 179,140 through 350,852
|
||||||
|
turn off 241,118 through 530,832
|
||||||
|
turn off 41,447 through 932,737
|
||||||
|
turn off 820,663 through 832,982
|
||||||
|
turn on 550,460 through 964,782
|
||||||
|
turn on 31,760 through 655,892
|
||||||
|
toggle 628,958 through 811,992
|
||||||
1
internal/data/2015/DayTen/input.txt
Normal file
1
internal/data/2015/DayTen/input.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1113122113
|
||||||
56
internal/data/2015/DayThirteen/input.txt
Normal file
56
internal/data/2015/DayThirteen/input.txt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
Alice would lose 57 happiness units by sitting next to Bob.
|
||||||
|
Alice would lose 62 happiness units by sitting next to Carol.
|
||||||
|
Alice would lose 75 happiness units by sitting next to David.
|
||||||
|
Alice would gain 71 happiness units by sitting next to Eric.
|
||||||
|
Alice would lose 22 happiness units by sitting next to Frank.
|
||||||
|
Alice would lose 23 happiness units by sitting next to George.
|
||||||
|
Alice would lose 76 happiness units by sitting next to Mallory.
|
||||||
|
Bob would lose 14 happiness units by sitting next to Alice.
|
||||||
|
Bob would gain 48 happiness units by sitting next to Carol.
|
||||||
|
Bob would gain 89 happiness units by sitting next to David.
|
||||||
|
Bob would gain 86 happiness units by sitting next to Eric.
|
||||||
|
Bob would lose 2 happiness units by sitting next to Frank.
|
||||||
|
Bob would gain 27 happiness units by sitting next to George.
|
||||||
|
Bob would gain 19 happiness units by sitting next to Mallory.
|
||||||
|
Carol would gain 37 happiness units by sitting next to Alice.
|
||||||
|
Carol would gain 45 happiness units by sitting next to Bob.
|
||||||
|
Carol would gain 24 happiness units by sitting next to David.
|
||||||
|
Carol would gain 5 happiness units by sitting next to Eric.
|
||||||
|
Carol would lose 68 happiness units by sitting next to Frank.
|
||||||
|
Carol would lose 25 happiness units by sitting next to George.
|
||||||
|
Carol would gain 30 happiness units by sitting next to Mallory.
|
||||||
|
David would lose 51 happiness units by sitting next to Alice.
|
||||||
|
David would gain 34 happiness units by sitting next to Bob.
|
||||||
|
David would gain 99 happiness units by sitting next to Carol.
|
||||||
|
David would gain 91 happiness units by sitting next to Eric.
|
||||||
|
David would lose 38 happiness units by sitting next to Frank.
|
||||||
|
David would gain 60 happiness units by sitting next to George.
|
||||||
|
David would lose 63 happiness units by sitting next to Mallory.
|
||||||
|
Eric would gain 23 happiness units by sitting next to Alice.
|
||||||
|
Eric would lose 69 happiness units by sitting next to Bob.
|
||||||
|
Eric would lose 33 happiness units by sitting next to Carol.
|
||||||
|
Eric would lose 47 happiness units by sitting next to David.
|
||||||
|
Eric would gain 75 happiness units by sitting next to Frank.
|
||||||
|
Eric would gain 82 happiness units by sitting next to George.
|
||||||
|
Eric would gain 13 happiness units by sitting next to Mallory.
|
||||||
|
Frank would gain 77 happiness units by sitting next to Alice.
|
||||||
|
Frank would gain 27 happiness units by sitting next to Bob.
|
||||||
|
Frank would lose 87 happiness units by sitting next to Carol.
|
||||||
|
Frank would gain 74 happiness units by sitting next to David.
|
||||||
|
Frank would lose 41 happiness units by sitting next to Eric.
|
||||||
|
Frank would lose 99 happiness units by sitting next to George.
|
||||||
|
Frank would gain 26 happiness units by sitting next to Mallory.
|
||||||
|
George would lose 63 happiness units by sitting next to Alice.
|
||||||
|
George would lose 51 happiness units by sitting next to Bob.
|
||||||
|
George would lose 60 happiness units by sitting next to Carol.
|
||||||
|
George would gain 30 happiness units by sitting next to David.
|
||||||
|
George would lose 100 happiness units by sitting next to Eric.
|
||||||
|
George would lose 63 happiness units by sitting next to Frank.
|
||||||
|
George would gain 57 happiness units by sitting next to Mallory.
|
||||||
|
Mallory would lose 71 happiness units by sitting next to Alice.
|
||||||
|
Mallory would lose 28 happiness units by sitting next to Bob.
|
||||||
|
Mallory would lose 10 happiness units by sitting next to Carol.
|
||||||
|
Mallory would gain 44 happiness units by sitting next to David.
|
||||||
|
Mallory would gain 22 happiness units by sitting next to Eric.
|
||||||
|
Mallory would gain 79 happiness units by sitting next to Frank.
|
||||||
|
Mallory would lose 16 happiness units by sitting next to George.
|
||||||
1
internal/data/2015/DayThree/input.txt
Normal file
1
internal/data/2015/DayThree/input.txt
Normal file
File diff suppressed because one or more lines are too long
1
internal/data/2015/DayTwelve/input.txt
Normal file
1
internal/data/2015/DayTwelve/input.txt
Normal file
File diff suppressed because one or more lines are too long
1000
internal/data/2015/DayTwo/input.txt
Normal file
1000
internal/data/2015/DayTwo/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user