Initial commit with solutions from 2024
This commit is contained in:
+334
@@ -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
@@ -0,0 +1,10 @@
|
||||
Debug/
|
||||
Release/
|
||||
.vs/
|
||||
*/Debug/
|
||||
*/Release/
|
||||
bin/
|
||||
.ycm_extra_conf.py
|
||||
cookies.txt
|
||||
res/*/*/input.txt
|
||||
Makefile
|
||||
@@ -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.
|
||||
@@ -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!
|
||||
@@ -0,0 +1,4 @@
|
||||
-xc++
|
||||
-Isrc/
|
||||
-std=c++17
|
||||
|
||||
@@ -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
@@ -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>
|
||||
@@ -0,0 +1,6 @@
|
||||
3 4
|
||||
4 3
|
||||
2 5
|
||||
1 3
|
||||
3 9
|
||||
3 3
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))
|
||||
@@ -0,0 +1,10 @@
|
||||
MMMSXXMASM
|
||||
MSAMXMSMSA
|
||||
AMXSXMAAMM
|
||||
MSAMASMSMX
|
||||
XMASAMXAMM
|
||||
XXAMMXXAMA
|
||||
SMSMSASXSS
|
||||
SAXAMASAAA
|
||||
MAMMMXMMMM
|
||||
MXMXAXMASX
|
||||
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
....#.....
|
||||
.........#
|
||||
..........
|
||||
..#.......
|
||||
.......#..
|
||||
..........
|
||||
.#..^.....
|
||||
........#.
|
||||
#.........
|
||||
......#...
|
||||
@@ -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
|
||||
@@ -0,0 +1,12 @@
|
||||
............
|
||||
........0...
|
||||
.....0......
|
||||
.......0....
|
||||
....0.......
|
||||
......A.....
|
||||
............
|
||||
............
|
||||
........A...
|
||||
.........A..
|
||||
............
|
||||
............
|
||||
@@ -0,0 +1 @@
|
||||
2333133121414131402
|
||||
@@ -0,0 +1,8 @@
|
||||
89010123
|
||||
78121874
|
||||
87430965
|
||||
96549874
|
||||
45678903
|
||||
32019012
|
||||
01329801
|
||||
10456732
|
||||
@@ -0,0 +1 @@
|
||||
125 17
|
||||
@@ -0,0 +1,10 @@
|
||||
RRRRIICCFF
|
||||
RRRRIICCCF
|
||||
VVRRRCCFFF
|
||||
VVRCCCJFFF
|
||||
VVVVCJJCFE
|
||||
VVIVCCJJEE
|
||||
VVIIICJJEE
|
||||
MIIIIIJJEE
|
||||
MIIISIJEEE
|
||||
MMMISSJEEE
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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^<<^
|
||||
@@ -0,0 +1,15 @@
|
||||
###############
|
||||
#.......#....E#
|
||||
#.#.###.#.###.#
|
||||
#.....#.#...#.#
|
||||
#.###.#####.#.#
|
||||
#.#.#.......#.#
|
||||
#.#.#####.###.#
|
||||
#...........#.#
|
||||
###.#.#####.#.#
|
||||
#...#.....#.#.#
|
||||
#.#.#.###.#.#.#
|
||||
#.....#...#.#.#
|
||||
#.###.#.#.#.#.#
|
||||
#S..#.....#...#
|
||||
###############
|
||||
@@ -0,0 +1,5 @@
|
||||
Register A: 2024
|
||||
Register B: 0
|
||||
Register C: 0
|
||||
|
||||
Program: 0,3,5,4,3,0
|
||||
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
r, wr, b, g, bwu, rb, gb, br
|
||||
|
||||
brwrr
|
||||
bggr
|
||||
gbbr
|
||||
rrbgbr
|
||||
ubwu
|
||||
bwurrg
|
||||
brgr
|
||||
bbrgwb
|
||||
@@ -0,0 +1,15 @@
|
||||
###############
|
||||
#...#...#.....#
|
||||
#.#.#.#.#.###.#
|
||||
#S#...#.#.#...#
|
||||
#######.#.#.###
|
||||
#######.#.#...#
|
||||
#######.#.###.#
|
||||
###..E#...#...#
|
||||
###.#######.###
|
||||
#...###...#...#
|
||||
#.#####.#.###.#
|
||||
#.#...#.#.#...#
|
||||
#.#.#.#.#.#.###
|
||||
#...#...#...###
|
||||
###############
|
||||
@@ -0,0 +1,5 @@
|
||||
029A
|
||||
980A
|
||||
179A
|
||||
456A
|
||||
379A
|
||||
@@ -0,0 +1,4 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
2024
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,39 @@
|
||||
#####
|
||||
.####
|
||||
.####
|
||||
.####
|
||||
.#.#.
|
||||
.#...
|
||||
.....
|
||||
|
||||
#####
|
||||
##.##
|
||||
.#.##
|
||||
...##
|
||||
...#.
|
||||
...#.
|
||||
.....
|
||||
|
||||
.....
|
||||
#....
|
||||
#....
|
||||
#...#
|
||||
#.#.#
|
||||
#.###
|
||||
#####
|
||||
|
||||
.....
|
||||
.....
|
||||
#.#..
|
||||
###..
|
||||
###.#
|
||||
###.#
|
||||
#####
|
||||
|
||||
.....
|
||||
.....
|
||||
.....
|
||||
#....
|
||||
#.#..
|
||||
#.#.#
|
||||
#####
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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{};
|
||||
}
|
||||
};
|
||||
@@ -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 << ")";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user