Initial commit with solutions from 2024

This commit is contained in:
Thraix
2025-11-26 21:59:50 +01:00
commit be113565fb
72 changed files with 7030 additions and 0 deletions
+334
View File
@@ -0,0 +1,334 @@
---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: true
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveShortCaseStatements:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCaseArrows: false
AlignCaseColons: false
AlignConsecutiveTableGenBreakingDAGArgColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenCondOperatorColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignConsecutiveTableGenDefinitionColons:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
AlignFunctionDeclarations: false
AlignFunctionPointers: false
PadOperators: false
AlignEscapedNewlines: Left
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 0
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowBreakBeforeNoexceptSpecifier: Never
AllowShortBlocksOnASingleLine: Never
AllowShortCaseExpressionOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortCompoundRequirementOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AllowShortNamespacesOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AttributeMacros:
- __capability
- absl_nonnull
- absl_nullable
- absl_nullability_unknown
BinPackArguments: false
BinPackLongBracedList: true
BinPackParameters: false
BitFieldColonSpacing: Both
BracedInitializerIndentWidth: -1
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterExternBlock: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAdjacentStringLiterals: true
BreakAfterAttributes: Leave
BreakAfterJavaFieldAnnotations: false
BreakAfterReturnType: None
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Allman
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTemplateCloser: false
BreakBeforeTernaryOperators: true
BreakBinaryOperations: Never
BreakConstructorInitializers: BeforeColon
BreakFunctionDefinitionParameters: false
BreakInheritanceList: BeforeColon
BreakStringLiterals: true
BreakTemplateDeclarations: Yes
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
EnumTrailingComma: Leave
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*\.h>'
Priority: 1
SortPriority: 0
CaseSensitive: false
- Regex: '^<.*'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 3
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '([-_](test|unittest))?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExportBlock: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertNewlineAtEOF: false
InsertTrailingCommas: None
IntegerLiteralSeparator:
Binary: 0
BinaryMinDigits: 0
Decimal: 0
DecimalMinDigits: 0
Hex: 0
HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLines:
AtEndOfFile: false
AtStartOfBlock: false
AtStartOfFile: true
KeepFormFeed: false
LambdaBodyIndentation: Signature
LineEnding: DeriveLF
MacroBlockBegin: ''
MacroBlockEnd: ''
MainIncludeChar: Quote
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
OneLineFormatOffRegex: ''
PackConstructorInitializers: Never
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakBeforeMemberAccess: 150
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakScopeResolution: 500
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
PPIndentWidth: -1
QualifierAlignment: Leave
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
- ParseTestProto
- ParsePartialTestProto
CanonicalDelimiter: pb
BasedOnStyle: google
ReferenceAlignment: Pointer
ReflowComments: Always
RemoveBracesLLVM: false
RemoveEmptyLinesInUnwrappedLines: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: OwnLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Always
ShortNamespaceLines: 1
SkipMacroDefinitionBody: false
SortIncludes:
Enabled: true
IgnoreCase: false
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterOperatorKeyword: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterNot: false
AfterOverloadedOperator: false
AfterPlacementOperator: true
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: Never
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
ExceptDoubleParentheses: false
InCStyleCasts: false
InConditionalStatements: false
InEmptyParentheses: false
Other: false
SpacesInSquareBrackets: false
Standard: Auto
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TableGenBreakInsideDAGArg: DontBreak
TabWidth: 2
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
- BOOST_PP_STRINGIZE
- CF_SWIFT_NAME
- NS_SWIFT_NAME
- PP_STRINGIZE
- STRINGIZE
WrapNamespaceBodyWithEmptyLines: Leave
...
+10
View File
@@ -0,0 +1,10 @@
Debug/
Release/
.vs/
*/Debug/
*/Release/
bin/
.ycm_extra_conf.py
cookies.txt
res/*/*/input.txt
Makefile
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Tim Håkansson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+44
View File
@@ -0,0 +1,44 @@
# Solutions for AdventOfCode
Solutions are located in `AdventOfCode/src/<year>/days/` and the project is built using [MakeGen](https://github.com/Thraix/MakeGen)
## Framework
The framework used to run all the different days is a homemade framework with inspiration from google test.
It will automatically run all the days (cpp files) which are included into the project.
### Framework demo
A simple example of how the framework works can be seen in `src/days/day01.cpp`.
`REGISTER_DAY(dayXX, InputType, OutputType)` will register the current day in the framework and set the input and output type of the current day.
`REGISTER_TEST(dayXX, TestName, Part, Result)` registers a test for the current day with a given name, part number and expected result.
It will read the input file located in `res/dayXX/input.txt`.
`REGISTER_TEST_EXAMPLE(dayXX, TestName, Part, Result)` registers an example test for the current day with a given name, part number and expected result.
It will read the input file located in `res/dayXX/test_input.txt`.
`READ_INPUT(input)` is the macro function which reads the input for the current day (located in `res/dayXX/`).
`input` is of type `std::istream` which is based on the input file from `REGISTER_TEST`.
The return value should be of type `InputType` specified in `REGISTER_DAY`.
`OUTPUT1(input)` is the macro function which calculates the solution for part 1 of the current day.
This will run for all registered tests which has `Part=1` as argument.
`input` is of type `InputType` specified in `REGISTER_DAY`.
The return value should be of type `OutputType` specified in `REGISTER_DAY`.
`OUTPUT2(input)` is the macro function which calculates the solution for part 2 of the current day.
This will run for all registered tests which has `Part=2` as argument.
`input` is of type `InputType` specified in `REGISTER_DAY`.
The return value should be of type `OutputType` specified in `REGISTER_DAY`.
### Framework Q&A
Is it over-engineered for this use case? It certainly is!
Does it make it simpler to create the solutions for each day? Debatable
Was it fun making it? Yep!
Did I learn a lot by making it? Also yep!
Can you have more relevant questions in your Q&A? Nope!
+4
View File
@@ -0,0 +1,4 @@
-xc++
-Isrc/
-std=c++17
Executable
+27
View File
@@ -0,0 +1,27 @@
#/bin/sh
if [ ! -e cookies.txt ]; then
echo "No cookies.txt file exists"
exit 1
fi
list=$(find -name "Day*.cpp" | awk -F '/' '{print $3 "/" $4}' | awk -F '.' '{print $1}' | sed 's/Day//g' | sort)
echo $list
for item in $list; do
echo $item
year=$(echo $item | awk -F '/' '{print $1}')
day=$(echo $item | awk -F '/' '{print $2}')
echo "year: $year"
echo "day: $day"
dir="res/$year/day$day/input.txt"
echo $dir
if [ ! -e $dir ]; then
webitem=$(echo $day | sed 's/^0//g')
echo "Fetching $dir"
mkdir -p "res/$year/day$day/"
touch "res/$year/day$day/test_input.txt"
curl -s --cookie cookies.txt https://adventofcode.com/$year/day/$webitem/input >$dir
fi
exit
done
+23
View File
@@ -0,0 +1,23 @@
<makegen>
<configuration name="Release">
<generatehfile>false</generatehfile>
<includedir>src/</includedir>
<outputdir>bin/</outputdir>
<outputname>adventofcode.out</outputname>
<outputtype>executable</outputtype>
<projectname>AdventOfCode</projectname>
<srcdir>src/</srcdir>
</configuration>
<configuration name="Debug">
<generatehfile>false</generatehfile>
<includedir>src/</includedir>
<cflag>-g</cflag>
<outputdir>bin/</outputdir>
<outputname>adventofcode.out</outputname>
<outputtype>executable</outputtype>
<projectname>AdventOfCode</projectname>
<srcdir>src/</srcdir>
</configuration>
<target>Debug</target>
<version>v1.3.0</version>
</makegen>
+6
View File
@@ -0,0 +1,6 @@
3 4
4 3
2 5
1 3
3 9
3 3
+6
View File
@@ -0,0 +1,6 @@
7 6 4 2 1
1 2 7 8 9
9 7 6 2 1
1 3 2 4 5
8 6 4 4 1
1 3 6 7 9
+1
View File
@@ -0,0 +1 @@
xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))
+10
View File
@@ -0,0 +1,10 @@
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
+28
View File
@@ -0,0 +1,28 @@
47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13
75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47
+10
View File
@@ -0,0 +1,10 @@
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#...
+9
View File
@@ -0,0 +1,9 @@
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
+12
View File
@@ -0,0 +1,12 @@
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
+1
View File
@@ -0,0 +1 @@
2333133121414131402
+8
View File
@@ -0,0 +1,8 @@
89010123
78121874
87430965
96549874
45678903
32019012
01329801
10456732
+1
View File
@@ -0,0 +1 @@
125 17
+10
View File
@@ -0,0 +1,10 @@
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
+15
View File
@@ -0,0 +1,15 @@
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279
+12
View File
@@ -0,0 +1,12 @@
p=0,4 v=3,-3
p=6,3 v=-1,-3
p=10,3 v=-1,2
p=2,0 v=2,-1
p=0,0 v=1,3
p=3,0 v=-2,-2
p=7,6 v=-1,-3
p=3,0 v=-1,-2
p=9,3 v=2,3
p=7,3 v=-1,2
p=2,4 v=2,-3
p=9,5 v=-3,-3
+21
View File
@@ -0,0 +1,21 @@
##########
#..O..O.O#
#......O.#
#.OO..O.O#
#..O@..O.#
#O#..O...#
#O..O..O.#
#.OO.O.OO#
#....O...#
##########
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
+15
View File
@@ -0,0 +1,15 @@
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############
+5
View File
@@ -0,0 +1,5 @@
Register A: 2024
Register B: 0
Register C: 0
Program: 0,3,5,4,3,0
+25
View File
@@ -0,0 +1,25 @@
5,4
4,2
4,5
3,0
2,1
6,3
2,4
1,5
0,6
3,3
2,6
5,1
1,2
5,5
2,5
6,5
1,4
0,4
6,4
1,1
6,1
1,0
0,5
1,6
2,0
+10
View File
@@ -0,0 +1,10 @@
r, wr, b, g, bwu, rb, gb, br
brwrr
bggr
gbbr
rrbgbr
ubwu
bwurrg
brgr
bbrgwb
+15
View File
@@ -0,0 +1,15 @@
###############
#...#...#.....#
#.#.#.#.#.###.#
#S#...#.#.#...#
#######.#.#.###
#######.#.#...#
#######.#.###.#
###..E#...#...#
###.#######.###
#...###...#...#
#.#####.#.###.#
#.#...#.#.#...#
#.#.#.#.#.#.###
#...#...#...###
###############
+5
View File
@@ -0,0 +1,5 @@
029A
980A
179A
456A
379A
+4
View File
@@ -0,0 +1,4 @@
1
2
3
2024
+32
View File
@@ -0,0 +1,32 @@
kh-tc
qp-kh
de-cg
ka-co
yn-aq
qp-ub
cg-tb
vc-aq
tb-ka
wh-tc
yn-cg
kh-ub
ta-co
de-co
tc-td
tb-wq
wh-td
ta-ka
td-qp
aq-cg
wq-ub
ub-vc
de-ta
wq-aq
wq-vc
wh-yn
ka-de
kh-ta
co-tc
wh-qp
tb-vc
td-yn
+47
View File
@@ -0,0 +1,47 @@
x00: 1
x01: 0
x02: 1
x03: 1
x04: 0
y00: 1
y01: 1
y02: 1
y03: 1
y04: 1
ntg XOR fgs -> mjb
y02 OR x01 -> tnw
kwq OR kpj -> z05
x00 OR x03 -> fst
tgd XOR rvg -> z01
vdt OR tnw -> bfw
bfw AND frj -> z10
ffh OR nrd -> bqk
y00 AND y03 -> djm
y03 OR y00 -> psh
bqk OR frj -> z08
tnw OR fst -> frj
gnj AND tgd -> z11
bfw XOR mjb -> z00
x03 OR x00 -> vdt
gnj AND wpb -> z02
x04 AND y00 -> kjc
djm OR pbm -> qhw
nrd AND vdt -> hwm
kjc AND fst -> rvg
y04 OR y02 -> fgs
y01 AND x02 -> pbm
ntg OR kjc -> kwq
psh XOR fgs -> tgd
qhw XOR tgd -> z09
pbm OR djm -> kpj
x03 XOR y03 -> ffh
x00 XOR y04 -> ntg
bfw OR bqk -> z06
nrd XOR fgs -> wpb
frj XOR qhw -> z04
bqk OR frj -> z07
y03 OR x01 -> nrd
hwm AND bqk -> z03
tgd XOR rvg -> z12
tnw OR pbm -> gnj
+39
View File
@@ -0,0 +1,39 @@
#####
.####
.####
.####
.#.#.
.#...
.....
#####
##.##
.#.##
...##
...#.
...#.
.....
.....
#....
#....
#...#
#.#.#
#.###
#####
.....
.....
#.#..
###..
###.#
###.#
#####
.....
.....
.....
#....
#.#..
#.#.#
#####
+59
View File
@@ -0,0 +1,59 @@
#include "common/aoc.h"
namespace day01
{
using InputType = std::pair<std::vector<int>, std::vector<int>>;
REGISTER_DAY(2024, Day01, InputType, int);
REGISTER_TEST_EXAMPLE(2024, Day01, ExampleInput, 1, 11);
REGISTER_TEST(2024, Day01, Input, 1, 1834060);
REGISTER_TEST_EXAMPLE(2024, Day01, ExampleInput, 2, 31);
REGISTER_TEST(2024, Day01, Input, 2, 21607792);
READ_INPUT(input)
{
std::vector<int> aVec, bVec;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
int a, b;
ss >> a >> b;
aVec.emplace_back(a);
bVec.emplace_back(b);
}
return {aVec, bVec};
}
OUTPUT1(input)
{
std::vector<int> copyA = input.first;
std::vector<int> copyB = input.second;
std::sort(copyA.begin(), copyA.end());
std::sort(copyB.begin(), copyB.end());
int sum = 0;
for (int i = 0; i < input.first.size(); i++)
{
sum += std::abs(copyA[i] - copyB[i]);
}
return sum;
}
OUTPUT2(input)
{
std::map<int, int> idCount;
for (int i : input.second)
{
idCount[i]++;
}
int sum = 0;
for (int i : input.first)
{
sum += i * idCount[i];
}
return sum;
}
}
+75
View File
@@ -0,0 +1,75 @@
#include "common/aoc.h"
namespace day02
{
REGISTER_DAY(2024, Day02, std::vector<std::vector<int>>, int);
REGISTER_TEST_EXAMPLE(2024, Day02, ExampleInput, 1, 2);
REGISTER_TEST(2024, Day02, Input, 1, 549);
REGISTER_TEST_EXAMPLE(2024, Day02, ExampleInput, 2, 4);
REGISTER_TEST(2024, Day02, Input, 2, 589);
READ_INPUT(input)
{
std::vector<std::vector<int>> list;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
list.emplace_back(Input::ReadInts(ss, ' '));
}
return list;
}
bool Validate(const std::vector<int>& vec)
{
bool lt = vec[0] < vec[1];
for (int j = 1; j < vec.size(); j++)
{
int diff = std::abs(vec[j] - vec[j - 1]);
if (vec[j - 1] < vec[j] != lt || diff < 1 || diff > 3)
{
return false;
}
}
return true;
}
bool ValidateWithout(const std::vector<int>& vec, int index)
{
std::vector<int> cpy = vec;
if (index < vec.size())
cpy.erase(cpy.begin() + index);
return Validate(cpy);
}
OUTPUT1(input)
{
int sum = 0;
for (int i = 0; i < input.size(); i++)
{
if (Validate(input[i]))
{
sum++;
}
}
return sum;
}
OUTPUT2(input)
{
int sum = 0;
for (int i = 0; i < input.size(); i++)
{
for (int j = 0; j < input[i].size() + 1; j++)
{
if (ValidateWithout(input[i], j))
{
sum++;
break;
}
}
}
return sum;
}
}
+50
View File
@@ -0,0 +1,50 @@
#include "common/aoc.h"
namespace day03
{
REGISTER_DAY(2024, Day03, std::string, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day03, ExampleInput, 1, 161);
REGISTER_TEST(2024, Day03, Input, 1, 178794710);
REGISTER_TEST_EXAMPLE(2024, Day03, ExampleInput, 2, 48);
REGISTER_TEST(2024, Day03, Input, 2, 76729637);
READ_INPUT(input)
{
return Input::Read(input);
}
int32_t Mult(const std::string& str)
{
int i, j;
std::stringstream ss{str};
ss >> "mul(" >> i >> "," >> j >> ")";
return i * j;
}
OUTPUT1(input)
{
int32_t sum = 0;
std::vector<std::string> matches = Helper::GetAllRegexMatches(input, "mul\\([0-9]*,[0-9]*\\)");
for (auto& str : matches) sum += Mult(str);
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
bool mul = true;
std::vector<std::string> matches =
Helper::GetAllRegexMatches(input, "(mul\\([0-9]*,[0-9]*\\)|do\\(\\)|don\'t\\(\\))");
for (auto& str : matches)
{
if (str == "do()")
mul = true;
else if (str == "don\'t()")
mul = false;
else if (mul)
sum += Mult(str);
}
return sum;
}
}
+76
View File
@@ -0,0 +1,76 @@
#include "common/aoc.h"
namespace day04
{
REGISTER_DAY(2024, Day04, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day04, ExampleInput, 1, 18);
REGISTER_TEST(2024, Day04, Input, 1, 2543);
REGISTER_TEST_EXAMPLE(2024, Day04, ExampleInput, 2, 9);
REGISTER_TEST(2024, Day04, Input, 2, 1930);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
OUTPUT1(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (y < input.height - 3 && input.Get(x, y) == 'X' && input.Get(x, y + 1) == 'M' &&
input.Get(x, y + 2) == 'A' && input.Get(x, y + 3) == 'S')
sum++;
if (y < input.height - 3 && input.Get(x, y) == 'S' && input.Get(x, y + 1) == 'A' &&
input.Get(x, y + 2) == 'M' && input.Get(x, y + 3) == 'X')
sum++;
if (x < input.width - 3 && input.Get(x, y) == 'X' && input.Get(x + 1, y) == 'M' && input.Get(x + 2, y) == 'A' &&
input.Get(x + 3, y) == 'S')
sum++;
if (x < input.width - 3 && input.Get(x, y) == 'S' && input.Get(x + 1, y) == 'A' && input.Get(x + 2, y) == 'M' &&
input.Get(x + 3, y) == 'X')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x, y) == 'X' && input.Get(x + 1, y + 1) == 'M' &&
input.Get(x + 2, y + 2) == 'A' && input.Get(x + 3, y + 3) == 'S')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x, y) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y + 2) == 'M' && input.Get(x + 3, y + 3) == 'X')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x + 3, y) == 'X' &&
input.Get(x + 2, y + 1) == 'M' && input.Get(x + 1, y + 2) == 'A' && input.Get(x, y + 3) == 'S')
sum++;
if (y < input.height - 3 && x < input.width - 3 && input.Get(x + 3, y) == 'S' &&
input.Get(x + 2, y + 1) == 'A' && input.Get(x + 1, y + 2) == 'M' && input.Get(x, y + 3) == 'X')
sum++;
}
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height - 2; y++)
{
for (int x = 0; x < input.width - 2; x++)
{
if (input.Get(x, y) == 'M' && input.Get(x, y + 2) == 'M' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'S' && input.Get(x + 2, y + 2) == 'S')
sum++;
if (input.Get(x, y) == 'M' && input.Get(x, y + 2) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'M' && input.Get(x + 2, y + 2) == 'S')
sum++;
if (input.Get(x, y) == 'S' && input.Get(x, y + 2) == 'M' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'S' && input.Get(x + 2, y + 2) == 'M')
sum++;
if (input.Get(x, y) == 'S' && input.Get(x, y + 2) == 'S' && input.Get(x + 1, y + 1) == 'A' &&
input.Get(x + 2, y) == 'M' && input.Get(x + 2, y + 2) == 'M')
sum++;
}
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day05
{
struct Manual
{
std::vector<std::pair<int, int>> pairs;
std::vector<std::vector<int>> pages;
};
REGISTER_DAY(2024, Day05, Manual, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day05, ExampleInput, 1, 143);
REGISTER_TEST(2024, Day05, Input, 1, 5964);
REGISTER_TEST_EXAMPLE(2024, Day05, ExampleInput, 2, 123);
REGISTER_TEST(2024, Day05, Input, 2, 4719);
READ_INPUT(input)
{
std::vector<std::pair<int, int>> pairs;
std::string str;
while (std::getline(input, str))
{
if (str.empty())
break;
int i, j;
std::stringstream ss{str};
ss >> i >> "|" >> j;
pairs.emplace_back(i, j);
}
std::vector<std::vector<int>> pages;
while (std::getline(input, str))
{
std::stringstream ss{str};
pages.emplace_back(Input::ReadInts(ss, ','));
}
return Manual{pairs, pages};
}
std::pair<bool, int> IsValid(const std::vector<int>& input, const std::vector<std::pair<int, int>>& pairs)
{
for (int i = 0; i < input.size() - 1; i++)
{
for (const auto& pair : pairs)
{
if (input[i] == pair.second && input[i + 1] == pair.first)
{
return {false, i};
}
}
}
return {true, 0};
}
std::vector<int> Sort(std::vector<int> input, const std::vector<std::pair<int, int>>& pairs)
{
bool valid;
do
{
auto [res, index] = IsValid(input, pairs);
if (!res)
std::swap(input[index], input[index + 1]);
valid = res;
} while (!valid);
return input;
}
OUTPUT1(input)
{
int32_t sum = 0;
for (const auto& man : input.pages)
{
if (IsValid(man, input.pairs).first)
sum += man[man.size() / 2];
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (const auto& man : input.pages)
{
if (!IsValid(man, input.pairs).first)
{
auto newMan = Sort(man, input.pairs);
sum += newMan[newMan.size() / 2];
}
}
return sum;
}
}
+90
View File
@@ -0,0 +1,90 @@
#include "common/aoc.h"
namespace day06
{
REGISTER_DAY(2024, Day06, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day06, ExampleInput, 1, 41);
REGISTER_TEST(2024, Day06, Input, 1, 4776);
REGISTER_TEST_EXAMPLE(2024, Day06, ExampleInput, 2, 6);
REGISTER_TEST(2024, Day06, Input, 2, 1586);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
bool TestObstacle(const Array2D<char>& map, Index2D obstacle, Index2D startPos, Index2D startDir)
{
std::set<std::pair<Index2D, Index2D>> visited;
Index2D index = startPos;
Index2D dir = startDir;
while (map.IsInside(index) && visited.find(std::pair{index, dir}) == visited.end())
{
visited.emplace(index, dir);
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if ((map.IsInside(newIndex) && map.Get(newIndex) == '#') || newIndex == obstacle)
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
index = newIndex;
}
}
return visited.find(std::pair{index, dir}) != visited.end();
}
OUTPUT1(input)
{
std::set<Index2D> visited;
Index2D index = input.Find('^');
Index2D dir{0, -1};
while (input.IsInside(index))
{
visited.emplace(index);
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if (input.IsInside(newIndex) && input.Get(newIndex) == '#')
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
index = newIndex;
}
}
return visited.size();
}
OUTPUT2(input)
{
std::set<Index2D> visited;
Index2D index = input.Find('^');
Index2D dir{0, -1};
int32_t sum = 0;
while (input.IsInside(index))
{
Index2D newIndex{index.x + dir.x, index.y + dir.y};
if (input.IsInside(newIndex) && input.Get(newIndex) == '#')
{
Index2D newDir{-dir.y, dir.x};
dir = newDir;
}
else
{
if (visited.count(newIndex) == 0)
{
if (TestObstacle(input, newIndex, index, dir))
sum++;
}
visited.emplace(index);
index = newIndex;
}
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day07
{
struct Data
{
int64_t result;
std::vector<int64_t> terms;
};
REGISTER_DAY(2024, Day07, std::vector<Data>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day07, ExampleInput, 1, 3749);
REGISTER_TEST(2024, Day07, Input, 1, 945512582195);
REGISTER_TEST_EXAMPLE(2024, Day07, ExampleInput, 2, 11387);
REGISTER_TEST(2024, Day07, Input, 2, 271691107779347);
READ_INPUT(input)
{
std::vector<Data> datas;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
Data data;
ss >> data.result >> ": ";
while (std::getline(ss, str, ' '))
{
data.terms.emplace_back(std::stoll(str));
}
datas.emplace_back(data);
}
return datas;
}
bool CanCalculateResultP1(const Data& data, int index, int64_t current)
{
if (index >= data.terms.size())
return data.result == current;
if (index == 0)
return CanCalculateResultP1(data, index + 1, data.terms[index]);
if (CanCalculateResultP1(data, index + 1, current + data.terms[index]))
return true;
else if (CanCalculateResultP1(data, index + 1, current * data.terms[index]))
return true;
return false;
}
int64_t Concat(int64_t i1, int64_t i2)
{
int64_t base = 10;
while (i2 >= base) base *= 10;
return i1 * base + i2;
}
bool CanCalculateResultP2(const Data& data, int index, int64_t current)
{
if (index >= data.terms.size())
return data.result == current;
if (index == 0)
return CanCalculateResultP2(data, index + 1, data.terms[index]);
if (CanCalculateResultP2(data, index + 1, current + data.terms[index]))
return true;
else if (CanCalculateResultP2(data, index + 1, current * data.terms[index]))
return true;
else if (CanCalculateResultP2(data, index + 1, Concat(current, data.terms[index])))
return true;
return false;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (const auto& data : input)
{
if (CanCalculateResultP1(data, 0, 0))
sum += data.result;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (const auto& data : input)
{
if (CanCalculateResultP2(data, 0, 0))
sum += data.result;
}
return sum;
}
}
+94
View File
@@ -0,0 +1,94 @@
#include "common/aoc.h"
namespace day08
{
struct Data
{
std::map<char, std::vector<Index2D>> map;
int width;
int height;
};
REGISTER_DAY(2024, Day08, Data, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day08, ExampleInput, 1, 14);
REGISTER_TEST(2024, Day08, Input, 1, 244);
REGISTER_TEST_EXAMPLE(2024, Day08, ExampleInput, 2, 34);
REGISTER_TEST(2024, Day08, Input, 2, 912);
READ_INPUT(input)
{
Data data;
std::string str;
int y = 0;
while (std::getline(input, str))
{
for (int i = 0; i < str.size(); i++)
{
if (str[i] != '.')
data.map[str[i]].emplace_back(Index2D{i, y});
}
data.width = str.size();
y++;
}
data.height = y;
return data;
}
OUTPUT1(input)
{
std::set<Index2D> antinodes;
int32_t sum = 0;
for (auto& [freq, locations] : input.map)
{
for (int i = 0; i < locations.size(); i++)
{
for (int j = i + 1; j < locations.size(); j++)
{
Index2D diff = locations[i] - locations[j];
Index2D newPos1 = locations[i] + diff;
Index2D newPos2 = locations[j] - diff;
if (newPos1.x >= 0 && newPos1.x < input.width && newPos1.y >= 0 && newPos1.y < input.height)
antinodes.emplace(newPos1);
if (newPos2.x >= 0 && newPos2.x < input.width && newPos2.y >= 0 && newPos2.y < input.height)
antinodes.emplace(newPos2);
}
}
}
return antinodes.size();
}
OUTPUT2(input)
{
std::set<Index2D> antinodes;
for (auto& [freq, locations] : input.map)
{
for (int i = 0; i < locations.size(); i++)
{
for (int j = i + 1; j < locations.size(); j++)
{
Index2D diff = locations[i] - locations[j];
int offset = 0;
while (true)
{
Index2D newPos = locations[i] + diff * offset;
if (newPos.x < 0 || newPos.x >= input.width || newPos.y < 0 || newPos.y >= input.height)
break;
antinodes.emplace(newPos);
offset++;
}
offset = 0;
while (true)
{
Index2D newPos = locations[j] - diff * offset;
if (newPos.x < 0 || newPos.x >= input.width || newPos.y < 0 || newPos.y >= input.height)
break;
antinodes.emplace(newPos);
offset++;
}
}
}
}
return antinodes.size();
}
}
+143
View File
@@ -0,0 +1,143 @@
#include "common/aoc.h"
namespace day09
{
REGISTER_DAY(2024, Day09, std::vector<int>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day09, ExampleInput, 1, 1928);
REGISTER_TEST(2024, Day09, Input, 1, 6283404590840);
REGISTER_TEST_EXAMPLE(2024, Day09, ExampleInput, 2, 2858);
REGISTER_TEST(2024, Day09, Input, 2, 6304576012713);
READ_INPUT(input)
{
std::string str;
std::getline(input, str);
if (str.size() % 2 == 0)
str.pop_back();
std::vector<int> disk;
std::transform(str.begin(), str.end(), std::back_inserter(disk), [](char c) { return c - '0'; });
return disk;
}
struct File
{
int64_t pos;
int64_t size;
int64_t id;
};
OUTPUT1(input)
{
int64_t sum = 0;
std::vector<std::pair<int, int>> resMemory;
resMemory.emplace_back(input[0], 0);
int currentFreeIndex = 1;
int currentFreeMemory = input[currentFreeIndex];
int currentMemoryIndex = input.size() - 1;
int currentMemoryToMove = input[currentMemoryIndex];
while (true)
{
if (currentMemoryToMove <= currentFreeMemory)
{
resMemory.emplace_back(currentMemoryToMove, currentMemoryIndex / 2);
currentFreeMemory -= currentMemoryToMove;
currentMemoryIndex -= 2;
currentMemoryToMove = input[currentMemoryIndex];
if (currentMemoryIndex < currentFreeIndex)
break;
}
else
{
resMemory.emplace_back(currentFreeMemory, currentMemoryIndex / 2);
currentMemoryToMove -= currentFreeMemory;
currentFreeMemory = 0;
}
if (currentFreeMemory == 0)
{
if (currentFreeIndex + 1 == currentMemoryIndex)
{
resMemory.back().first += currentMemoryToMove;
break;
}
else
{
resMemory.emplace_back(input[currentFreeIndex + 1], (currentFreeIndex + 1) / 2);
currentFreeIndex += 2;
currentFreeMemory = input[currentFreeIndex];
}
}
}
int64_t pos = 0;
for (auto [count, id] : resMemory)
{
// Based on sum of integer formula:
// S = numberOfIntegers * (firstNumber + lastNumber) / 2
int64_t firstNumber = pos;
int64_t lastNumber = pos + count - 1;
sum += count * (firstNumber + lastNumber) / 2 * id;
pos += count;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
std::map<int64_t, int64_t> idToPos;
std::vector<File> resMemory;
std::vector<std::pair<int64_t, int64_t>> freeMemory;
int pos = 0;
for (int i = 0; i < input.size(); i += 2)
{
idToPos.emplace(i / 2, pos);
pos += input[i];
if (i != input.size() - 1)
{
freeMemory.emplace_back(pos, input[i + 1]);
pos += input[i + 1];
}
}
for (int i = input.size() - 1; i >= 0; i -= 2)
{
int fileSize = input[i];
int fileId = i / 2;
int filePos = idToPos[fileId];
for (int j = 0; j < freeMemory.size(); j++)
{
auto& [freeMemoryPos, freeMemorySize] = freeMemory[j];
if (freeMemoryPos > filePos)
{
// Couldn't find a suitable place for the file, don't move it
resMemory.emplace_back(File{filePos, fileSize, fileId});
break;
}
if (freeMemorySize >= fileSize)
{
resMemory.emplace_back(File{freeMemoryPos, fileSize, fileId});
freeMemorySize -= fileSize;
freeMemoryPos += fileSize;
if (freeMemorySize == 0)
freeMemory.erase(freeMemory.begin() + j);
break;
}
}
}
for (const auto& file : resMemory)
{
// Based on sum of integer formula:
// S = numberOfIntegers * (firstNumber + lastNumber) / 2
int64_t firstNumber = file.pos;
int64_t lastNumber = file.pos + file.size - 1;
sum += file.size * (firstNumber + lastNumber) / 2 * file.id;
}
return sum;
}
}
+84
View File
@@ -0,0 +1,84 @@
#include "common/aoc.h"
namespace day10
{
REGISTER_DAY(2024, Day10, Array2D<int>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day10, ExampleInput, 1, 36);
REGISTER_TEST(2024, Day10, Input, 1, 786);
REGISTER_TEST_EXAMPLE(2024, Day10, ExampleInput, 2, 81);
REGISTER_TEST(2024, Day10, Input, 2, 1722);
READ_INPUT(input)
{
return Input::ReadDigitsAsArray2D(input);
}
void GetTops(const Array2D<int>& map, Index2D pos, std::set<Index2D>& visited)
{
int val = map.Get(pos);
if (val == 9)
{
visited.emplace(pos);
return;
}
std::vector<Index2D> indices{Index2D{-1, 0}, Index2D{1, 0}, Index2D{0, -1}, Index2D{0, 1}};
for (auto index : indices)
{
if (map.IsInside(pos + index) && map.Get(pos + index) == val + 1)
GetTops(map, pos + index, visited);
}
}
int CountPaths(const Array2D<int>& map, Index2D pos)
{
int val = map.Get(pos);
if (val == 9)
return 1;
int paths = 0;
std::vector<Index2D> indices{Index2D{-1, 0}, Index2D{1, 0}, Index2D{0, -1}, Index2D{0, 1}};
for (auto index : indices)
{
if (map.IsInside(pos + index) && map.Get(pos + index) == val + 1)
paths += CountPaths(map, pos + index);
}
return paths;
}
OUTPUT1(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (input.Get(x, y) == 0)
{
std::set<Index2D> visited;
GetTops(input, Index2D{x, y}, visited);
sum += visited.size();
}
}
}
return sum;
}
OUTPUT2(input)
{
int32_t sum = 0;
for (int y = 0; y < input.height; y++)
{
for (int x = 0; x < input.width; x++)
{
if (input.Get(x, y) == 0)
{
sum += CountPaths(input, Index2D{x, y});
}
}
}
return sum;
}
}
+96
View File
@@ -0,0 +1,96 @@
#include "common/aoc.h"
namespace day11
{
using Stones = std::map<int64_t, int64_t>;
REGISTER_DAY(2024, Day11, Stones, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day11, ExampleInput, 1, 55312);
REGISTER_TEST(2024, Day11, Input, 1, 189167);
REGISTER_TEST_EXAMPLE(2024, Day11, ExampleInput, 2, 65601038650482);
REGISTER_TEST(2024, Day11, Input, 2, 225253278506288);
READ_INPUT(input)
{
Stones stones;
for (auto i : Input::ReadInt64s(input, ' ')) stones[i]++;
return stones;
}
int logi10(int64_t i)
{
int log = 1;
int64_t base = 10;
while (i >= base)
{
base *= 10;
log++;
}
return log;
}
int64_t pow10(int i)
{
int64_t pow = 1;
for (int j = 0; j < i; j++) pow *= 10;
return pow;
}
std::pair<int64_t, int64_t> split(int64_t i)
{
int log = logi10(i);
int64_t pow = pow10(log / 2);
return {i / pow, i % pow};
}
void Step(std::map<int64_t, int64_t>& stones)
{
std::map<int64_t, int64_t> next;
for (auto [cur, count] : stones)
{
if (cur == 0)
{
next[1] += count;
}
else if (logi10(cur) % 2 == 0)
{
auto [i1, i2] = split(cur);
next[i1] += count;
next[i2] += count;
}
else
{
next[cur * 2024] += count;
}
}
stones = std::move(next);
}
int64_t CalculateStones(const std::map<int64_t, int64_t>& stones, int iterations)
{
std::map<int64_t, int64_t> cpy = stones;
for (int i = 0; i < iterations; i++) Step(cpy);
int64_t sum = 0;
for (auto& [cur, count] : cpy)
{
sum += count;
}
return sum;
}
OUTPUT1(input)
{
return CalculateStones(input, 25);
}
OUTPUT2(input)
{
return CalculateStones(input, 75);
}
}
+117
View File
@@ -0,0 +1,117 @@
#include "common/aoc.h"
namespace day12
{
REGISTER_DAY(2024, Day12, Array2D<char>, int);
REGISTER_TEST_EXAMPLE(2024, Day12, ExampleInput, 1, 1930);
REGISTER_TEST(2024, Day12, Input, 1, 1550156);
REGISTER_TEST_EXAMPLE(2024, Day12, ExampleInput, 2, 1206);
REGISTER_TEST(2024, Day12, Input, 2, 946084);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
void CalculateAreaAndPerimiter(const Array2D<char>& map,
Index2D position,
std::set<Index2D>& totalVisited,
std::set<Index2D>& area,
std::set<std::pair<Index2D, Index2D>>& perimeter)
{
char c = map.Get(position);
std::stack<Index2D> toEvaluate;
toEvaluate.emplace(position);
area.emplace(position);
while (!toEvaluate.empty())
{
Index2D current = toEvaluate.top();
toEvaluate.pop();
totalVisited.emplace(current);
for (auto& dir : Helper::GetNeighborDirections())
{
Index2D newPos = current + dir;
if (area.count(newPos) == 0)
{
if (map.IsInside(newPos) && map.Get(newPos) == c)
{
toEvaluate.emplace(newPos);
area.emplace(newPos);
}
else
perimeter.emplace(current, dir);
}
}
}
}
void RemovePerimetersNextToPos(std::set<std::pair<Index2D, Index2D>>& perimeter,
Index2D pos,
Index2D normal,
Index2D dir)
{
Index2D next = pos + dir;
auto it = perimeter.find({next, normal});
while (it != perimeter.end())
{
perimeter.erase(it);
next = next + dir;
it = perimeter.find({next, normal});
}
}
int CalculatePerimeterSideSize(std::set<std::pair<Index2D, Index2D>>& perimeter)
{
int perimeterSize = 0;
while (!perimeter.empty())
{
auto [index, dir] = *perimeter.begin();
perimeter.erase(perimeter.begin());
perimeterSize++;
RemovePerimetersNextToPos(perimeter, index, dir, Index2D{dir.y, dir.x});
RemovePerimetersNextToPos(perimeter, index, dir, Index2D{-dir.y, -dir.x});
}
return perimeterSize;
}
OUTPUT1(input)
{
int sum = 0;
std::set<Index2D> visited;
for (auto it = input.begin(); it != input.end(); it++)
{
Index2D index = it.index;
if (visited.count(index) == 0)
{
std::set<Index2D> area;
std::set<std::pair<Index2D, Index2D>> perimeter;
CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter);
sum += perimeter.size() * area.size();
}
}
return sum;
}
OUTPUT2(input)
{
int sum = 0;
std::set<Index2D> visited;
for (auto it = input.begin(); it != input.end(); it++)
{
if (visited.count(it.index) == 0)
{
std::set<Index2D> area;
std::set<std::pair<Index2D, Index2D>> perimeter;
CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter);
int perimeterSize = CalculatePerimeterSideSize(perimeter);
sum += perimeterSize * area.size();
}
}
return sum;
}
}
+102
View File
@@ -0,0 +1,102 @@
#include "common/aoc.h"
namespace day13
{
struct ClawMachine
{
int64_t buttonAX;
int64_t buttonAY;
int64_t buttonBX;
int64_t buttonBY;
int64_t prizeX;
int64_t prizeY;
};
REGISTER_DAY(2024, Day13, std::vector<ClawMachine>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day13, ExampleInput, 1, 480);
REGISTER_TEST(2024, Day13, Input, 1, 26005);
REGISTER_TEST_EXAMPLE(2024, Day13, ExampleInput, 2, 875318608908);
REGISTER_TEST(2024, Day13, Input, 2, 105620095782547);
READ_INPUT(input)
{
std::vector<ClawMachine> clawMachines;
std::string str;
while (std::getline(input, str))
{
ClawMachine machine;
std::stringstream ss{str};
ss >> "Button A: X+" >> machine.buttonAX >> ", Y+" >> machine.buttonAY;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Button B: X+" >> machine.buttonBX >> ", Y+" >> machine.buttonBY;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Prize: X=" >> machine.prizeX >> ", Y=" >> machine.prizeY;
std::getline(input, str);
clawMachines.emplace_back(machine);
}
return clawMachines;
}
int64_t GetCost(const ClawMachine& machine)
{
// Solve
// aX * a + bX * b = pX
// aY * a + bY * b = pY
// aY * aX * a + aY * bX * b = aY * pX
// -aY * aX * a + -aX * bY * b = -aX * pY
// (aY * bX * b) + (-aX * bY * b) = aY * pX + (-aX * pY)
// (aY * bX - aX * bY) * b = aY * pX - aX * pY
// b = (aY * pX - aX * pY) / (aY * bX - aX * bY)
// a = (pX - bX * b) / aX
int64_t aYbX = machine.buttonAY * machine.buttonBX;
int64_t aYpX = machine.buttonAY * machine.prizeX;
int64_t aXbY = machine.buttonAX * machine.buttonBY;
int64_t aXpY = machine.buttonAX * machine.prizeY;
if (aYbX - aXbY == 0)
{
std::cerr << "Multiple solutions" << std::endl;
}
else if ((aYpX - aXpY) % (aYbX - aXbY) == 0) // integer solution exists
{
int64_t b = (aYpX - aXpY) / (aYbX - aXbY);
int64_t a = (machine.prizeX - b * machine.buttonBX) / machine.buttonAX;
if (a >= 0 && b >= 0)
return b + a * 3;
}
return 0; // No solution
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto& machine : input)
{
sum += GetCost(machine);
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (auto machine : input)
{
machine.prizeX += 10000000000000;
machine.prizeY += 10000000000000;
sum += GetCost(machine);
}
return sum;
}
}
+98
View File
@@ -0,0 +1,98 @@
#include "common/aoc.h"
namespace day14
{
struct Robot
{
Index2D pos;
Index2D vel;
};
REGISTER_DAY(2024, Day14, std::vector<Robot>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day14, ExampleInput, 1, 12);
REGISTER_TEST(2024, Day14, Input, 1, 228421332);
// REGISTER_TEST_EXAMPLE(2024, Day14, ExampleInput, 2, 31);
REGISTER_TEST(2024, Day14, Input, 2, 7790);
READ_INPUT(input)
{
std::vector<Robot> robots;
std::string str;
while (std::getline(input, str))
{
Robot robot;
std::stringstream ss{str};
ss >> "p=" >> robot.pos.x >> "," >> robot.pos.y >> "v=" >> robot.vel.x >> "," >> robot.vel.y;
robots.emplace_back(robot);
}
return robots;
}
OUTPUT1(input)
{
int width = 101;
int height = 103;
if (isExample)
{
width = 11;
height = 7;
}
int a{0}, b{0}, c{0}, d{0};
for (auto& robot : input)
{
Index2D endPos = robot.pos + robot.vel * 100;
endPos.x = (endPos.x % width + width) % width;
endPos.y = (endPos.y % height + height) % height;
if (endPos.x < width / 2 && endPos.y < height / 2)
a++;
if (endPos.x < width / 2 && endPos.y > height / 2)
b++;
if (endPos.x > width / 2 && endPos.y < height / 2)
c++;
if (endPos.x > width / 2 && endPos.y > height / 2)
d++;
}
return a * b * c * d;
}
int GetStepCoord(const std::vector<Robot>& robots, int length, int dimension)
{
int maxIndex = 0;
int maxNeighbors = 0;
for (int i = 1; i <= length; i++)
{
std::vector<int> amounts(length, 0);
for (const auto& robot : robots)
{
int endPos = robot.pos[dimension] + robot.vel[dimension] * i;
endPos = (endPos % length + length) % length;
amounts[endPos]++;
}
int neighbors = 0;
for (int i = 0; i < length; i++)
{
neighbors += amounts[i] * (amounts[(i + 1) % length] + amounts[(i + length - 1) % length]);
}
if (neighbors > maxNeighbors)
{
maxNeighbors = neighbors;
maxIndex = i;
}
}
return maxIndex;
}
OUTPUT2(input)
{
int width = 101;
int height = 103;
int stepX = GetStepCoord(input, width, 0);
int stepY = GetStepCoord(input, height, 1);
return Helper::ChineseRemainderTheorem({width, height}, {stepX, stepY});
}
}
+198
View File
@@ -0,0 +1,198 @@
#include "common/aoc.h"
namespace day15
{
struct Map
{
Array2D<char> map;
std::string input;
};
REGISTER_DAY(2024, Day15, Map, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day15, ExampleInput, 1, 10092);
REGISTER_TEST(2024, Day15, Input, 1, 1526673);
REGISTER_TEST_EXAMPLE(2024, Day15, ExampleInput, 2, 9021);
REGISTER_TEST(2024, Day15, Input, 2, 1535509);
READ_INPUT(input)
{
Map map;
map.map = Input::ReadArray2D(input);
std::string str;
while (std::getline(input, str)) map.input += str;
return map;
}
void Move(Array2D<char>& map, Index2D boxPos, Index2D dir)
{
if (dir.x == 0)
{
char leftC = map.Get(boxPos.x, boxPos.y + dir.y);
if (leftC == '[')
Move(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
if (leftC == ']')
Move(map, Index2D{boxPos.x - 1, boxPos.y + dir.y}, dir);
char rightC = map.Get(boxPos.x + 1, boxPos.y + dir.y);
if (rightC == '[')
Move(map, Index2D{boxPos.x + 1, boxPos.y + dir.y}, dir);
if (rightC == ']')
Move(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
map.Set(boxPos.x, boxPos.y + dir.y, '[');
map.Set(boxPos.x + 1, boxPos.y + dir.y, ']');
map.Set(boxPos.x, boxPos.y, '.');
map.Set(boxPos.x + 1, boxPos.y, '.');
}
else
{
if (map.Get(boxPos.x + dir.x * 2, boxPos.y) == '[')
Move(map, Index2D{boxPos.x + dir.x * 2, boxPos.y}, dir);
map.Set(boxPos.x + dir.x, boxPos.y, '[');
map.Set(boxPos.x + dir.x + 1, boxPos.y, ']');
}
}
bool CanMove(const Array2D<char>& map, Index2D boxPos, Index2D dir);
bool CanMoveSide(const Array2D<char>& map, Index2D boxSidePos, Index2D dir)
{
char c = map[boxSidePos];
if (c == '.')
return true;
else if (c == '[')
return CanMove(map, Index2D{boxSidePos.x, boxSidePos.y}, dir);
else if (c == ']')
return CanMove(map, Index2D{boxSidePos.x - 1, boxSidePos.y}, dir);
return false;
}
// boxPos is position of [
bool CanMove(const Array2D<char>& map, Index2D boxPos, Index2D dir)
{
if (dir.x == 0)
{
bool canMoveLeftSide = CanMoveSide(map, Index2D{boxPos.x, boxPos.y + dir.y}, dir);
bool canMoveRightSide = CanMoveSide(map, Index2D{boxPos.x + 1, boxPos.y + dir.y}, dir);
return canMoveLeftSide && canMoveRightSide;
}
else
{
Index2D pos = boxPos + dir;
while (true)
{
char n = map[pos];
if (n == '.')
return true;
else if (n == '#')
return false;
pos = pos + dir;
}
}
}
int64_t CalculateResult(const Array2D<char>& map)
{
int64_t sum = 0;
for (auto it = map.begin(); it != map.end(); it++)
if (*it == 'O' || *it == '[')
sum += it.index.x + it.index.y * 100;
return sum;
}
OUTPUT1(input)
{
Map cpy = input;
Index2D position = cpy.map.Find('@');
for (auto& dirC : cpy.input)
{
Index2D dir = Helper::GetDirection(dirC);
char nextChar = cpy.map[position + dir];
if (nextChar == '.')
{
cpy.map[position] = '.';
cpy.map[position + dir] = '@';
position = position + dir;
}
else if (nextChar == 'O')
{
Index2D pos = position + dir;
while (true)
{
char n = cpy.map[pos];
if (n == '.')
{
cpy.map[position] = '.';
cpy.map[position + dir] = '@';
cpy.map[pos] = 'O';
position = position + dir;
break;
}
else if (n == '#')
break;
pos = pos + dir;
}
}
}
return CalculateResult(cpy.map);
}
OUTPUT2(input)
{
Array2D<char> map{input.map.width * 2, input.map.height, '.'};
for (auto it = input.map.begin(); it != input.map.end(); it++)
{
if (*it == 'O')
{
map.Set(it.index.x * 2, it.index.y, '[');
map.Set(it.index.x * 2 + 1, it.index.y, ']');
}
else if (*it == '#')
{
map.Set(it.index.x * 2, it.index.y, '#');
map.Set(it.index.x * 2 + 1, it.index.y, '#');
}
else if (*it == '@')
{
map.Set(it.index.x * 2, it.index.y, '@');
}
}
Index2D position = map.Find('@');
for (auto& dirC : input.input)
{
Index2D dir = Helper::GetDirection(dirC);
char nextChar = map[position + dir];
if (nextChar == '.')
{
map[position] = '.';
map[position + dir] = '@';
position = position + dir;
}
else if (nextChar == ']' || nextChar == '[')
{
Index2D boxPos = position + dir;
if (nextChar == ']')
boxPos.x -= 1;
if (CanMove(map, boxPos, dir))
{
Move(map, boxPos, dir);
map[position] = '.';
map[position + dir] = '@';
position = position + dir;
}
}
}
return CalculateResult(map);
}
}
+55
View File
@@ -0,0 +1,55 @@
#include "common/aoc.h"
namespace day16
{
REGISTER_DAY(2024, Day16, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day16, ExampleInput, 1, 7036);
REGISTER_TEST(2024, Day16, Input, 1, 72428);
REGISTER_TEST_EXAMPLE(2024, Day16, ExampleInput, 2, 45);
REGISTER_TEST(2024, Day16, Input, 2, 456);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
std::vector<std::pair<int, std::pair<Index2D, Index2D>>> Branch(const Array2D<char>& map,
const std::pair<Index2D, Index2D>& state)
{
std::vector<std::pair<int, std::pair<Index2D, Index2D>>> paths;
if (map.Get(state.first + state.second) != '#')
paths.emplace_back(1, std::pair{state.first + state.second, state.second});
Index2D dir2 = Index2D{state.second.y, -state.second.x};
if (map.Get(state.first + dir2) != '#')
paths.emplace_back(1001, std::pair{state.first + dir2, dir2});
Index2D dir3 = Index2D{-state.second.y, state.second.x};
if (map.Get(state.first + dir3) != '#')
paths.emplace_back(1001, std::pair{state.first + dir3, dir3});
return paths;
}
bool Goal(const Array2D<char>& map, const std::pair<Index2D, Index2D>& state)
{
return map.Get(state.first) == 'E';
}
OUTPUT1(input)
{
return Helper::Dijkstras(input, std::pair{input.Find('S'), Index2D{1, 0}}, Branch, Goal);
}
OUTPUT2(input)
{
std::set<std::pair<Index2D, Index2D>> allVisited =
Helper::DijkstrasAllVisited(input, std::pair{input.Find('S'), Index2D{1, 0}}, Branch, Goal);
std::set<Index2D> solutions;
for (const auto& [pos, dir] : allVisited) solutions.emplace(pos);
return solutions.size();
}
}
+173
View File
@@ -0,0 +1,173 @@
#include <bitset>
#include "common/aoc.h"
namespace day17
{
struct Program
{
int64_t A;
int64_t B;
int64_t C;
std::vector<int> instructions;
};
REGISTER_DAY(2024, Day17, Program, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day17, ExampleInput, 1, 0);
REGISTER_TEST(2024, Day17, Input, 1, 0);
REGISTER_TEST_EXAMPLE(2024, Day17, ExampleInput, 2, 117440);
REGISTER_TEST(2024, Day17, Input, 2, 202322348616234);
READ_INPUT(input)
{
Program program;
std::string str;
std::getline(input, str);
std::stringstream ss{str};
ss >> "Register A: " >> program.A;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Register B: " >> program.B;
std::getline(input, str);
ss = std::stringstream{str};
ss >> "Register C: " >> program.C;
std::getline(input, str);
std::getline(input, str, ' ');
program.instructions = Input::ReadInts(input, ',');
return program;
}
int64_t Combo(int i, int64_t a, int64_t b, int64_t c)
{
if (i <= 3)
return i;
if (i == 4)
return a;
if (i == 5)
return b;
if (i == 6)
return c;
std::cerr << "Invalid combo: " << i << std::endl;
return -1;
}
std::vector<int> RunProgram(const Program& program)
{
int64_t a = program.A;
int64_t b = program.B;
int64_t c = program.C;
int pc = 0;
std::vector<int> result;
while (pc < program.instructions.size())
{
int ins = program.instructions[pc];
int arg = program.instructions[pc + 1];
if (ins == 0)
a = a >> Combo(arg, a, b, c);
else if (ins == 1)
b = b ^ arg;
else if (ins == 2)
b = Combo(arg, a, b, c) % 8;
else if (ins == 3 && a != 0)
pc = arg - 2;
else if (ins == 4)
b = b ^ c;
else if (ins == 5)
result.emplace_back(Combo(arg, a, b, c) % 8);
else if (ins == 6)
b = a >> Combo(arg, a, b, c);
else if (ins == 7)
c = a >> Combo(arg, a, b, c);
pc += 2;
}
return result;
}
std::vector<int64_t> GetDiffPeriodicity(const std::vector<int64_t>& ints)
{
if (ints.empty())
return {};
const int MIN_LOOP_COUNT = 3;
if ((ints.size() - 1) % MIN_LOOP_COUNT != 0)
return {};
std::vector<int64_t> diffs;
for (int i = 0; i < ints.size() - 1; i++)
{
diffs.emplace_back(ints[i + 1] - ints[i]);
}
int loopSize = ints.size() / MIN_LOOP_COUNT;
for (int i = 0; i < MIN_LOOP_COUNT - 1; i++)
{
for (int j = 0; j < loopSize; j++)
{
if (diffs[i * loopSize + j] != diffs[i * loopSize + loopSize + j])
{
return {};
}
}
}
return {diffs.begin(), diffs.begin() + loopSize};
}
OUTPUT1(input)
{
std::vector<int> result = RunProgram(input);
std::cout << result << std::endl;
return 0;
}
OUTPUT2(input)
{
int64_t aRegister = 0;
int currentDepth = 0;
std::vector<int64_t> diffs{1};
std::vector<int64_t> aRegisterSolution{};
Program program = input;
int offset = 0;
while (true)
{
program.A = aRegister;
std::vector<int> vec = RunProgram(program);
if (vec == input.instructions)
{
return aRegister;
}
if (vec.size() > currentDepth && vec[currentDepth] == program.instructions[currentDepth])
{
aRegisterSolution.emplace_back(aRegister);
}
std::vector<int64_t> loop = GetDiffPeriodicity(aRegisterSolution);
if (!loop.empty())
{
aRegister = aRegisterSolution[0];
diffs = loop;
aRegisterSolution.clear();
offset = 0;
currentDepth++;
}
else
{
aRegister += diffs[offset % diffs.size()];
offset++;
}
}
return 0;
}
}
+96
View File
@@ -0,0 +1,96 @@
#include "common/aoc.h"
namespace day18
{
using Memory = std::map<Index2D, int>;
REGISTER_DAY(2024, Day18, Memory, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day18, ExampleInput, 1, 22);
REGISTER_TEST(2024, Day18, Input, 1, 304);
REGISTER_TEST_EXAMPLE(2024, Day18, ExampleInput, 2, 20);
REGISTER_TEST(2024, Day18, Input, 2, 2876);
READ_INPUT(input)
{
std::map<Index2D, int> memory;
std::string str;
int i = 0;
while (std::getline(input, str))
{
Index2D index;
std::stringstream ss{str};
ss >> index.x >> "," >> index.y;
memory.emplace(index, i);
i++;
}
return memory;
}
struct Map
{
const std::map<Index2D, int>& memory;
int size;
int time;
};
std::vector<std::pair<int, Index2D>> Branch(const Map& map, const Index2D& current)
{
std::vector<std::pair<int, Index2D>> branch;
for (auto& dir : Helper::GetNeighborDirections())
{
Index2D pos = current + dir;
if (pos.x < 0 || pos.y < 0 || pos.x > map.size || pos.y > map.size)
continue;
auto it = map.memory.find(pos);
if (it == map.memory.end() || it->second >= map.time)
branch.emplace_back(1, pos);
}
return branch;
}
bool Goal(const Map& map, const Index2D& current)
{
return current.x == map.size && current.y == map.size;
}
bool ValidPath(const Map& map, int index)
{
Map cpy = map;
cpy.time = index;
return Helper::Dijkstras(cpy, Index2D{0, 0}, Branch, Goal) != 0;
}
OUTPUT1(input)
{
int32_t sum = 0;
int time = 1024;
int size = 70;
if (isExample)
{
size = 6;
time = 12;
}
Map map{input, size, time};
return Helper::Dijkstras(map, Index2D{0, 0}, Branch, Goal);
}
OUTPUT2(input)
{
int size = 70;
if (isExample)
size = 6;
Map map{input, size, 0};
int result = Helper::BinarySearch(map, 0, input.size(), ValidPath);
for (auto& index : input)
{
if (index.second == result)
{
std::cout << index.first.x << "," << index.first.y << std::endl;
return result;
}
}
return 0;
}
}
+116
View File
@@ -0,0 +1,116 @@
#include "common/aoc.h"
namespace day19
{
struct Towels
{
std::vector<std::string> patterns;
std::vector<std::string> towels;
};
REGISTER_DAY(2024, Day19, Towels, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day19, ExampleInput, 1, 6);
REGISTER_TEST(2024, Day19, Input, 1, 353);
REGISTER_TEST_EXAMPLE(2024, Day19, ExampleInput, 2, 16);
REGISTER_TEST(2024, Day19, Input, 2, 880877787214477);
READ_INPUT(input)
{
Towels strings;
std::string str;
std::getline(input, str);
std::stringstream ss{str};
while (std::getline(ss, str, ','))
{
if (str.front() == ' ')
str = str.substr(1);
strings.patterns.emplace_back(str);
}
std::getline(input, str);
while (std::getline(input, str))
{
strings.towels.emplace_back(str);
}
return strings;
}
bool HasCombination(const std::string& towel,
const std::vector<std::string>& patterns,
int offset,
std::set<int>& memoization)
{
if (offset == towel.size())
return true;
if (memoization.count(offset) != 0)
return false;
for (auto& other : patterns)
{
if (Helper::StartsWith(std::string_view(towel.c_str() + offset, towel.size() - offset), other))
{
if (HasCombination(towel, patterns, offset + other.size(), memoization))
{
return true;
}
}
}
memoization.emplace(offset);
return false;
}
int64_t AmountOfCombinations(const std::string& towel,
const std::vector<std::string>& patterns,
int offset,
std::map<int, int64_t>& memoization)
{
if (offset == towel.size())
return 1;
auto it = memoization.find(offset);
if (it != memoization.end())
return it->second;
int64_t amount = 0;
for (auto& other : patterns)
{
if (Helper::StartsWith(std::string_view(towel.c_str() + offset, towel.size() - offset), other))
{
amount += AmountOfCombinations(towel, patterns, offset + other.size(), memoization);
}
}
memoization[offset] = amount;
return amount;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto& towel : input.towels)
{
std::set<int> memoization;
if (HasCombination(towel, input.patterns, 0, memoization))
sum++;
}
return sum;
}
OUTPUT2(input)
{
int64_t sum = 0;
for (auto& towel : input.towels)
{
std::map<int, int64_t> memoization;
sum += AmountOfCombinations(towel, input.patterns, 0, memoization);
}
return sum;
}
}
+93
View File
@@ -0,0 +1,93 @@
#include "common/aoc.h"
namespace day20
{
REGISTER_DAY(2024, Day20, Array2D<char>, int32_t);
REGISTER_TEST_EXAMPLE(2024, Day20, ExampleInput, 1, 1);
REGISTER_TEST(2024, Day20, Input, 1, 1429);
REGISTER_TEST_EXAMPLE(2024, Day20, ExampleInput, 2, 285);
REGISTER_TEST(2024, Day20, Input, 2, 988931);
READ_INPUT(input)
{
return Input::ReadArray2D(input);
}
// Single path DFS, assumes the path can never hit a dead end
static std::pair<int, std::vector<Index2D>> DFS(const Array2D<char>& map, const Index2D& initial)
{
std::vector<Index2D> path;
Index2D prev = Index2D{-1, -1};
std::pair<int, Index2D> cur{0, initial};
path.emplace_back(initial);
while (map[cur.second] != 'E')
{
for (auto dir : Helper::GetNeighborDirections())
{
Index2D pos = cur.second + dir;
if (map.IsInside(pos))
{
if (pos != prev && map[pos] != '#')
{
prev = cur.second;
cur.first++;
cur.second = pos;
path.emplace_back(pos);
break;
}
}
}
}
return {cur.first, path};
}
int Cheat(const Array2D<char>& map, int cheatTime, int diff)
{
int sum = 0;
std::pair<int, std::vector<Index2D>> path = DFS(map, map.Find('S'));
std::vector<int> distanceToGoal(map.width * map.height, -1);
for (int i = 0; i < path.second.size(); i++)
{
distanceToGoal[map.GetIndex(path.second[i])] = path.second.size() - i - 1;
}
for (int i = 0; i < path.second.size(); i++)
{
for (int y = -cheatTime; y <= cheatTime; y++)
{
int yAbs = std::abs(y);
for (int x = -cheatTime + yAbs; x <= cheatTime - yAbs; x++)
{
int xAbs = std::abs(x);
int cost = i + xAbs + yAbs;
Index2D pos{path.second[i].x + x, path.second[i].y + y};
if (map.IsInside(pos))
{
int index = map.GetIndex(pos);
if (distanceToGoal[index] >= 0)
{
if (cost + distanceToGoal[index] <= path.first - diff)
{
sum++;
}
}
}
}
}
}
return sum;
}
OUTPUT1(input)
{
return Cheat(input, 2, isExample ? 50 : 100);
}
OUTPUT2(input)
{
return Cheat(input, 20, isExample ? 50 : 100);
}
}
+213
View File
@@ -0,0 +1,213 @@
#include "common/aoc.h"
namespace day21
{
REGISTER_DAY(2024, Day21, std::vector<std::string>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day21, ExampleInput, 1, 126384);
REGISTER_TEST(2024, Day21, Input, 1, 177814);
REGISTER_TEST_EXAMPLE(2024, Day21, ExampleInput, 2, 154115708116294);
REGISTER_TEST(2024, Day21, Input, 2, 220493992841852);
READ_INPUT(input)
{
return Input::ReadLines(input);
}
Index2D KeyPadCoord(char c)
{
if (c == '7')
return Index2D{0, 0};
if (c == '8')
return Index2D{1, 0};
if (c == '9')
return Index2D{2, 0};
if (c == '4')
return Index2D{0, 1};
if (c == '5')
return Index2D{1, 1};
if (c == '6')
return Index2D{2, 1};
if (c == '1')
return Index2D{0, 2};
if (c == '2')
return Index2D{1, 2};
if (c == '3')
return Index2D{2, 2};
if (c == '0')
return Index2D{1, 3};
if (c == 'A')
return Index2D{2, 3};
abort();
}
Index2D MovePadCoord(const Index2D& from, const Index2D& to)
{
if (from.x < to.x)
return Index2D{2, 1};
if (from.x > to.x)
return Index2D{0, 1};
if (from.y < to.y)
return Index2D{1, 1};
if (from.y > to.y)
return Index2D{1, 0};
abort();
}
bool IsSameDir(const Index2D& prev, const Index2D& cur, const Index2D& next)
{
Index2D diffA = cur - prev;
Index2D diffB = next - cur;
if (diffA.x < 0 && diffB.x < 0)
return true;
if (diffA.x > 0 && diffB.x > 0)
return true;
if (diffA.y < 0 && diffB.y < 0)
return true;
if (diffA.y > 0 && diffB.y > 0)
return true;
return false;
}
std::vector<std::vector<Index2D>> PossiblePaths(const Index2D& from, const Index2D& to, bool keypad)
{
std::set<Index2D> visited;
std::queue<std::vector<Index2D>> openPath;
openPath.emplace(std::vector<Index2D>{from});
std::vector<std::vector<Index2D>> paths;
while (!openPath.empty())
{
auto path = openPath.front();
openPath.pop();
visited.emplace(path.back());
if (path.back() == to)
{
paths.emplace_back(path);
continue;
}
for (const auto& dir : Helper::GetNeighborDirections())
{
Index2D newPos = path.back() + dir;
bool keyPadCondition =
keypad && newPos != Index2D{0, 3} && newPos.x >= 0 && newPos.x <= 2 && newPos.y >= 0 && newPos.y <= 3;
bool movePadCondition =
!keypad && newPos != Index2D{0, 0} && newPos.x >= 0 && newPos.x <= 2 && newPos.y >= 0 && newPos.y <= 1;
if (visited.count(newPos) == 0 && (keyPadCondition || movePadCondition))
{
std::vector<Index2D> newPath = path;
if (path.size() == 1)
newPath.emplace_back(newPos);
else if (!IsSameDir(path[path.size() - 2], path.back(), newPos))
newPath.emplace_back(newPos);
else
newPath.back() = newPos;
openPath.emplace(newPath);
}
}
}
return paths;
}
struct State
{
Index2D from;
Index2D to;
int depth;
int64_t amount;
bool operator<(const State& other) const
{
if (from != other.from)
return from < other.from;
if (to != other.to)
return to < other.to;
if (depth != other.depth)
return depth < other.depth;
return amount < other.amount;
}
};
int64_t Move(int maxDepth,
const Index2D& from,
const Index2D& to,
int depth,
int64_t amount,
std::map<State, int64_t>& memoization)
{
if (depth == maxDepth)
return Helper::ManhattanDistance(to, from) + amount;
State state{from, to, depth, amount};
auto it = memoization.find(state);
if (it != memoization.end())
return it->second;
std::vector<std::vector<Index2D>> paths = PossiblePaths(from, to, depth == 0);
int64_t min = std::numeric_limits<int64_t>::max();
for (const auto& path : paths)
{
Index2D moveFrom = Index2D{2, 0};
int64_t sum = 0;
for (int j = 0; j < path.size() - 1; j++)
{
Index2D moveTo = MovePadCoord(path[j], path[j + 1]);
int diff = Helper::ManhattanDistance(path[j], path[j + 1]);
sum += Move(maxDepth, moveFrom, moveTo, depth + 1, diff, memoization);
moveFrom = moveTo;
}
int diff = Helper::ManhattanDistance(path.back(), path.front());
sum += Move(maxDepth, moveFrom, Index2D{2, 0}, depth + 1, amount, memoization);
min = std::min(sum, min);
}
memoization[state] = min;
return min;
}
int64_t Solve(const std::vector<std::string>& input, int depth)
{
std::map<State, int64_t> memoization;
int64_t sum = 0;
for (int i = 0; i < input.size(); i++)
{
Index2D moveFrom = KeyPadCoord('A');
int64_t codeSum = 0;
for (auto c : input[i])
{
Index2D keyPadCoord = KeyPadCoord(c);
codeSum += Move(depth, moveFrom, keyPadCoord, 0, 1, memoization);
moveFrom = keyPadCoord;
}
int64_t codeNum = std::stoi(input[i]);
sum += codeNum * codeSum;
}
return sum;
}
OUTPUT1(input)
{
return Solve(input, 2);
}
OUTPUT2(input)
{
return Solve(input, 25);
}
}
+80
View File
@@ -0,0 +1,80 @@
#include "common/aoc.h"
namespace day22
{
REGISTER_DAY(2024, Day22, std::vector<int>, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day22, ExampleInput, 1, 37990510);
REGISTER_TEST(2024, Day22, Input, 1, 14273043166);
REGISTER_TEST_EXAMPLE(2024, Day22, ExampleInput, 2, 23);
REGISTER_TEST(2024, Day22, Input, 2, 1667);
READ_INPUT(input)
{
return Input::ReadInts(input);
}
int64_t NextNumber(int64_t currentNum)
{
currentNum = ((currentNum * 64) ^ currentNum) % 16777216;
currentNum = ((currentNum / 32) ^ currentNum) % 16777216;
currentNum = ((currentNum * 2048) ^ currentNum) % 16777216;
return currentNum;
}
int GetDiffIndex(const std::vector<int>& vec)
{
int val = 0;
for (int i = vec.size() - 5; i < vec.size() - 1; i++)
{
val *= 19;
val += vec[i + 1] - vec[i] + 9;
}
return val;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (auto num : input)
{
for (int i = 0; i < 2000; i++)
{
num = NextNumber(num);
}
sum += num;
}
return sum;
}
OUTPUT2(input)
{
const int MAX_INDEX = 19 * 19 * 19 * 19;
std::vector<int> monkeySequences(MAX_INDEX, 0);
int best = 0;
for (auto num : input)
{
std::vector<bool> alreadyFoundSequence(MAX_INDEX, false);
std::vector<int> sequence;
sequence.emplace_back(num % 10);
for (int i = 0; i < 2000; i++)
{
num = NextNumber(num);
sequence.emplace_back(num % 10);
if (sequence.size() >= 5)
{
int diffIndex = GetDiffIndex(sequence);
if (!alreadyFoundSequence[diffIndex])
{
alreadyFoundSequence[diffIndex] = true;
monkeySequences[diffIndex] += num % 10;
if (monkeySequences[diffIndex] > best)
best = monkeySequences[diffIndex];
}
}
}
}
return best;
}
}
+105
View File
@@ -0,0 +1,105 @@
#include "common/aoc.h"
namespace day23
{
struct Network
{
std::map<std::string, std::set<std::string>> graph;
};
REGISTER_DAY(2024, Day23, Network, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day23, ExampleInput, 1, 7);
REGISTER_TEST(2024, Day23, Input, 1, 1170);
REGISTER_TEST_EXAMPLE(2024, Day23, ExampleInput, 2, 52);
REGISTER_TEST(2024, Day23, Input, 2, 350);
READ_INPUT(input)
{
Network network;
std::string str;
while (std::getline(input, str))
{
std::stringstream ss{str};
std::string s1, s2;
std::getline(ss, s1, '-');
std::getline(ss, s2, '-');
network.graph[s1].emplace(s2);
network.graph[s2].emplace(s1);
}
return network;
}
void FindLargest(const Network& network, std::set<std::string>& computers, std::set<std::string>& largest)
{
if (computers.size() <= largest.size())
return;
for (const auto& computer : computers)
{
for (const auto& otherComputer : computers)
{
if (otherComputer == computer)
continue;
const auto& neighbors = network.graph.find(computer)->second;
if (neighbors.find(otherComputer) == neighbors.end())
{
// Solution wasn't found, so remove an element and try for a smaller set of computers
for (const auto& node : computers)
{
auto cpy = computers;
cpy.erase(node);
FindLargest(network, cpy, largest);
}
return;
}
}
}
largest = computers;
}
OUTPUT1(input)
{
std::set<std::set<std::string>> groupsOfThree;
for (const auto& computer : input.graph)
{
if (computer.first[0] != 't')
continue;
for (auto it = computer.second.begin(); it != computer.second.end(); it++)
{
auto it2 = it;
it2++;
for (it2; it2 != computer.second.end(); it2++)
{
const auto& paths = input.graph.find(*it)->second;
if (paths.find(*it2) != paths.end())
groupsOfThree.emplace(std::set<std::string>{computer.first, *it, *it2});
}
}
}
return groupsOfThree.size();
}
OUTPUT2(input)
{
std::set<std::string> largest;
for (const auto& computer : input.graph)
{
std::set<std::string> computers = computer.second;
computers.emplace(computer.first);
FindLargest(input, computers, largest);
}
for (const auto& s : largest) std::cout << s << ",";
std::cout << std::endl;
// Some arbitrary solution number to have some form of verification in the code.
// Sum all letters of the computers (with a=0, b=1, etc)
int64_t sum = 0;
for (const auto& s : largest) sum += (s[0] - 'a') + (s[1] - 'a');
return sum;
}
}
+233
View File
@@ -0,0 +1,233 @@
#include "common/aoc.h"
namespace day24
{
struct Gate
{
std::string left;
std::string right;
std::string op;
std::string res;
};
struct Gates
{
std::map<std::string, int> gates;
std::vector<Gate> gateOperation;
};
REGISTER_DAY(2024, Day24, Gates, int64_t);
REGISTER_TEST_EXAMPLE(2024, Day24, ExampleInput, 1, 2024);
REGISTER_TEST(2024, Day24, Input, 1, 57344080719736);
// REGISTER_TEST_EXAMPLE(2024, Day24, ExampleInput, 2, 0);
REGISTER_TEST(2024, Day24, Input, 2, 2328);
READ_INPUT(input)
{
std::string str;
Gates gates;
while (std::getline(input, str))
{
if (str.empty())
break;
std::stringstream ss{str};
std::string name;
int val;
ss >> name >> val;
name.pop_back(); // Remove ':'
gates.gates.emplace(name, val);
}
while (std::getline(input, str))
{
std::stringstream ss{str};
std::string left;
std::string right;
std::string op;
std::string res;
ss >> left >> op >> right >> "->" >> res;
Gate gate{left, right, op, res};
gates.gates.emplace(res, -1);
gates.gateOperation.emplace_back(gate);
}
return gates;
}
int GetVal(Gates& gates, const std::string& wire)
{
return gates.gates[wire];
}
void SetVal(Gates& gates, const std::string& wire, int val)
{
gates.gates[wire] = val;
}
void SetInput(Gates& gates, int64_t x, int64_t y)
{
for (auto& wire : gates.gates)
{
if (wire.first[0] == 'x')
{
wire.second = x & 1;
x >>= 1;
}
if (wire.first[0] == 'y')
{
wire.second = y & 1;
y >>= 1;
}
}
}
std::string IndexToStr(int i)
{
std::string s;
if (i < 10)
s += "0";
s += std::to_string(i);
return s;
}
void SwapOutputs(Gates& gates, const std::string& output1, const std::string& output2, std::set<std::string>& swapped)
{
int i1 = -1;
int i2 = -1;
for (int i = 0; i < gates.gateOperation.size(); i++)
{
if (gates.gateOperation[i].res == output1)
i1 = i;
if (gates.gateOperation[i].res == output2)
i2 = i;
}
std::swap(gates.gateOperation[i1].res, gates.gateOperation[i2].res);
swapped.emplace(gates.gateOperation[i1].res);
swapped.emplace(gates.gateOperation[i2].res);
}
std::string Find(const Gates& gates, const std::string& left, const std::string& right, const std::string& op)
{
for (const auto& gate : gates.gateOperation)
{
if (gate.op == op)
{
if ((gate.left == left && gate.right == right) || (gate.left == right && gate.right == left))
{
return gate.res;
}
}
}
return "";
}
OUTPUT1(input)
{
// Too low: 1972354943
Gates cpy = input;
bool somethingChanged = true;
while (somethingChanged)
{
somethingChanged = false;
for (const auto& gate : cpy.gateOperation)
{
int leftVal = GetVal(cpy, gate.left);
int rightVal = GetVal(cpy, gate.right);
if (leftVal != -1 && rightVal != -1 && GetVal(cpy, gate.res) == -1)
{
somethingChanged = true;
if (gate.op == "AND")
{
SetVal(cpy, gate.res, leftVal & rightVal);
}
else if (gate.op == "OR")
{
SetVal(cpy, gate.res, leftVal | rightVal);
}
else if (gate.op == "XOR")
{
SetVal(cpy, gate.res, leftVal ^ rightVal);
}
}
}
}
int64_t num = 0;
for (const auto& wire : cpy.gates)
{
if (wire.first[0] == 'z')
{
int pos = std::stoi(wire.first.substr(1));
num |= ((int64_t)wire.second << pos);
}
}
return num;
}
OUTPUT2(input)
{
Gates gates = input;
std::set<std::string> outputs;
for (const auto& gate : gates.gateOperation)
{
outputs.emplace(gate.res);
}
std::string carry{Find(gates, "x00", "y00", "AND")};
std::set<std::string> swapped;
for (int i = 1; i < 46; i++)
{
std::string index = IndexToStr(i);
std::string x = "x" + index;
std::string y = "y" + index;
std::string z = "z" + index;
std::string xorIn = Find(gates, x, y, "XOR");
std::string res = Find(gates, carry, xorIn, "XOR");
if (res.empty())
{
for (const auto& output : outputs)
{
std::string res = Find(gates, carry, output, "XOR");
if (res == z)
{
SwapOutputs(gates, xorIn, output, swapped);
xorIn = output;
break;
}
}
}
else if (res != z)
{
SwapOutputs(gates, res, z, swapped);
}
std::string andIn = Find(gates, x, y, "AND");
std::string andXorInCarry = Find(gates, xorIn, carry, "AND");
carry = Find(gates, andIn, andXorInCarry, "OR");
}
for (const auto& swap : swapped)
{
std::cout << swap << ",";
}
std::cout << std::endl;
// Some arbitrary solution number to have some form of verification in the code.
// Sum all letters in the swapped outputs
int64_t sum = 0;
for (const auto& s : swapped)
{
for (char c : s)
{
sum += c = 'a';
}
}
return sum;
}
}
+100
View File
@@ -0,0 +1,100 @@
#include "common/aoc.h"
namespace day25
{
struct KeysLocks
{
int height{0};
std::vector<std::vector<int>> keys;
std::vector<std::vector<int>> locks;
};
REGISTER_DAY(2024, Day25, KeysLocks, int);
REGISTER_TEST_EXAMPLE(2024, Day25, ExampleInput, 1, 3);
REGISTER_TEST(2024, Day25, Input, 1, 3663);
REGISTER_TEST_EXAMPLE(2024, Day25, ExampleInput, 2, 0);
REGISTER_TEST(2024, Day25, Input, 2, 0);
READ_INPUT(input)
{
KeysLocks keysLocks;
bool next = true;
while (next)
{
next = false;
std::string str;
std::vector<int> lockOrKey;
bool lock = true;
int height = 0;
while (std::getline(input, str))
{
next = true;
if (str.empty())
break;
if (lockOrKey.empty())
{
if (str[0] == '.')
lock = false;
lockOrKey = std::vector<int>(str.size(), 0);
}
for (int i = 0; i < str.size(); i++)
{
if (str[i] == '#')
lockOrKey[i]++;
}
height++;
}
if (keysLocks.height == 0)
keysLocks.height = height;
if (!lockOrKey.empty())
{
if (lock)
keysLocks.locks.emplace_back(lockOrKey);
else
keysLocks.keys.emplace_back(lockOrKey);
}
}
return keysLocks;
}
OUTPUT1(input)
{
int64_t sum = 0;
for (const auto& key : input.keys)
{
for (const auto& lock : input.locks)
{
bool matches = true;
for (int i = 0; i < key.size(); i++)
{
if (key[i] + lock[i] > input.height)
{
matches = false;
break;
}
}
if (matches)
sum++;
}
}
return sum;
}
OUTPUT2(input)
{
std::cout << " ___ ___" << std::endl;
std::cout << " (o o) (o o)" << std::endl;
std::cout << "( V ) MERRY CHRISTMAS! ( V )" << std::endl;
std::cout << "--m-m----------------------m-m--" << std::endl;
return 0;
}
}
+304
View File
@@ -0,0 +1,304 @@
#pragma once
#include <algorithm>
#include <bitset>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <numeric>
#include <optional>
#include <queue>
#include <regex>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>
#include "common/lib/Array2D.h"
#include "common/lib/Array3D.h"
#include "common/lib/Array4D.h"
#include "common/lib/Graph.h"
#include "common/lib/Helper.h"
#include "common/lib/Input.h"
#include "common/lib/Json.h"
#include "common/lib/Mapper.h"
#include "common/lib/Math.h"
#include "common/lib/Timer.h"
#define TERM_RED "\x1B[31m"
#define TERM_GREEN "\x1B[32m"
#define TERM_CLEAR "\033[0m"
namespace aoc
{
template <typename InputType, typename OutputType>
class Test
{
private:
std::string name;
std::string inputFile;
OutputType expectedOutput;
bool isExample = false;
int part = 0;
public:
Test(
const std::string& name, const std::string& inputFile, const OutputType& expectedOutput, bool isExample, int part)
: name{name},
inputFile{inputFile},
expectedOutput{expectedOutput},
isExample{isExample},
part{part}
{
std::transform(this->inputFile.begin(),
this->inputFile.end(),
this->inputFile.begin(),
[](char c) { return std::tolower(c); });
}
const std::string& GetInputFile() const
{
return inputFile;
}
const std::string& GetName() const
{
return name;
}
const OutputType& GetExpectedOutput() const
{
return expectedOutput;
}
bool IsExample() const
{
return isExample;
}
int GetPart() const
{
return part;
}
};
class Day
{
public:
virtual bool AocRunTestCases() = 0;
virtual const std::string& GetName() const = 0;
};
template <typename InputType, typename OutputType>
class DayT : public Day
{
protected:
std::vector<Test<InputType, OutputType>*> tests;
std::string name;
bool isExample = true;
public:
DayT(const std::string& name)
: name{name}
{
}
void AddTest(Test<InputType, OutputType>* test)
{
tests.emplace_back(test);
}
bool AocRunTestCases() override
{
for (const auto& test : tests)
{
isExample = test->IsExample();
std::ifstream inputStream{test->GetInputFile()};
if (!inputStream.is_open())
{
std::cout << TERM_RED << "Could not find input file: " << test->GetInputFile() << TERM_CLEAR << std::endl;
return false;
}
std::string testName = name + ".P" + std::to_string(test->GetPart()) + "." + test->GetName();
std::cout << TERM_GREEN << "[ RUN ] " << TERM_CLEAR << testName << std::endl;
InputType input = ReadInput(inputStream);
OutputType output = test->GetPart() == 1 ? Output1(input) : Output2(input);
if (!Check(test->GetExpectedOutput(), output, testName))
{
return false;
};
}
return true;
}
const std::string& GetName() const override
{
return name;
}
virtual InputType ReadInput(std::istream& input) = 0;
virtual OutputType Output1(const InputType& input) = 0;
virtual OutputType Output2(const InputType& input) = 0;
private:
bool Check(const OutputType& expected, const OutputType& actual, const std::string& name) const
{
if (expected == actual)
{
std::cout << TERM_GREEN << "[ OK ] " << TERM_CLEAR << " Result: " << actual << std::endl;
return true;
}
else
{
std::cout << TERM_RED << "[ ] " << TERM_CLEAR << "Unexpected value in " << name << std::endl;
std::cout << TERM_RED << "[ ] " << TERM_CLEAR << " expect: " << expected << std::endl;
std::cout << TERM_RED << "[ FAILED ] " << TERM_CLEAR << " actual: " << actual << std::endl;
return false;
}
}
};
struct ptr_compare
{
using is_transparent = void;
bool operator()(const Day* lhs, const Day* rhs) const
{
return lhs->GetName() < rhs->GetName();
};
bool operator()(const Day* lhs, const std::string& rhs) const
{
return lhs->GetName() < rhs;
};
bool operator()(const std::string& lhs, const Day* rhs) const
{
return lhs < rhs->GetName();
};
};
class Registry
{
private:
using Days = std::set<Day*, ptr_compare>;
Days days;
Day* AddDay(Day* day)
{
days.emplace(day);
return day;
}
template <typename InputType, typename OutputType>
Test<InputType, OutputType>* AddTest(std::string year, std::string day, Test<InputType, OutputType>* test)
{
std::string name = year + "." + day;
auto it = days.find(name);
if (it == days.end())
{
std::cout << TERM_RED << "Could not find Day: " << name << TERM_CLEAR << std::endl;
return test;
}
DayT<InputType, OutputType>* dayT = dynamic_cast<DayT<InputType, OutputType>*>(*it);
if (!dayT)
{
std::cout << TERM_RED << "Failed to cast day: " << name << TERM_CLEAR << std::endl;
return test;
}
dayT->AddTest(test);
return test;
}
public:
template <typename DayImpl>
static DayImpl* RegisterDay(DayImpl* day)
{
return (DayImpl*)GetInstance()->AddDay(day);
}
template <typename InputType, typename OutputType>
static Test<InputType, OutputType>* RegisterTest(std::string year,
std::string day,
Test<InputType, OutputType>* test)
{
return GetInstance()->AddTest(year, day, test);
}
static Registry* GetInstance();
static Days& GetDays()
{
return GetInstance()->days;
}
};
}
#define DAY_STR(year, day) #year "." #day
#define REGISTER_DAY(year, day, InputTypeType, OutputTypeType) \
using InputType = InputTypeType; \
using OutputType = OutputTypeType; \
class DayImpl : public aoc::DayT<InputType, OutputType> \
{ \
public: \
DayImpl() \
: DayT(DAY_STR(year, day)) \
{ \
} \
InputType ReadInput(std::istream& input) override; \
OutputType Output1(const InputType& input) override; \
OutputType Output2(const InputType& input) override; \
static DayImpl* test; \
}; \
DayImpl* DayImpl::test = aoc::Registry::RegisterDay(new DayImpl())
#define READ_INPUT(x) InputType DayImpl::ReadInput(std::istream& x)
#define OUTPUT1(x) OutputType DayImpl::Output1(const InputType& x)
#define OUTPUT2(x) OutputType DayImpl::Output2(const InputType& x)
#define TEST_NAME_EXAMPLE(year, day, name, part) y##year##day##name##part
#define TEST_NAME(year, day, name, part) y##year##day##name##part
#define _INTERNAL_REGISTER_TEST_FULL(year, day, name, part, val, testName, inputFile, isExample) \
static aoc::Test<InputType, OutputType>* testName = aoc::Registry::RegisterTest( \
#year, #day, new aoc::Test<InputType, OutputType>(#name, inputFile, val, isExample, part))
#define REGISTER_TEST_EXAMPLE(year, day, name, part, val) \
_INTERNAL_REGISTER_TEST_FULL(year, \
day, \
name, \
part, \
val, \
TEST_NAME_EXAMPLE(year, day, name, part), \
"res/" #year "/" #day "/test_input.txt", \
true)
#define REGISTER_TEST_EXAMPLE_FILE(year, day, name, part, val, inputFileName) \
_INTERNAL_REGISTER_TEST_FULL(year, \
day, \
name, \
part, \
val, \
TEST_NAME_EXAMPLE(year, day, name, part), \
"res/" #year "/" #day "/" + std::string(inputFileName), \
true)
#define REGISTER_TEST(year, day, name, part, val) \
_INTERNAL_REGISTER_TEST_FULL( \
year, day, name, part, val, TEST_NAME(year, day, name, part), "res/" #year "/" #day "/input.txt", false)
#define REGISTER_TEST_FILE(year, day, name, part, val, inputFileName) \
_INTERNAL_REGISTER_TEST_FULL(year, \
day, \
name, \
part, \
val, \
TEST_NAME(year, day, name, part), \
"res/" #year "/" #day "/" + std::string(inputFileName), \
false)
+506
View File
@@ -0,0 +1,506 @@
#pragma once
#include <cassert>
#include <iostream>
#include <vector>
struct Index2D
{
int x = -1;
int y = -1;
bool IsValid()
{
return x != -1 && y != -1;
}
operator bool()
{
return IsValid();
}
Index2D operator+(const Index2D& other) const
{
return Index2D{x + other.x, y + other.y};
}
Index2D operator-(const Index2D& other) const
{
return Index2D{x - other.x, y - other.y};
}
Index2D operator*(int scale) const
{
return Index2D{x * scale, y * scale};
}
int operator[](int dimension) const
{
assert(dimension == 0 || dimension == 1);
if (dimension == 0)
return x;
if (dimension == 1)
return y;
return 0;
}
friend std::ostream& operator<<(std::ostream& stream, const Index2D& index)
{
return stream << "(" << index.x << " " << index.y << ")";
}
friend bool operator<(const Index2D& lhs, const Index2D& rhs)
{
if (lhs.x != rhs.x)
return lhs.x < rhs.x;
return lhs.y < rhs.y;
}
friend bool operator==(const Index2D& lhs, const Index2D& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y;
}
friend bool operator!=(const Index2D& lhs, const Index2D& rhs)
{
return !operator==(lhs, rhs);
}
};
template <typename T>
struct Array2D
{
struct ConstIterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = const T;
using pointer = const T*; // or also value_type*
using reference = const T&; // or also value_type&
const Array2D<T>& array2D;
Index2D index;
ConstIterator(const Array2D<T>& array2D, const Index2D& index)
: array2D{array2D},
index{index}
{
}
reference operator*() const
{
return array2D.Get(index);
}
pointer operator->() const
{
return &array2D.Get(index);
}
// Prefix
ConstIterator& operator++()
{
index.x++;
if (index.x == array2D.width)
{
index.x = 0;
index.y++;
}
return *this;
}
// Postfix
ConstIterator operator++(int)
{
ConstIterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const ConstIterator& rhs)
{
return index == rhs.index;
}
bool operator!=(const ConstIterator& rhs)
{
return !(*this == rhs);
}
};
struct Iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*; // or also value_type*
using reference = T&; // or also value_type&
Array2D<T>& array2D;
Index2D index;
Iterator(Array2D<T>& array2D, const Index2D& index)
: array2D{array2D},
index{index}
{
}
reference operator*() const
{
return array2D.Get(index);
}
pointer operator->() const
{
return &array2D.Get(index);
}
// Prefix
Iterator& operator++()
{
index.x++;
if (index.x == array2D.width)
{
index.x = 0;
index.y++;
}
return *this;
}
// Postfix
Iterator operator++(int)
{
Iterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const Iterator& rhs)
{
return index == rhs.index;
}
bool operator!=(const Iterator& rhs)
{
return !(*this == rhs);
}
};
int width;
int height;
std::vector<T> array2D;
Array2D()
: width{0},
height{0}
{
}
Array2D(int width, int height)
: width{width},
height{height}
{
array2D.resize(width * height);
}
Array2D(int width, int height, const T& fill)
: width{width},
height{height}
{
array2D = std::vector<T>(width * height, fill);
array2D.resize(width * height);
}
Array2D(int width, int height, const std::vector<T>& data)
: width{width},
height{height},
array2D{data}
{
}
void Fill(const T& value)
{
for (int i = 0; i < width * height; i++)
{
array2D[i] = value;
}
}
int GetIndex(int x, int y) const
{
assert(IsInside(x, y));
return x + y * width;
}
int GetIndex(const Index2D& index) const
{
return GetIndex(index.x, index.y);
}
T& Get(int x, int y)
{
assert(IsInside(x, y));
return array2D[x + y * width];
}
const T& Get(int x, int y) const
{
assert(IsInside(x, y));
return array2D[x + y * width];
}
T& Get(const Index2D& index)
{
return Get(index.x, index.y);
}
const T& Get(const Index2D& index) const
{
return Get(index.x, index.y);
}
T& operator[](const Index2D& index)
{
return Get(index.x, index.y);
}
const T& operator[](const Index2D& index) const
{
return Get(index.x, index.y);
}
T& GetMirror(int x, int y)
{
// if width = 3, it will repeat x like this: 012210012210, same with height
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
return array2D[x + y * width];
}
const T& GetMirror(int x, int y) const
{
// if width = 3, it will repeat x like this: 012210012210, same with height
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
return array2D[x + y * width];
}
T& GetMod(int x, int y)
{
return array2D[((x % width + width) % width) + ((y % height + height) % height) * width];
}
const T& GetMod(int x, int y) const
{
return array2D[((x % width + width) % width) + ((y % height + height) % height) * width];
}
void Set(int x, int y, const T& val)
{
Get(x, y) = val;
}
void SetMod(int x, int y, const T& val)
{
GetMod(x, y) = val;
}
void SetMirror(int x, int y, const T& val)
{
GetMirror(x, y) = val;
}
int GetNeighbors(const T& val, int x, int y, int size = 1) const
{
int neighbors = 0;
for (int yi = y - size; yi <= y + size; yi++)
{
for (int xi = x - size; xi <= x + size; xi++)
{
if (xi < 0 || yi < 0 || xi >= width || yi >= height)
continue;
if (xi == x && yi == y)
continue;
if (Get(xi, yi) == val)
neighbors++;
}
}
return neighbors;
}
std::vector<Index2D> GetNeighbors(const Index2D& index, bool includeDiagonals) const
{
std::vector<Index2D> neighbors;
if (index.x > 0)
neighbors.emplace_back(Index2D{index.x - 1, index.y});
if (index.y > 0)
neighbors.emplace_back(Index2D{index.x, index.y - 1});
if (index.x < width - 1)
neighbors.emplace_back(Index2D{index.x + 1, index.y});
if (index.y < height - 1)
neighbors.emplace_back(Index2D{index.x, index.y + 1});
if (includeDiagonals)
{
if (index.x > 0 && index.y > 0)
neighbors.emplace_back(Index2D{index.x - 1, index.y - 1});
if (index.x > 0 && index.y < height - 1)
neighbors.emplace_back(Index2D{index.x - 1, index.y + 1});
if (index.x < width - 1 && index.y < height - 1)
neighbors.emplace_back(Index2D{index.x + 1, index.y + 1});
if (index.x < width - 1 && index.y > 0)
neighbors.emplace_back(Index2D{index.x + 1, index.y - 1});
}
return neighbors;
}
bool IsInside(int x, int y) const
{
return x >= 0 && y >= 0 && x < width && y < height;
}
bool IsInside(const Index2D& index) const
{
return IsInside(index.x, index.y);
}
int Count(const T& val) const
{
int count = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y) == val)
count++;
}
}
return count;
}
void RotateCW()
{
Array2D cpy = *this;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
array2D[height - y - 1 + x * height] = cpy.Get(x, y);
}
}
std::swap(width, height);
}
void RotateCCW()
{
Array2D cpy = *this;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
array2D[y + (width - x - 1) * height] = cpy.Get(x, y);
}
}
std::swap(width, height);
}
void Rotate180()
{
Array2D cpy = *this;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
array2D[width - x - 1 + (height - y - 1) * width] = cpy.Get(x, y);
}
}
}
void FlipVertical()
{
for (int y = 0; y < height / 2; y++)
{
for (int x = 0; x < width; x++)
{
std::swap(array2D[GetIndex(x, y)], array2D[GetIndex(x, height - y - 1)]);
}
}
}
void FlipHorizontal()
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width / 2; x++)
{
std::swap(array2D[GetIndex(x, y)], array2D[GetIndex(width - x - 1, y)]);
}
}
}
template <typename Function>
void Each(Function func) const
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
func(*this, Index2D{x, y});
}
}
}
Index2D Find(const T& value) const
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y) == value)
return Index2D{x, y};
}
}
return Index2D{};
}
void Increment(Index2D& index) const
{
index.x++;
if (index.x != width)
return;
index.x = 0;
index.y++;
}
typename Array2D<T>::Iterator begin()
{
return Iterator{*this, Index2D{0, 0}};
}
typename Array2D<T>::Iterator end()
{
return Iterator{*this, Index2D{0, height}};
}
typename Array2D<T>::ConstIterator begin() const
{
return ConstIterator{*this, Index2D{0, 0}};
}
typename Array2D<T>::ConstIterator end() const
{
return ConstIterator{*this, Index2D{0, height}};
}
friend std::ostream& operator<<(std::ostream& os, const Array2D& array2D)
{
for (int y = 0; y < array2D.height; y++)
{
for (int x = 0; x < array2D.width; x++)
{
os << array2D.Get(x, y);
}
os << std::endl;
}
return std::cout;
}
};
+424
View File
@@ -0,0 +1,424 @@
#pragma once
#include <iostream>
#include <vector>
struct Index3D
{
int x = -1;
int y = -1;
int z = -1;
bool IsValid()
{
return x != -1 && y != -1 && z != -1;
}
operator bool()
{
return IsValid();
}
Index3D operator+(const Index3D& other) const
{
return Index3D{x + other.x, y + other.y, z + other.z};
}
Index3D operator-(const Index3D& other) const
{
return Index3D{x - other.x, y - other.y, z - other.z};
}
friend std::ostream& operator<<(std::ostream& stream, const Index3D& index)
{
return stream << "(" << index.x << " " << index.y << " " << index.z << ")";
}
friend bool operator<(const Index3D& lhs, const Index3D& rhs)
{
if (lhs.x != rhs.x)
return lhs.x < rhs.x;
if (lhs.y != rhs.y)
return lhs.y < rhs.y;
return lhs.z < rhs.z;
}
friend bool operator==(const Index3D& lhs, const Index3D& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
}
friend bool operator!=(const Index3D& lhs, const Index3D& rhs)
{
return !operator==(lhs, rhs);
}
};
template <typename T>
struct Array3D
{
struct ConstIterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = const T;
using pointer = const T*; // or also value_type*
using reference = const T&; // or also value_type&
const Array3D<T>& array3D;
Index3D index;
ConstIterator(const Array3D<T>& array3D, const Index3D& index)
: array3D{array3D},
index{index}
{
}
reference operator*() const
{
return array3D.Get(index);
}
pointer operator->() const
{
return &array3D.Get(index);
}
// Prefix
ConstIterator& operator++()
{
index.x++;
if (index.x == array3D.width)
{
index.x = 0;
index.y++;
if (index.y == array3D.height)
{
index.y = 0;
index.z++;
}
}
return *this;
}
// Postfix
ConstIterator operator++(int)
{
ConstIterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const ConstIterator& rhs)
{
return index == rhs.index;
}
bool operator!=(const ConstIterator& rhs)
{
return !(*this == rhs);
}
};
struct Iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*; // or also value_type*
using reference = T&; // or also value_type&
Array3D<T>& array3D;
Index3D index;
Iterator(Array3D<T>& array3D, const Index3D& index)
: array3D{array3D},
index{index}
{
}
reference operator*() const
{
return array3D.Get(index);
}
pointer operator->() const
{
return &array3D.Get(index);
}
// Prefix
Iterator& operator++()
{
index.x++;
if (index.x == array3D.width)
{
index.x = 0;
index.y++;
if (index.y == array3D.height)
{
index.y = 0;
index.z++;
}
}
}
// Postfix
Iterator operator++(int)
{
Iterator tmp = *this;
++(*this);
return tmp;
}
bool operator==(const Iterator& rhs)
{
return index == rhs.index;
}
bool operator!=(const Iterator& rhs)
{
return !(*this == rhs);
}
};
int width;
int height;
int length;
std::vector<T> array3D;
Array3D()
: width{0},
height{0},
length{0}
{
}
Array3D(int width, int height, int length)
: width{width},
height{height},
length{length}
{
array3D.resize(width * height * length);
}
Array3D(int width, int height, int length, const std::vector<T>& data)
: width{width},
height{height},
length{length},
array3D{data}
{
}
void Fill(const T& value)
{
for (int i = 0; i < width * height * length; i++)
{
array3D[i] = value;
}
}
int GetIndex(int x, int y, int z) const
{
return x + y * width + z * width * height;
}
T& Get(int x, int y, int z)
{
return array3D[x + y * width + z * width * height];
}
const T& Get(int x, int y, int z) const
{
return array3D[x + y * width + z * width * height];
}
T& Get(const Index3D& index)
{
return Get(index.x, index.y, index.z);
}
const T& Get(const Index3D& index) const
{
return Get(index.x, index.y, index.z);
}
T& operator[](const Index3D& index)
{
return Get(index.x, index.y, index.z);
}
const T& operator[](const Index3D& index) const
{
return Get(index.x, index.y, index.z);
}
T& GetMirror(int x, int y, int z)
{
// if width = 3, it will repeat x like this: 012210012210, same with height and length
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
z = (z / length) % 2 == 0 ? z % length : length - (z % length) - 1;
return array3D[x + y * width + z * width * height];
}
const T& GetMirror(int x, int y, int z) const
{
// if width = 3, it will repeat x like this: 012210012210, same with height and length
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
z = (z / length) % 2 == 0 ? z % length : length - (z % length) - 1;
return array3D[x + y * width + z * width * height];
}
T& GetMod(int x, int y, int z)
{
return array3D[((x % width + width) % width) + ((y % height + height) % height) * width +
((z % length + length) % length) * width * height];
}
const T& GetMod(int x, int y, int z) const
{
return array3D[((x % width + width) % width) + ((y % height + height) % height) * width +
((z % length + length) % length) * width * height];
}
void Set(int x, int y, int z, const T& val)
{
Get(x, y, z) = val;
}
void SetMod(int x, int y, int z, const T& val)
{
GetMod(x, y, z) = val;
}
void SetMirror(int x, int y, int z, const T& val)
{
GetMirror(x, y, z) = val;
}
int GetNeighbors(const T& val, int x, int y, int z, int size = 1) const
{
int neighbors = 0;
for (int zi = z - size; zi <= z + size; zi++)
{
for (int yi = y - size; yi <= y + size; yi++)
{
for (int xi = x - size; xi <= x + size; xi++)
{
if (xi < 0 || yi < 0 || zi < 0 || xi >= width || yi >= height || zi >= length)
continue;
if (xi == x && yi == y && zi == z)
continue;
if (Get(xi, yi, zi) == val)
neighbors++;
}
}
}
return neighbors;
}
bool IsInside(const Index3D& index) const
{
return index.x >= 0 && index.y >= 0 && index.z >= 0 && index.x < width && index.y < height && index.z < length;
}
int Count(const T& val)
{
int count = 0;
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y, z) == val)
count++;
}
}
}
return count;
}
template <typename Function>
void Each(Function func) const
{
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
func(*this, Index3D{x, y, z});
}
}
}
}
Index3D Find(const T& value)
{
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y, z) == value)
return Index3D{x, y, z};
}
}
}
return Index3D{};
}
void Increment(Index3D& index)
{
index.x++;
if (index.x != width)
return;
index.x = 0;
index.y++;
if (index.y != height)
return;
index.y = 0;
index.z++;
}
typename Array3D<T>::Iterator begin()
{
return Iterator{*this, Index3D{0, 0, 0}};
}
typename Array3D<T>::Iterator end()
{
return Iterator{*this, Index3D{0, 0, length}};
}
typename Array3D<T>::ConstIterator begin() const
{
return ConstIterator{*this, Index3D{0, 0, 0}};
}
typename Array3D<T>::ConstIterator end() const
{
return ConstIterator{*this, Index3D{0, 0, length}};
}
friend std::ostream& operator<<(std::ostream& os, const Array3D& array3D)
{
for (int z = 0; z < array3D.length; z++)
{
std::cout << "z=" << z << std::endl;
for (int y = 0; y < array3D.height; y++)
{
for (int x = 0; x < array3D.width; x++)
{
std::cout << array3D.Get(x, y, z);
}
std::cout << std::endl;
}
}
return std::cout;
}
};
+320
View File
@@ -0,0 +1,320 @@
#pragma once
#include <iostream>
#include <vector>
struct Index4D
{
int x = -1;
int y = -1;
int z = -1;
int w = -1;
bool IsValid()
{
return x != -1 && y != -1 && z != -1 && w != -1;
}
operator bool()
{
return IsValid();
}
Index4D operator+(const Index4D& other) const
{
return Index4D{x + other.x, y + other.y, z + other.z, w + other.w};
}
Index4D operator-(const Index4D& other) const
{
return Index4D{x - other.x, y - other.y, z - other.z, w - other.w};
}
friend std::ostream& operator<<(std::ostream& stream, const Index4D& index)
{
return stream << "(" << index.x << " " << index.y << " " << index.z << " " << index.w << ")";
}
friend bool operator<(const Index4D& lhs, const Index4D& rhs)
{
if (lhs.x != rhs.x)
return lhs.x < rhs.x;
if (lhs.y != rhs.y)
return lhs.y < rhs.y;
if (lhs.z != rhs.z)
return lhs.z < rhs.z;
return lhs.w < rhs.w;
}
friend bool operator==(const Index4D& lhs, const Index4D& rhs)
{
return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z && lhs.w == rhs.w;
}
friend bool operator!=(const Index4D& lhs, const Index4D& rhs)
{
return !operator==(lhs, rhs);
}
};
template <typename T>
struct Array4D
{
int width;
int height;
int length;
int time;
std::vector<T> array4D;
Array4D()
: width{0},
height{0},
length{0},
time{0}
{
}
Array4D(int width, int height, int length, int time)
: width{width},
height{height},
length{length},
time{time}
{
array4D.resize(width * height * length * time);
}
Array4D(int width, int height, int length, int time, const std::vector<T>& data)
: width{width},
height{height},
length{length},
time{time},
array4D{data}
{
}
void Fill(const T& value)
{
for (int i = 0; i < width * height * length * time; i++)
{
array4D[i] = value;
}
}
int GetIndex(int x, int y, int z, int w) const
{
return x + y * width + z * width * height + w * width * height * length;
}
T& Get(int x, int y, int z, int w)
{
return array4D[x + y * width + z * width * height + w * width * height * length];
}
const T& Get(int x, int y, int z, int w) const
{
return array4D[x + y * width + z * width * height + w * width * height * length];
}
T& operator[](const Index4D& index)
{
return Get(index.x, index.y, index.z, index.w);
}
const T& operator[](const Index4D& index) const
{
return Get(index.x, index.y, index.z, index.w);
}
T& GetMirror(int x, int y, int z, int w)
{
// if width = 3, it will repeat x like this: 012210012210, same with height and length
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
z = (z / length) % 2 == 0 ? z % length : length - (z % length) - 1;
w = (w / time) % 2 == 0 ? w % time : time - (z % time) - 1;
return array4D[x + y * width + z * width * height + w * width * height * length];
}
const T& GetMirror(int x, int y, int z, int w) const
{
// if width = 3, it will repeat x like this: 012210012210, same with height and length
x = (x / width) % 2 == 0 ? x % width : width - (x % width) - 1;
y = (y / height) % 2 == 0 ? y % height : height - (y % height) - 1;
z = (z / length) % 2 == 0 ? z % length : length - (z % length) - 1;
w = (w / time) % 2 == 0 ? w % time : time - (z % time) - 1;
return array4D[x + y * width + z * width * height + w * width * height * length];
}
T& GetMod(int x, int y, int z, int w)
{
return array4D[((x % width + width) % width) + ((y % height + height) % height) * width +
((z % length + length) % length) * width * height +
((w % time + time) % time) * width * height * length];
}
const T& GetMod(int x, int y, int z, int w) const
{
return array4D[((x % width + width) % width) + ((y % height + height) % height) * width +
((z % length + length) % length) * width * height +
((w % time + time) % time) * width * height * length];
}
void Set(int x, int y, int z, int w, const T& val)
{
Get(x, y, z, w) = val;
}
void SetMod(int x, int y, int z, int w, const T& val)
{
GetMod(x, y, z, w) = val;
}
void SetMirror(int x, int y, int z, int w, const T& val)
{
GetMirror(x, y, z, w) = val;
}
int GetNeighbors(const T& val, int x, int y, int z, int w, int size = 1) const
{
int neighbors = 0;
for (int wi = w - size; wi <= w + size; wi++)
{
for (int zi = z - size; zi <= z + size; zi++)
{
for (int yi = y - size; yi <= y + size; yi++)
{
for (int xi = x - size; xi <= x + size; xi++)
{
if (xi < 0 || yi < 0 || zi < 0 || wi < 0 || xi >= width || yi >= height || zi >= length || wi >= time)
continue;
if (xi == x && yi == y && zi == z && wi == w)
continue;
if (Get(xi, yi, zi, wi) == val)
neighbors++;
}
}
}
}
return neighbors;
}
bool IsInside(const Index4D& index) const
{
return index.x >= 0 && index.y >= 0 && index.z >= 0 && index.w >= 0 && index.x < width && index.y < height &&
index.z < length && index.w < time;
}
int Count(const T& val)
{
int count = 0;
for (int w = 0; w < length; w++)
{
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y, z, w) == val)
count++;
}
}
}
}
return count;
}
template <typename Function>
void Each(T func) const
{
for (int w = 0; w < length; w++)
{
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
func(*this, Index4D{x, y, z, w});
}
}
}
}
}
Index4D Find(const T& value)
{
for (int w = 0; w < time; w++)
{
for (int z = 0; z < length; z++)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
if (Get(x, y, z, w) == value)
return Index4D{x, y, z, w};
}
}
}
}
return Index4D{};
}
void Increment(Index4D& index)
{
index.x++;
if (index.x != width)
return;
index.x = 0;
index.y++;
if (index.y != height)
return;
index.y = 0;
index.z++;
if (index.z != length)
return;
index.z = 0;
index.w++;
}
typename std::vector<T>::iterator begin()
{
return array4D.begin();
}
typename std::vector<T>::iterator end()
{
return array4D.end();
}
typename std::vector<T>::const_iterator begin() const
{
return array4D.cbegin();
}
typename std::vector<T>::const_iterator end() const
{
return array4D.cend();
}
friend std::ostream& operator<<(std::ostream& os, const Array4D& array4D)
{
for (int w = 0; w < array4D.time; w++)
{
for (int z = 0; z < array4D.length; z++)
{
std::cout << "w=" << w << " z=" << z << std::endl;
for (int y = 0; y < array4D.height; y++)
{
for (int x = 0; x < array4D.width; x++)
{
std::cout << array4D.Get(x, y, z, w);
}
std::cout << std::endl;
}
}
}
return std::cout;
}
};
+153
View File
@@ -0,0 +1,153 @@
#pragma once
#include <map>
#include <set>
template <typename Key, typename Value>
class Graph
{
std::map<Key, std::map<Key, Value>> graph;
// Data based on the graph
std::set<Key> nodes;
public:
Graph() = default;
void AddNode(const Key& from)
{
graph.emplace(from);
}
void AddOneWayEdge(const Key& from, const Key& to, const Value& value)
{
graph[from][to] = value;
nodes.emplace(from);
}
void AddTwoWayEdge(const Key& from, const Key& to, const Value& value)
{
graph[from][to] = value;
graph[to][from] = value;
nodes.emplace(from);
nodes.emplace(to);
}
Value DFSLong(const Key& from, const Key& to)
{
std::set<Key> visited;
return DFSLong(to, visited, from, Value{});
}
Value DFS(const Key& from, const Key& to)
{
std::set<Key> visited;
return DFS(to, visited, from, Value{});
}
Value Dijkstras(const Key& from, const Key& to) const
{
std::map<Key, Value> visited;
std::multimap<Value, Key> openSet;
openSet.emplace(Value{}, from);
while (!openSet.empty())
{
const auto& [cost, node] = *openSet.begin();
if (node == to)
return cost;
for (auto& [next, nextCost] : graph.find(node)->second)
{
auto it = visited.find(next);
if (it == visited.end() || it->second > cost + nextCost)
{
openSet.emplace(cost + nextCost, next);
visited[next] = cost + nextCost;
}
}
openSet.erase(openSet.begin());
}
return Value{};
}
std::pair<Value, std::vector<Key>> DijkstrasWithPath(const Key& from, const Key& to) const
{
std::map<Key, Value> visited;
std::multimap<Value, std::vector<Key>> openSet;
openSet.emplace(Value{}, std::vector<Key>{from});
while (!openSet.empty())
{
const auto& [cost, path] = *openSet.begin();
if (path.back() == to)
return {cost, path};
for (auto& [next, nextCost] : graph.find(path.back())->second)
{
auto it = visited.find(next);
if (it == visited.end() || it->second > cost + nextCost)
{
std::vector<Key> newPath = path;
newPath.emplace_back(next);
openSet.emplace(cost + nextCost, newPath);
visited[next] = cost + nextCost;
}
}
openSet.erase(openSet.begin());
}
return {Value{}, {}};
}
const Value& GetEdge(const Key& from, const Key& to) const
{
// Verification that the nodes exists in the graph?
return graph.find(from)->second.find(to)->second;
}
const std::map<Key, Value>& GetPaths(const Key& from) const
{
return graph.find(from)->second;
}
const std::set<Key>& GetNodes() const
{
return nodes;
}
private:
Value DFSLong(const Key& to, std::set<Key>& visited, const Key& current, const Value& cost)
{
if (visited.count(current))
return Value{};
if (current == to)
return cost;
visited.emplace(current);
Value max{};
for (auto& [next, nextCost] : graph.find(current)->second)
{
max = std::max(max, DFSLong(to, visited, next, cost + nextCost));
}
visited.erase(current);
return max;
}
Value DFS(const Key& to, std::set<Key>& visited, const Key& current, const Value& cost)
{
if (visited.count(current))
return Value{};
if (current == to)
return cost;
visited.emplace(current);
for (auto& [next, nextCost] : graph.find(current)->second)
{
Value val = DFS(to, visited, next, cost + nextCost);
if (val != Value{})
return val;
}
visited.erase(current);
return Value{};
}
};
+643
View File
@@ -0,0 +1,643 @@
#pragma once
#include <algorithm>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <regex>
#include <set>
#include <vector>
#include "Array2D.h"
#include "Graph.h"
#define BIT(x) (1 << (x))
struct Helper
{
template <typename T, typename S>
static void RemoveIf(T& t, S func)
{
t.erase(std::remove_if(t.begin(), t.end(), func), t.end());
}
static bool StartsWith(const std::string_view& str, const std::string& prefix)
{
return str.substr(0, prefix.size()) == prefix;
}
static bool EndsWith(const std::string_view& str, const std::string& prefix)
{
return str.substr(str.size() - prefix.size(), prefix.size()) == prefix;
}
static bool IsDigit(char c)
{
return c >= '0' && c <= '9';
}
// Converts a binary string to an int32_t ie "10100111001" to 1337
static int BinStrToInt(const std::string& str)
{
if (str.size() >= 32)
{
std::cout << "BinStrToInt: Too big string, use BinStrToInt64 instead" << std::endl;
return 0;
}
int val = 0;
for (size_t i = 0; i < str.size(); i++)
{
if (str[i] == '1')
val |= (1 << (str.size() - i - 1));
}
return val;
}
// Converts a binary string to an int64_t ie "10100111001" to 1337
static int64_t BinStrToInt64(const std::string& str)
{
if (str.size() >= 64)
{
std::cout << "BinStrToInt64: Too big string" << std::endl;
return 0;
}
int64_t val = 0;
for (size_t i = 0; i < str.size(); i++)
{
if (str[i] == '1')
val |= (1ll << (str.size() - i - 1));
}
return val;
}
template <typename T, typename S>
static T Sum(const S& container)
{
return std::accumulate(container.begin(), container.end(), (T)0, std::plus<T>());
}
template <typename T, typename S, typename Container>
static T Sum(const Container& container, S func)
{
return std::accumulate(container.begin(), container.end(), (T)0, func);
}
template <typename T, typename S>
static T Product(const S& container)
{
return std::accumulate(container.begin(), container.end(), (T)1, std::multiplies<T>());
}
template <typename T, typename S, typename Container>
static T Product(const Container& container, S func)
{
return std::accumulate(container.begin(), container.end(), (T)1, func);
}
template <typename Container>
static typename Container::value_type Min(const Container& container)
{
if (container.size() == 0)
{
std::cout << "Min: Container empty" << std::endl;
return typename Container::value_type{};
}
typename Container::value_type min = *container.begin();
for (auto&& element : container)
{
min = std::min(min, element);
}
return min;
}
template <typename Container>
static typename Container::value_type Max(const Container& container)
{
if (container.size() == 0)
{
std::cout << "Min: Container empty" << std::endl;
return typename Container::value_type{};
}
typename Container::value_type max = *container.begin();
for (auto&& element : container)
{
max = std::max(max, element);
}
return max;
}
template <typename T>
static T Sign(const T& t)
{
return t > 0 ? 1 : t < 0 ? -1 : 0;
}
template <typename T>
static std::set<T> GetUnion(const std::vector<std::set<T>>& list)
{
std::set<char> uniq;
for (auto l : list)
{
for (auto t : l)
{
uniq.emplace(t);
}
}
return uniq;
}
template <typename T>
static std::set<T> GetIntersection(const std::vector<std::set<T>>& list)
{
std::set<char> intersection = list[0];
for (size_t i = 1; i < list.size(); i++)
{
for (auto it = intersection.begin(); it != intersection.end();)
{
if (list[i].find(*it) == list[i].end())
it = intersection.erase(it);
else
it++;
}
}
return intersection;
}
template <typename T>
static std::set<T> GetIntersection(const std::set<T>& s1, const std::set<T>& s2)
{
std::set<T> intersection = s1;
for (auto it = intersection.begin(); it != intersection.end();)
{
if (s2.find(*it) == s2.end())
it = intersection.erase(it);
else
it++;
}
return intersection;
}
template <typename T>
static bool IsSubset(const std::set<T>& smallSet, const std::set<T>& bigSet)
{
if (bigSet.size() < smallSet.size())
return false;
for (auto&& t : smallSet)
{
if (bigSet.find(t) == bigSet.end())
return false;
}
return true;
}
// Input - Puzzle input
// State - initial state of the path finder
// Heuristic - int(Input, State)
// Given a state, what is the minimum cost to get to the goal
// state
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost
// to get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Heuristic, typename Branch, typename Goal>
static int AStar(const Input& input, const std::vector<State>& initial, Heuristic heuristic, Branch branch, Goal goal)
{
std::map<State, int> visited;
std::multimap<int, std::pair<int, State>> open;
for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair<int, State>{0, initial[i]});
while (!open.empty())
{
auto it = open.begin();
if (goal(input, it->second.second))
{
return it->second.first;
}
int stateCost = it->second.first;
for (auto& newState : branch(input, it->second.second))
{
auto it = visited.find(newState.second);
int cost = stateCost + newState.first;
if (it == visited.end() || it->second > cost)
{
open.emplace(heuristic(input, newState.second) + cost, std::pair<int, State>{cost, newState.second});
visited.emplace(newState.second, cost);
}
}
open.erase(open.begin());
}
return 0;
}
// Input - Puzzle input
// State - initial state of the path finder
// Heuristic - int(Input, State)
// Given a state, what is the minimum cost to get to the goal
// state
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost
// to get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Heuristic, typename Branch, typename Goal>
static std::pair<int, std::vector<State>> AStarPath(
const Input& input, const std::vector<State>& initial, Heuristic heuristic, Branch branch, Goal goal)
{
std::map<State, int> visited;
std::multimap<int, std::pair<int, std::vector<State>>> open;
for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair<int, std::vector<State>>{0, {initial[i]}});
while (!open.empty())
{
const auto& [heuristicVal, state] = *open.begin();
const auto& [cost, path] = state;
if (goal(input, path.back()))
return {cost, path};
for (const auto& [stateCost, newState] : branch(input, path.back()))
{
auto it = visited.find(newState);
int newCost = cost + stateCost;
if (it == visited.end() || it->second > cost)
{
auto newPath = path;
newPath.emplace_back(newState);
open.emplace(heuristic(input, newState) + newCost, std::pair<int, std::vector<State>>{newCost, newPath});
visited.emplace(newState, newCost);
}
}
open.erase(open.begin());
}
return {0, {}};
}
// Input - Puzzle input
// State - initial state of the path finder
// Heuristic - int(Input, State)
// Given a state, what is the minimum cost to get to the goal
// state
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost
// to get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Heuristic, typename Branch, typename Goal>
static std::pair<int, std::vector<State>> AStarPath(
const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal)
{
return AStarPath(input, std::vector<State>{initial}, heuristic, branch, goal);
}
// Input - Puzzle input
// State - State of the path finder
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost to
// get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Branch, typename Goal>
static std::pair<int, std::vector<State>> DijkstrasPath(const Input& input,
const std::vector<State>& initial,
Branch branch,
Goal goal)
{
return AStarPath(input, initial, [](const Input&, const State&) { return 0; }, branch, goal);
}
// Input - Puzzle input
// State - State of the path finder
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost to
// get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Branch, typename Goal>
static std::pair<int, std::vector<State>> DijkstrasPath(const Input& input,
const State& initial,
Branch branch,
Goal goal)
{
return AStarPath(input, std::vector<State>{initial}, [](const Input&, const State&) { return 0; }, branch, goal);
}
// Input - Puzzle input
// State - State of the path finder
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost to
// get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Branch, typename Goal>
static std::set<State> DijkstrasAllVisited(const Input& input, const State& initial, Branch branch, Goal goal)
{
std::set<State> totalVisited;
int bestCost = std::numeric_limits<int>::max();
std::map<State, int> visited;
std::multimap<int, std::vector<State>> open;
open.emplace(0, std::vector{initial});
while (!open.empty())
{
const auto& [cost, path] = *open.begin();
if (cost > bestCost)
{
open.erase(open.begin());
continue;
}
if (goal(input, path.back()))
{
bestCost = cost;
for (const auto& state : path)
{
totalVisited.emplace(state);
}
open.erase(open.begin());
continue;
}
for (const auto& [newStateCost, newState] : branch(input, path.back()))
{
int newCost = cost + newStateCost;
auto it = visited.find(newState);
if (it == visited.end() || it->second >= newCost)
{
auto cpy = path;
cpy.emplace_back(newState);
open.emplace(newCost, cpy);
visited.emplace(newState, newCost);
}
}
open.erase(open.begin());
}
return totalVisited;
}
// Input - Puzzle input
// State - initial state of the path finder
// Heuristic - int(Input, State)
// Given a state, what is the minimum cost to get to the goal
// state
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost
// to get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Heuristic, typename Branch, typename Goal>
static int AStar(const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal)
{
return AStar(input, std::vector<State>{initial}, heuristic, branch, goal);
}
// Input - Puzzle input
// State - State of the path finder
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost to
// get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Branch, typename Goal>
static int Dijkstras(const Input& input, const std::vector<State>& initial, Branch branch, Goal goal)
{
return AStar(input, initial, [](const Input&, const State&) { return 0; }, branch, goal);
}
// Input - Puzzle input
// State - State of the path finder
// Branch - std::vector<std::pair<int, State>>(Input, State);
// Given a state, what are the possible next states and the cost to
// get to it
// Goal - bool(Input, State)
// Given a state, are we in the goal state
template <typename Input, typename State, typename Branch, typename Goal>
static int Dijkstras(const Input& input, const State& initial, Branch branch, Goal goal)
{
return AStar(input, std::vector<State>{initial}, [](const Input&, const State&) { return 0; }, branch, goal);
}
template <typename Container>
static std::ostream& Print(std::ostream& stream, const Container& container)
{
stream << '[';
int i = 0;
for (auto it = container.begin(); it != container.end(); ++it)
{
if (i != 0)
stream << ", ";
stream << *it;
i++;
}
stream << ']';
return stream;
}
static std::string Repeat(const std::string& str, int count)
{
std::string s;
for (int i = 0; i < count; i++)
{
s += str;
}
return s;
}
template <typename T>
static std::vector<T> Repeat(const std::vector<T>& vec, int count)
{
std::vector<T> ret;
for (int i = 0; i < count; i++)
{
for (const auto& t : vec)
{
ret.emplace_back(t);
}
}
return ret;
}
static std::vector<std::string> GetAllRegexMatches(const std::string& str, const std::string& regex)
{
std::vector<std::string> matches;
std::regex reg{regex};
auto it = std::sregex_iterator(str.begin(), str.end(), reg);
auto end = std::sregex_iterator();
for (; it != end; it++)
{
matches.emplace_back(it->str());
}
return matches;
}
static std::vector<Index2D> GetNeighborDirections()
{
return std::vector<Index2D>{Index2D{1, 0}, Index2D{0, 1}, Index2D{-1, 0}, Index2D{0, -1}};
}
static Index2D GetDirection(char c)
{
if (c == 'v' || c == 'V')
return Index2D{0, 1};
if (c == '^')
return Index2D{0, -1};
if (c == '>')
return Index2D{1, 0};
if (c == '<')
return Index2D{-1, 0};
std::cerr << "GetDirection: Invalid char: " << c << std::endl;
return Index2D{-1, -1};
}
static int64_t ChineseRemainderTheoremTwo(int64_t mod1, int64_t mod2, int64_t res1, int64_t res2)
{
int64_t i = res1;
while (true)
{
if (i % mod2 == res2)
return i;
i += mod1;
}
return 0;
}
static int64_t ChineseRemainderTheorem(const std::vector<int64_t>& mods, const std::vector<int64_t>& results)
{
int64_t currentMod = mods.front();
int64_t currentRes = results.front();
for (int i = 1; i < mods.size(); i++)
{
int64_t result = ChineseRemainderTheoremTwo(currentMod, mods[i], currentRes, results[i]);
currentRes = result;
currentMod = currentMod * mods[i];
}
return currentRes;
}
// Input - Puzzle input
// Function - bool(int64_t);
// Given index, return whether the index should increase next
// iteration
//
// Returns the last index where Function returns true, or -1 if it never
// returns true
template <typename Input, typename Function>
static int64_t BinarySearch(const Input& input, int64_t min, int64_t max, Function func)
{
if (min >= max)
{
if (func(input, min))
return min;
else
return min - 1;
}
int i = min + (max - min) / 2;
if (func(input, i))
return BinarySearch(input, i + 1, max, func);
else
return BinarySearch(input, min, i - 1, func);
}
static int64_t ManhattanDistance(const Index2D& from, const Index2D& to)
{
return std::abs(from.x - to.x) + std::abs(from.y - to.y);
}
private:
template <typename Key, typename Value, typename Compare, typename Eval>
static int TSP(const Graph<Key, Value>& graph,
const std::set<Key>& nodesLeft,
const std::vector<Key>& order,
Compare compare,
Eval eval)
{
if (nodesLeft.empty())
{
return eval(graph, order);
}
bool hasSet = false;
Value tsp;
for (auto& node : nodesLeft)
{
std::set<Key> newNodesLeft = nodesLeft;
newNodesLeft.erase(node);
std::vector<Key> newOrder = order;
newOrder.emplace_back(node);
if (hasSet)
tsp = compare(tsp, TSP(graph, newNodesLeft, newOrder, compare, eval));
else
tsp = TSP(graph, newNodesLeft, newOrder, compare, eval);
hasSet = true;
}
return tsp;
}
public:
// Key - Node type of the graph
// Value - Edge type of the graph
// Compare - Value(Value, Value)
// Given two solutions, which one is better
// Eval - int(const Graph&, const std::vector<Key>&)
// Given the graph and order of the TSP, what is the total cost
template <typename Key, typename Value, typename Compare, typename Eval>
static int TSP(const Graph<Key, Value>& graph, const Key& start, Compare compare, Eval eval)
{
std::set<Key> nodes = graph.GetNodes();
nodes.erase(nodes.find(start));
return TSP(graph, nodes, std::vector<Key>{start}, compare, eval);
}
// Key - Node type of the graph
// Value - Edge type of the graph
// Compare - Value(Value, Value)
// Given two solutions, which one is better
// Eval - int(const Graph&, const std::vector<Key>&)
// Given the graph and order of the TSP, what is the total cost
template <typename Key, typename Value, typename Compare, typename Eval>
static int TSP(const Graph<Key, Value>& graph, Compare compare, Eval eval)
{
return TSP(graph, graph.GetNodes(), std::vector<Key>{}, compare, eval);
}
};
template <typename T>
static std::ostream& operator<<(std::ostream& stream, const std::vector<T>& container)
{
return Helper::Print(stream, container);
}
template <typename T>
static std::ostream& operator<<(std::ostream& stream, const std::set<T>& container)
{
return Helper::Print(stream, container);
}
template <typename T, typename S>
static std::ostream& operator<<(std::ostream& stream, const std::map<T, S>& container)
{
stream << "{";
int i = 0;
for (const auto& [t, s] : container)
{
if (i != 0)
stream << ", ";
stream << t << " => " << s;
i++;
}
stream << "}";
return stream;
}
template <typename T, typename S>
static std::ostream& operator<<(std::ostream& stream, const std::pair<T, S>& pair)
{
return stream << "(" << pair.first << ", " << pair.second << ")";
}
+102
View File
@@ -0,0 +1,102 @@
#pragma once
#include <stdint.h>
#include <iostream>
#include <string>
#include "Array2D.h"
// Makes it possible to parse strings directly without having to put it in a variable
// Ie std::cin >> "number: " >> number;
static std::istream& operator>>(std::istream& stream, char const* pattern)
{
char c;
while (stream.peek() == ' ') stream.get(c);
while (*pattern != '\0' && stream && *pattern == stream.peek() && stream.get(c)) ++pattern;
if (*pattern != '\0')
stream.setstate(std::ios::failbit);
return stream;
}
struct Input
{
static Array2D<char> ReadArray2D(std::istream& input)
{
std::vector<char> data;
int width = 0;
int height = 0;
std::string str;
while (getline(input, str))
{
if (str.empty())
break;
width = (int)str.length();
height++;
for (auto c : str) data.emplace_back(c);
}
return Array2D<char>(width, height, data);
}
static Array2D<int> ReadDigitsAsArray2D(std::istream& input)
{
std::vector<int> data;
int width = 0;
int height = 0;
std::string str;
while (getline(input, str))
{
width = (int)str.length();
height++;
for (auto c : str) data.emplace_back(c - '0');
}
return Array2D<int>(width, height, data);
}
static std::vector<int> ReadInts(std::istream& input)
{
return ReadInts(input, '\n');
}
static std::vector<int64_t> ReadInt64s(std::istream& input)
{
return ReadInt64s(input, '\n');
}
static std::vector<int> ReadInts(std::istream& input, char delimiter)
{
std::vector<int> ints;
std::string str;
while (getline(input, str, delimiter)) ints.emplace_back(std::atoi(str.c_str()));
return ints;
}
static std::vector<int64_t> ReadInt64s(std::istream& input, char delimiter)
{
std::vector<int64_t> ints;
std::string str;
while (getline(input, str, delimiter)) ints.emplace_back(std::atoll(str.c_str()));
return ints;
}
static std::vector<std::string> ReadLines(std::istream& input)
{
std::vector<std::string> strings;
std::string str;
while (getline(input, str)) strings.emplace_back(str);
return strings;
}
static std::string Read(std::istream& input)
{
std::string result;
std::string str;
while (getline(input, str)) result += str + '\n';
if (!result.empty())
result.pop_back();
return result;
}
};
+269
View File
@@ -0,0 +1,269 @@
#include "Json.h"
#include "JsonUtil.h"
#define READ_WHITESPACE() \
if (!JsonUtil::ReadWhiteSpace(str, pos)) \
{ \
std::cout << "EOF" << std::endl; \
return false; \
}
#define IF_READ_TOKEN(c) \
if (!JsonUtil::ReadWhiteSpace(str, pos)) \
{ \
std::cout << "EOF at reading token: " << c << std::endl; \
return false; \
} \
if (CheckAndIncrement(str, pos, c))
#define IF_NOT_READ_TOKEN(c) \
if (!JsonUtil::ReadWhiteSpace(str, pos)) \
{ \
std::cout << "EOF at reading token: " << c << std::endl; \
return false; \
} \
if (!CheckAndIncrement(str, pos, c))
bool CheckAndIncrement(const std::string& str, uint64_t& pos, char c)
{
bool correct = str[pos] == c;
if (correct)
pos++;
return correct;
}
void JsonObject::Add(const std::string& key, const Json& value)
{
values.emplace(key, value);
}
Json& JsonObject::Reserve(const std::string& key)
{
return values.emplace(key, Nullptr{}).first->second;
}
void JsonObject::Remove(const std::string& key)
{
values.erase(values.find(key));
}
void JsonObject::Filter(const std::set<std::string>& set)
{
for (auto it = values.begin(); it != values.end();)
{
if (set.find(it->first) != set.end())
it++;
else
it = values.erase(it);
}
}
bool JsonObject::Has(const std::string& key) const
{
return values.find(key) != values.end();
}
const Json& JsonObject::Get(const std::string& key) const
{
auto it = values.find(key);
static Json null{Nullptr{}};
if (it == values.end())
return null;
return it->second;
}
Json& JsonObject::Get(const std::string& key)
{
auto it = values.find(key);
static Json null{Nullptr{}};
if (it == values.end())
return null;
return it->second;
}
std::ostream& operator<<(std::ostream& os, const JsonObject& object)
{
size_t i = 0;
os << "{";
for (auto value : object.values)
{
os << "\"" << value.first << "\":" << value.second;
if (i != object.values.size() - 1)
{
os << ",";
}
i++;
}
os << "}";
return os;
}
void JsonArray::Add(const Json& value)
{
values.emplace_back(value);
}
Json& JsonArray::Reserve()
{
return values.emplace_back(Nullptr{});
}
const Json& JsonArray::Get(size_t i) const
{
if (i < values.size())
return values[i];
static Json null{Nullptr{}};
return null;
}
Json& JsonArray::Get(size_t i)
{
if (i < values.size())
return values[i];
static Json null{Nullptr{}};
return null;
}
size_t JsonArray::Size() const
{
return values.size();
}
std::ostream& operator<<(std::ostream& os, const JsonArray& object)
{
size_t i = 0;
os << "[";
for (auto& value : object.values)
{
os << value;
if (i != object.values.size() - 1)
{
os << ",";
}
i++;
}
os << "]";
return os;
}
bool Json::Read(const std::string& str, uint64_t& pos, Json& json)
{
uint64_t startPos = pos;
READ_WHITESPACE();
if (str[pos] == '{')
{
pos++;
JsonObject& object = json.value.emplace<JsonObject>();
while (true)
{
IF_READ_TOKEN('}')
{
break;
}
Json key;
if (!JsonUtil::ReadString(str, pos, key))
{
std::cout << JsonUtil::GetStringPos(str, pos) << ": Failed reading string" << std::endl;
return false;
}
IF_NOT_READ_TOKEN(':')
{
std::cout << JsonUtil::GetStringPos(str, pos) << ": Token after JsonObject key (\"" << key << "\") is not \':\'"
<< std::endl;
return false;
}
Json& element = object.Reserve(key.GetString());
uint64_t readPos = pos;
if (!Read(str, pos, element))
{
std::cout << JsonUtil::GetStringPos(str, readPos) << ": Json type of JsonObject key (\"" << key << "\") failed"
<< std::endl;
return false;
}
IF_NOT_READ_TOKEN(',')
{
IF_NOT_READ_TOKEN('}')
{
std::cout << JsonUtil::GetStringPos(str, pos) << ": expected token \",\" or \"}\"" << std::endl;
return false;
}
break;
}
}
return true;
}
else if (str[pos] == '[')
{
pos++;
JsonArray& array = json.value.emplace<JsonArray>();
while (true)
{
IF_READ_TOKEN(']')
{
break;
}
uint64_t readPos = pos;
Json& element = array.Reserve();
if (!Read(str, pos, element))
{
std::cout << JsonUtil::GetStringPos(str, readPos) << ": Json type of JsonArray failed" << std::endl;
return false;
}
IF_NOT_READ_TOKEN(',')
{
IF_NOT_READ_TOKEN(']')
{
std::cout << JsonUtil::GetStringPos(str, pos) << ": expected token \",\" or \"]\"" << std::endl;
return false;
}
break;
}
}
return true;
}
else if (str[pos] == '\"')
{
return JsonUtil::ReadString(str, pos, json);
}
else if (JsonUtil::IsKeyword(str, pos, "true"))
{
json.value.emplace<bool>(true);
return true;
}
else if (JsonUtil::IsKeyword(str, pos, "false"))
{
json.value.emplace<bool>(false);
return true;
}
else if (JsonUtil::IsKeyword(str, pos, "null"))
{
json.value.emplace<Nullptr>();
return true;
}
else if (JsonUtil::IsFloat(str, pos))
{
char* end;
double d = std::strtod(str.c_str() + pos, &end);
pos = end - str.c_str();
json.value.emplace<double>(d);
return true;
}
else if (JsonUtil::IsNumber(str, pos))
{
char* end;
int64_t i = std::strtoll(str.c_str() + pos, &end, 10);
pos = end - str.c_str();
json.value.emplace<uint64_t>(i);
return true;
}
return false;
}
+415
View File
@@ -0,0 +1,415 @@
#pragma once
#include <stdint.h>
#include <iostream>
#include <iterator>
#include <map>
#include <set>
#include <string>
#include <variant>
#include <vector>
class Json;
class JsonObject
{
private:
std::map<std::string, Json> values;
public:
void Add(const std::string& key, const Json& value);
Json& Reserve(const std::string& key);
void Remove(const std::string& key);
void Filter(const std::set<std::string>& set);
bool Has(const std::string& key) const;
const Json& Get(const std::string& key) const;
Json& Get(const std::string& key);
friend std::ostream& operator<<(std::ostream& os, const JsonObject& object);
std::map<std::string, Json>::iterator begin()
{
return values.begin();
}
std::map<std::string, Json>::iterator end()
{
return values.end();
}
std::map<std::string, Json>::const_iterator begin() const
{
return values.cbegin();
}
std::map<std::string, Json>::const_iterator end() const
{
return values.cend();
}
};
class JsonArray
{
private:
std::vector<Json> values;
public:
void Add(const Json& value);
Json& Reserve();
const Json& Get(size_t i) const;
Json& Get(size_t i);
size_t Size() const;
friend std::ostream& operator<<(std::ostream& os, const JsonArray& object);
std::vector<Json>::iterator begin()
{
return values.begin();
}
std::vector<Json>::iterator end()
{
return values.end();
}
std::vector<Json>::const_iterator begin() const
{
return values.cbegin();
}
std::vector<Json>::const_iterator end() const
{
return values.cend();
}
};
class Nullptr
{
friend std::ostream& operator<<(std::ostream& os, const Nullptr& object)
{
return os << "null";
}
};
class Json
{
public:
friend class JsonUtil;
using JsonVariant = std::variant<JsonObject, JsonArray, std::string, double, uint64_t, bool, Nullptr>;
private:
JsonVariant value;
public:
Json()
: value{Nullptr{}}
{
}
Json(const JsonObject& value)
: value{value}
{
}
Json(const JsonArray& value)
: value{value}
{
}
Json(const std::string& value)
: value{value}
{
}
Json(double value)
: value{value}
{
}
Json(float value)
: value{(double)value}
{
}
Json(uint64_t value)
: value{value}
{
}
Json(int64_t value)
: value{(uint64_t)value}
{
}
Json(uint32_t value)
: value{(uint64_t)value}
{
}
Json(int32_t value)
: value{(uint64_t)value}
{
}
Json(uint16_t value)
: value{(uint64_t)value}
{
}
Json(int16_t value)
: value{(uint64_t)value}
{
}
Json(uint8_t value)
: value{(uint64_t)value}
{
}
Json(int8_t value)
: value{(uint64_t)value}
{
}
Json(bool value)
: value{value}
{
}
Json(Nullptr value)
: value{value}
{
}
const JsonObject& GetJsonObject() const
{
if (IsJsonObject())
{
return std::get<JsonObject>(value);
}
static JsonObject ref;
return ref;
}
JsonObject& GetJsonObject()
{
if (IsJsonObject())
{
return std::get<JsonObject>(value);
}
static JsonObject ref;
return ref;
}
const JsonArray& GetJsonArray() const
{
if (IsJsonArray())
{
return std::get<JsonArray>(value);
}
static JsonArray ref;
return ref;
}
JsonArray& GetJsonArray()
{
if (IsJsonArray())
{
return std::get<JsonArray>(value);
}
static JsonArray ref;
return ref;
}
const std::string& GetString() const
{
if (IsString())
{
return std::get<std::string>(value);
}
static std::string ref;
return ref;
}
std::string& GetString()
{
if (IsString())
{
return std::get<std::string>(value);
}
static std::string ref;
return ref;
}
float GetFloat() const
{
if (IsFloat())
{
return (float)std::get<double>(value);
}
return 0.0f;
}
double GetDouble() const
{
if (IsString())
{
return std::get<double>(value);
}
return 0.0;
}
uint64_t GetUInt64() const
{
if (IsInt())
{
return std::get<uint64_t>(value);
}
return 0;
}
int64_t GetInt64() const
{
if (IsInt())
{
return (int64_t)std::get<uint64_t>(value);
}
return 0;
}
int32_t GetUInt32() const
{
if (IsInt())
{
return (uint32_t)std::get<uint64_t>(value);
}
return 0;
}
int32_t GetInt32() const
{
if (IsInt())
{
return (int32_t)std::get<uint64_t>(value);
}
return 0;
}
uint16_t GetUInt16() const
{
if (IsInt())
{
return (uint16_t)std::get<uint64_t>(value);
}
return 0;
}
int16_t GetInt16() const
{
if (IsInt())
{
return (int16_t)std::get<uint64_t>(value);
}
return 0;
}
uint8_t GetUInt8() const
{
if (IsInt())
{
return (uint8_t)std::get<uint64_t>(value);
}
return 0;
}
int8_t GetInt8() const
{
if (IsInt())
{
return (int8_t)std::get<uint64_t>(value);
}
return 0;
}
bool GetBool() const
{
if (IsBool())
{
return std::get<bool>(value);
}
return false;
}
bool IsJsonObject() const
{
return std::holds_alternative<JsonObject>(value);
}
bool IsJsonArray() const
{
return std::holds_alternative<JsonArray>(value);
}
bool IsString() const
{
return std::holds_alternative<std::string>(value);
}
bool IsFloat() const
{
return std::holds_alternative<double>(value);
}
bool IsInt() const
{
return std::holds_alternative<uint64_t>(value);
}
bool IsBool() const
{
return std::holds_alternative<bool>(value);
}
bool IsNull() const
{
return std::holds_alternative<Nullptr>(value);
}
friend std::ostream& operator<<(std::ostream& os, const Json& json)
{
if (json.IsJsonObject())
os << json.GetJsonObject();
else if (json.IsJsonArray())
os << json.GetJsonArray();
else if (json.IsString())
os << "\"" << json.GetString() << "\"";
else if (json.IsFloat())
os << json.GetFloat();
else if (json.IsInt())
os << json.GetInt64();
else if (json.IsBool())
os << json.GetBool() ? "true" : "false";
else if (json.IsNull())
os << "null";
return os;
}
static std::pair<bool, Json> FromString(const std::string& str)
{
uint64_t pos = 0;
Json json;
bool res = Read(str, pos, json);
return {res, json};
}
static std::pair<bool, Json> FromStream(std::istream& stream)
{
std::string s(std::istreambuf_iterator<char>(stream), {});
uint64_t pos = 0;
Json json;
bool res = Read(s, pos, json);
return {res, json};
}
static bool Read(const std::string& str, uint64_t& pos, Json& json);
};
+115
View File
@@ -0,0 +1,115 @@
#include "JsonUtil.h"
#include <cstring>
#include <utility>
#include "Json.h"
bool JsonUtil::IsWhiteSpace(char c)
{
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
}
JsonUtil::CursorPos JsonUtil::GetStringPos(const std::string& str, uint64_t pos)
{
CursorPos cursor;
for (uint64_t i = 0; i < pos; i++)
{
if (str[i] == '\n')
{
cursor.x = 1;
cursor.y++;
}
else
{
cursor.x++;
}
}
return cursor;
}
bool JsonUtil::ReadWhiteSpace(const std::string& str, uint64_t& pos)
{
while (pos < str.size() && IsWhiteSpace(str[pos]))
{
pos++;
}
return pos < str.size();
}
bool JsonUtil::IsNumber(char c)
{
return c >= '0' && c <= '9';
}
bool JsonUtil::IsFloat(const std::string& str, uint64_t pos)
{
if (str[pos] == '-')
pos++;
while (pos < str.size() && IsNumber(str[pos]))
{
pos++;
}
if (pos >= str.size())
return false;
if (str[pos] != '.')
return false;
return true;
}
bool JsonUtil::IsNumber(const std::string& str, uint64_t pos)
{
if (str[pos] == '-')
pos++;
while (pos < str.size() && IsNumber(str[pos]))
{
pos++;
}
if (pos >= str.size())
return false;
return true;
}
bool JsonUtil::IsKeyword(const std::string& str, uint64_t& pos, const std::string& s)
{
if (std::strncmp(str.c_str() + pos, s.c_str(), s.size()) != 0)
return false;
pos += s.size();
return true;
}
// "\\" is currently not possible, need to fix that at some point
// or any double escaped \\ before a "
bool JsonUtil::ReadString(const std::string& str, uint64_t& pos, Json& json)
{
size_t min = 10 < str.size() - pos ? 10 : str.size() - pos;
std::string_view view{str.c_str() + pos, min};
if (pos >= str.size())
{
std::cout << "EOF at start of string" << std::endl;
return false;
}
if (str[pos] != '\"')
{
std::cout << GetStringPos(str, pos) << ": String does not start with citation: " << view << std::endl;
return false;
}
pos++;
uint64_t stringStart = pos;
while (true)
{
while (pos < str.size() && str[pos] != '\"') pos++;
if (pos >= str.size() || str[pos - 1] != '\\')
break;
}
if (pos >= str.size())
{
pos = stringStart - 1;
std::cout << GetStringPos(str, pos) << ": String did not close with citation: " << view << std::endl;
return false;
}
pos++;
json.value.emplace<std::string>(str.begin() + stringStart, str.begin() + pos - 1);
return true;
}
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <stdint.h>
#include <iostream>
#include <string>
class Json;
class JsonUtil
{
private:
static bool IsWhiteSpace(char c);
public:
struct CursorPos
{
uint64_t x = 1;
uint64_t y = 1;
friend std::ostream& operator<<(std::ostream& os, const CursorPos& pos)
{
return os << "(" << pos.y << "," << pos.x << ")";
}
};
static CursorPos GetStringPos(const std::string& str, uint64_t pos);
static bool ReadWhiteSpace(const std::string& str, uint64_t& pos);
static bool IsNumber(char c);
static bool IsFloat(const std::string& str, uint64_t pos);
static bool IsNumber(const std::string& str, uint64_t pos);
static bool IsKeyword(const std::string& str, uint64_t& pos, const std::string& s);
// "\\" is currently not possible, need to fix that at some point
// or any double escaped \\ before a "
static bool ReadString(const std::string& str, uint64_t& pos, Json& json);
};
+75
View File
@@ -0,0 +1,75 @@
#pragma once
#include <map>
template <typename T, typename S>
struct Mapper
{
std::map<T, S> map1;
std::map<S, T> map2;
Mapper()
{
}
Mapper(const std::map<T, S>& map)
: map1{map}
{
for (auto&& elem : map)
{
auto&& res = map2.emplace({elem.second, elem.first});
if (!res.first)
{
map1.clear();
map2.clear();
std::cout << "Mapper : mappings are not unique : " << elem.second << std::endl;
return;
}
}
}
Mapper(const std::initializer_list<std::pair<T, S>>& map)
{
for (auto&& elem : map)
{
auto&& res = map1.emplace(elem.first, elem.second);
if (!res.second)
{
map1.clear();
map2.clear();
std::cout << "Mapper : mappings are not unique : " << elem.first << std::endl;
return;
}
auto&& res2 = map2.emplace(elem.second, elem.first);
if (!res2.second)
{
map1.clear();
map2.clear();
std::cout << "Mapper : mappings are not unique : " << elem.second << std::endl;
return;
}
}
}
void Emplace(const T& t, const S& s)
{
map1.emplace(t, s);
map2.emplace(s, t);
}
T* GetFirst(const S& key)
{
auto it = map2.find(key);
if (it == map2.end())
return nullptr;
return &it->second;
}
S* GetSecond(const T& key)
{
auto it = map1.find(key);
if (it == map1.end())
return nullptr;
return &it->second;
}
};
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include "Array2D.h"
#include "Array3D.h"
struct Math
{
Math() = delete;
static int Dot(const Index2D& i1, const Index2D& i2)
{
return i1.x * i2.x + i1.y * i2.y;
}
static int Dot(const Index3D& i1, const Index3D& i2)
{
return i1.x * i2.x + i1.y * i2.y + i1.z * i2.z;
}
};
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include <chrono>
class Timer
{
private:
using Time = std::chrono::steady_clock;
using TimeStamp = std::chrono::time_point<std::chrono::steady_clock>;
using Duration = std::chrono::duration<double>;
TimeStamp past;
TimeStamp current;
public:
Timer()
: past{Time::now()}
{
}
void Start()
{
past = Time::now();
}
void Stop()
{
current = Time::now();
}
double Elapsed()
{
return Duration{current - past}.count();
}
};
+64
View File
@@ -0,0 +1,64 @@
#include <chrono>
#include <iostream>
#include <vector>
#include "aoc.h"
aoc::Registry* aoc::Registry::GetInstance()
{
static aoc::Registry* instance = new aoc::Registry();
return instance;
}
int main(int argc, char** argv)
{
std::regex regex{""};
if (argc > 1)
regex = argv[1];
Timer totalTimer;
int passed = 0;
int skipped = 0;
for (auto day : aoc::Registry::GetDays())
{
if (!std::regex_search(day->GetName(), regex))
{
skipped++;
continue;
}
std::cout << TERM_GREEN << "[ RUN ] " << TERM_CLEAR << day->GetName() << std::endl;
Timer timer;
bool res = day->AocRunTestCases();
timer.Stop();
if (res)
{
std::cout << TERM_GREEN << "[=========] " << TERM_CLEAR << std::endl;
std::cout << TERM_GREEN << "[ OK ] " << TERM_CLEAR << day->GetName() << " Took: " << timer.Elapsed() << "s"
<< std::endl;
passed++;
}
else
{
std::cout << TERM_RED << "[=========] " << TERM_CLEAR << std::endl;
std::cout << TERM_RED << "[ FAILED ] " << TERM_CLEAR << day->GetName() << std::endl;
}
std::cout << std::endl;
}
totalTimer.Stop();
if (passed + skipped != aoc::Registry::GetDays().size())
{
std::cout << std::endl
<< TERM_RED << "[ FAILED ] " << TERM_CLEAR << passed << "/" << aoc::Registry::GetDays().size() - skipped
<< " days passed (" << skipped << " skipped) in " << totalTimer.Elapsed() << "s" << std::endl;
return 1;
}
else
{
std::cout << std::endl
<< TERM_GREEN << "[ PASSED ] " << TERM_CLEAR << "All days passed (" << skipped << " skipped) in "
<< totalTimer.Elapsed() << "s" << std::endl;
return 0;
}
}