commit be113565fbcacadadfac4bf62a20e793c9442a27 Author: Thraix Date: Wed Nov 26 21:59:50 2025 +0100 Initial commit with solutions from 2024 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0dd9657 --- /dev/null +++ b/.clang-format @@ -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: '^' + 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 +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b0bc5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +Debug/ +Release/ +.vs/ +*/Debug/ +*/Release/ +bin/ +.ycm_extra_conf.py +cookies.txt +res/*/*/input.txt +Makefile diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3a148af --- /dev/null +++ b/LICENSE @@ -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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..26bfe8e --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# Solutions for AdventOfCode + +Solutions are located in `AdventOfCode/src//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! diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..4b01977 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,4 @@ +-xc++ +-Isrc/ +-std=c++17 + diff --git a/fetch.sh b/fetch.sh new file mode 100755 index 0000000..bb9bd06 --- /dev/null +++ b/fetch.sh @@ -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 diff --git a/makegen.xml b/makegen.xml new file mode 100644 index 0000000..0edd2bb --- /dev/null +++ b/makegen.xml @@ -0,0 +1,23 @@ + + + false + src/ + bin/ + adventofcode.out + executable + AdventOfCode + src/ + + + false + src/ + -g + bin/ + adventofcode.out + executable + AdventOfCode + src/ + + Debug + v1.3.0 + diff --git a/res/2024/day01/test_input.txt b/res/2024/day01/test_input.txt new file mode 100644 index 0000000..dfca0b1 --- /dev/null +++ b/res/2024/day01/test_input.txt @@ -0,0 +1,6 @@ +3 4 +4 3 +2 5 +1 3 +3 9 +3 3 \ No newline at end of file diff --git a/res/2024/day02/test_input.txt b/res/2024/day02/test_input.txt new file mode 100644 index 0000000..82cd679 --- /dev/null +++ b/res/2024/day02/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day03/test_input.txt b/res/2024/day03/test_input.txt new file mode 100644 index 0000000..30032cb --- /dev/null +++ b/res/2024/day03/test_input.txt @@ -0,0 +1 @@ +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) diff --git a/res/2024/day04/test_input.txt b/res/2024/day04/test_input.txt new file mode 100644 index 0000000..c41c5ea --- /dev/null +++ b/res/2024/day04/test_input.txt @@ -0,0 +1,10 @@ +MMMSXXMASM +MSAMXMSMSA +AMXSXMAAMM +MSAMASMSMX +XMASAMXAMM +XXAMMXXAMA +SMSMSASXSS +SAXAMASAAA +MAMMMXMMMM +MXMXAXMASX \ No newline at end of file diff --git a/res/2024/day05/test_input.txt b/res/2024/day05/test_input.txt new file mode 100644 index 0000000..d4d4441 --- /dev/null +++ b/res/2024/day05/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day06/test_input.txt b/res/2024/day06/test_input.txt new file mode 100644 index 0000000..b60e466 --- /dev/null +++ b/res/2024/day06/test_input.txt @@ -0,0 +1,10 @@ +....#..... +.........# +.......... +..#....... +.......#.. +.......... +.#..^..... +........#. +#......... +......#... \ No newline at end of file diff --git a/res/2024/day07/test_input.txt b/res/2024/day07/test_input.txt new file mode 100644 index 0000000..87b8b25 --- /dev/null +++ b/res/2024/day07/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day08/test_input.txt b/res/2024/day08/test_input.txt new file mode 100644 index 0000000..de0f909 --- /dev/null +++ b/res/2024/day08/test_input.txt @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ \ No newline at end of file diff --git a/res/2024/day09/test_input.txt b/res/2024/day09/test_input.txt new file mode 100644 index 0000000..5ff5aae --- /dev/null +++ b/res/2024/day09/test_input.txt @@ -0,0 +1 @@ +2333133121414131402 \ No newline at end of file diff --git a/res/2024/day10/test_input.txt b/res/2024/day10/test_input.txt new file mode 100644 index 0000000..7bb1248 --- /dev/null +++ b/res/2024/day10/test_input.txt @@ -0,0 +1,8 @@ +89010123 +78121874 +87430965 +96549874 +45678903 +32019012 +01329801 +10456732 \ No newline at end of file diff --git a/res/2024/day11/test_input.txt b/res/2024/day11/test_input.txt new file mode 100644 index 0000000..528f9d5 --- /dev/null +++ b/res/2024/day11/test_input.txt @@ -0,0 +1 @@ +125 17 \ No newline at end of file diff --git a/res/2024/day12/test_input.txt b/res/2024/day12/test_input.txt new file mode 100644 index 0000000..0b328f1 --- /dev/null +++ b/res/2024/day12/test_input.txt @@ -0,0 +1,10 @@ +RRRRIICCFF +RRRRIICCCF +VVRRRCCFFF +VVRCCCJFFF +VVVVCJJCFE +VVIVCCJJEE +VVIIICJJEE +MIIIIIJJEE +MIIISIJEEE +MMMISSJEEE \ No newline at end of file diff --git a/res/2024/day13/test_input.txt b/res/2024/day13/test_input.txt new file mode 100644 index 0000000..444a287 --- /dev/null +++ b/res/2024/day13/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day14/test_input.txt b/res/2024/day14/test_input.txt new file mode 100644 index 0000000..72a324a --- /dev/null +++ b/res/2024/day14/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day15/test_input.txt b/res/2024/day15/test_input.txt new file mode 100644 index 0000000..84cf1fb --- /dev/null +++ b/res/2024/day15/test_input.txt @@ -0,0 +1,21 @@ +########## +#..O..O.O# +#......O.# +#.OO..O.O# +#..O@..O.# +#O#..O...# +#O..O..O.# +#.OO.O.OO# +#....O...# +########## + +^v>^vv^v>v<>v^v<<><>>v^v^>^<<<><^ +vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<^<^^>>>^<>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^v^^<^^vv< +<>^^^^>>>v^<>vvv^>^^^vv^^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ +^><^><>>><>^^<<^^v>>><^^>v>>>^v><>^v><<<>vvvv>^<><<>^>< +^>><>^v<><^vvv<^^<><^v<<<><<<^^<^>>^<<<^>>^v^>>^v>vv>^<<^v<>><<><<>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ +<><^^>^^^<>^vv<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> +^^>vv<^v^v^<>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<>< +v^^>>><<^^<>>^v^v^<<>^<^v^v><^<<<><<^vv>>v>v^<<^ diff --git a/res/2024/day16/test_input.txt b/res/2024/day16/test_input.txt new file mode 100644 index 0000000..2c21676 --- /dev/null +++ b/res/2024/day16/test_input.txt @@ -0,0 +1,15 @@ +############### +#.......#....E# +#.#.###.#.###.# +#.....#.#...#.# +#.###.#####.#.# +#.#.#.......#.# +#.#.#####.###.# +#...........#.# +###.#.#####.#.# +#...#.....#.#.# +#.#.#.###.#.#.# +#.....#...#.#.# +#.###.#.#.#.#.# +#S..#.....#...# +############### diff --git a/res/2024/day17/test_input.txt b/res/2024/day17/test_input.txt new file mode 100644 index 0000000..4a91c26 --- /dev/null +++ b/res/2024/day17/test_input.txt @@ -0,0 +1,5 @@ +Register A: 2024 +Register B: 0 +Register C: 0 + +Program: 0,3,5,4,3,0 diff --git a/res/2024/day18/test_input.txt b/res/2024/day18/test_input.txt new file mode 100644 index 0000000..0371b23 --- /dev/null +++ b/res/2024/day18/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day19/test_input.txt b/res/2024/day19/test_input.txt new file mode 100644 index 0000000..ad43a74 --- /dev/null +++ b/res/2024/day19/test_input.txt @@ -0,0 +1,10 @@ +r, wr, b, g, bwu, rb, gb, br + +brwrr +bggr +gbbr +rrbgbr +ubwu +bwurrg +brgr +bbrgwb \ No newline at end of file diff --git a/res/2024/day20/test_input.txt b/res/2024/day20/test_input.txt new file mode 100644 index 0000000..f107d40 --- /dev/null +++ b/res/2024/day20/test_input.txt @@ -0,0 +1,15 @@ +############### +#...#...#.....# +#.#.#.#.#.###.# +#S#...#.#.#...# +#######.#.#.### +#######.#.#...# +#######.#.###.# +###..E#...#...# +###.#######.### +#...###...#...# +#.#####.#.###.# +#.#...#.#.#...# +#.#.#.#.#.#.### +#...#...#...### +############### \ No newline at end of file diff --git a/res/2024/day21/test_input.txt b/res/2024/day21/test_input.txt new file mode 100644 index 0000000..dd73dfd --- /dev/null +++ b/res/2024/day21/test_input.txt @@ -0,0 +1,5 @@ +029A +980A +179A +456A +379A \ No newline at end of file diff --git a/res/2024/day22/test_input.txt b/res/2024/day22/test_input.txt new file mode 100644 index 0000000..cfb1988 --- /dev/null +++ b/res/2024/day22/test_input.txt @@ -0,0 +1,4 @@ +1 +2 +3 +2024 \ No newline at end of file diff --git a/res/2024/day23/test_input.txt b/res/2024/day23/test_input.txt new file mode 100644 index 0000000..d8576a8 --- /dev/null +++ b/res/2024/day23/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day24/test_input.txt b/res/2024/day24/test_input.txt new file mode 100644 index 0000000..09fb230 --- /dev/null +++ b/res/2024/day24/test_input.txt @@ -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 \ No newline at end of file diff --git a/res/2024/day25/test_input.txt b/res/2024/day25/test_input.txt new file mode 100644 index 0000000..a53fe9b --- /dev/null +++ b/res/2024/day25/test_input.txt @@ -0,0 +1,39 @@ +##### +.#### +.#### +.#### +.#.#. +.#... +..... + +##### +##.## +.#.## +...## +...#. +...#. +..... + +..... +#.... +#.... +#...# +#.#.# +#.### +##### + +..... +..... +#.#.. +###.. +###.# +###.# +##### + +..... +..... +..... +#.... +#.#.. +#.#.# +##### \ No newline at end of file diff --git a/src/2024/Day01.cpp b/src/2024/Day01.cpp new file mode 100644 index 0000000..411aaca --- /dev/null +++ b/src/2024/Day01.cpp @@ -0,0 +1,59 @@ +#include "common/aoc.h" + +namespace day01 +{ + using InputType = std::pair, std::vector>; + 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 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 copyA = input.first; + std::vector 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 idCount; + for (int i : input.second) + { + idCount[i]++; + } + + int sum = 0; + for (int i : input.first) + { + sum += i * idCount[i]; + } + + return sum; + } +} diff --git a/src/2024/Day02.cpp b/src/2024/Day02.cpp new file mode 100644 index 0000000..f4972a7 --- /dev/null +++ b/src/2024/Day02.cpp @@ -0,0 +1,75 @@ +#include "common/aoc.h" + +namespace day02 +{ + REGISTER_DAY(2024, Day02, std::vector>, 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> 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& 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& vec, int index) + { + std::vector 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; + } +} diff --git a/src/2024/Day03.cpp b/src/2024/Day03.cpp new file mode 100644 index 0000000..de95296 --- /dev/null +++ b/src/2024/Day03.cpp @@ -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 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 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; + } +} diff --git a/src/2024/Day04.cpp b/src/2024/Day04.cpp new file mode 100644 index 0000000..d2f9ae9 --- /dev/null +++ b/src/2024/Day04.cpp @@ -0,0 +1,76 @@ +#include "common/aoc.h" + +namespace day04 +{ + REGISTER_DAY(2024, Day04, Array2D, 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; + } +} diff --git a/src/2024/Day05.cpp b/src/2024/Day05.cpp new file mode 100644 index 0000000..cc35788 --- /dev/null +++ b/src/2024/Day05.cpp @@ -0,0 +1,94 @@ +#include "common/aoc.h" + +namespace day05 +{ + struct Manual + { + std::vector> pairs; + std::vector> 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> 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> pages; + while (std::getline(input, str)) + { + std::stringstream ss{str}; + pages.emplace_back(Input::ReadInts(ss, ',')); + } + return Manual{pairs, pages}; + } + + std::pair IsValid(const std::vector& input, const std::vector>& 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 Sort(std::vector input, const std::vector>& 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; + } +} diff --git a/src/2024/Day06.cpp b/src/2024/Day06.cpp new file mode 100644 index 0000000..d65bbbb --- /dev/null +++ b/src/2024/Day06.cpp @@ -0,0 +1,90 @@ +#include "common/aoc.h" + +namespace day06 +{ + REGISTER_DAY(2024, Day06, Array2D, 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& map, Index2D obstacle, Index2D startPos, Index2D startDir) + { + std::set> 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 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 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; + } +} diff --git a/src/2024/Day07.cpp b/src/2024/Day07.cpp new file mode 100644 index 0000000..2774db8 --- /dev/null +++ b/src/2024/Day07.cpp @@ -0,0 +1,94 @@ +#include "common/aoc.h" + +namespace day07 +{ + struct Data + { + int64_t result; + std::vector terms; + }; + + REGISTER_DAY(2024, Day07, std::vector, 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 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; + } +} diff --git a/src/2024/Day08.cpp b/src/2024/Day08.cpp new file mode 100644 index 0000000..7c8493f --- /dev/null +++ b/src/2024/Day08.cpp @@ -0,0 +1,94 @@ +#include "common/aoc.h" + +namespace day08 +{ + struct Data + { + std::map> 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 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 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(); + } +} diff --git a/src/2024/Day09.cpp b/src/2024/Day09.cpp new file mode 100644 index 0000000..0540245 --- /dev/null +++ b/src/2024/Day09.cpp @@ -0,0 +1,143 @@ +#include "common/aoc.h" + +namespace day09 +{ + REGISTER_DAY(2024, Day09, std::vector, 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 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> 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 idToPos; + std::vector resMemory; + std::vector> 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; + } +} diff --git a/src/2024/Day10.cpp b/src/2024/Day10.cpp new file mode 100644 index 0000000..3c08c84 --- /dev/null +++ b/src/2024/Day10.cpp @@ -0,0 +1,84 @@ +#include "common/aoc.h" + +namespace day10 +{ + REGISTER_DAY(2024, Day10, Array2D, 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& map, Index2D pos, std::set& visited) + { + int val = map.Get(pos); + if (val == 9) + { + visited.emplace(pos); + return; + } + + std::vector 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& map, Index2D pos) + { + int val = map.Get(pos); + if (val == 9) + return 1; + + int paths = 0; + std::vector 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 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; + } +} diff --git a/src/2024/Day11.cpp b/src/2024/Day11.cpp new file mode 100644 index 0000000..40ed4c9 --- /dev/null +++ b/src/2024/Day11.cpp @@ -0,0 +1,96 @@ +#include "common/aoc.h" + +namespace day11 +{ + using Stones = std::map; + 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 split(int64_t i) + { + int log = logi10(i); + int64_t pow = pow10(log / 2); + + return {i / pow, i % pow}; + } + + void Step(std::map& stones) + { + std::map 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& stones, int iterations) + { + std::map 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); + } +} diff --git a/src/2024/Day12.cpp b/src/2024/Day12.cpp new file mode 100644 index 0000000..50f2e36 --- /dev/null +++ b/src/2024/Day12.cpp @@ -0,0 +1,117 @@ +#include "common/aoc.h" + +namespace day12 +{ + REGISTER_DAY(2024, Day12, Array2D, 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& map, + Index2D position, + std::set& totalVisited, + std::set& area, + std::set>& perimeter) + { + char c = map.Get(position); + std::stack 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>& 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>& 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 visited; + for (auto it = input.begin(); it != input.end(); it++) + { + Index2D index = it.index; + if (visited.count(index) == 0) + { + std::set area; + std::set> perimeter; + CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter); + + sum += perimeter.size() * area.size(); + } + } + return sum; + } + + OUTPUT2(input) + { + int sum = 0; + std::set visited; + for (auto it = input.begin(); it != input.end(); it++) + { + if (visited.count(it.index) == 0) + { + std::set area; + std::set> perimeter; + CalculateAreaAndPerimiter(input, it.index, visited, area, perimeter); + int perimeterSize = CalculatePerimeterSideSize(perimeter); + + sum += perimeterSize * area.size(); + } + } + return sum; + } +} diff --git a/src/2024/Day13.cpp b/src/2024/Day13.cpp new file mode 100644 index 0000000..50d8374 --- /dev/null +++ b/src/2024/Day13.cpp @@ -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, 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 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; + } +} diff --git a/src/2024/Day14.cpp b/src/2024/Day14.cpp new file mode 100644 index 0000000..9bf95af --- /dev/null +++ b/src/2024/Day14.cpp @@ -0,0 +1,98 @@ +#include "common/aoc.h" + +namespace day14 +{ + struct Robot + { + Index2D pos; + Index2D vel; + }; + + REGISTER_DAY(2024, Day14, std::vector, 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 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& robots, int length, int dimension) + { + int maxIndex = 0; + int maxNeighbors = 0; + for (int i = 1; i <= length; i++) + { + std::vector 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}); + } +} diff --git a/src/2024/Day15.cpp b/src/2024/Day15.cpp new file mode 100644 index 0000000..aa51540 --- /dev/null +++ b/src/2024/Day15.cpp @@ -0,0 +1,198 @@ +#include "common/aoc.h" + +namespace day15 +{ + struct Map + { + Array2D 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& 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& map, Index2D boxPos, Index2D dir); + + bool CanMoveSide(const Array2D& 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& 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& 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 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); + } +} diff --git a/src/2024/Day16.cpp b/src/2024/Day16.cpp new file mode 100644 index 0000000..f91044d --- /dev/null +++ b/src/2024/Day16.cpp @@ -0,0 +1,55 @@ +#include "common/aoc.h" + +namespace day16 +{ + REGISTER_DAY(2024, Day16, Array2D, 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>> Branch(const Array2D& map, + const std::pair& state) + { + std::vector>> 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& map, const std::pair& 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> allVisited = + Helper::DijkstrasAllVisited(input, std::pair{input.Find('S'), Index2D{1, 0}}, Branch, Goal); + + std::set solutions; + for (const auto& [pos, dir] : allVisited) solutions.emplace(pos); + + return solutions.size(); + } +} diff --git a/src/2024/Day17.cpp b/src/2024/Day17.cpp new file mode 100644 index 0000000..291085a --- /dev/null +++ b/src/2024/Day17.cpp @@ -0,0 +1,173 @@ +#include + +#include "common/aoc.h" + +namespace day17 +{ + struct Program + { + int64_t A; + int64_t B; + int64_t C; + std::vector 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 RunProgram(const Program& program) + { + int64_t a = program.A; + int64_t b = program.B; + int64_t c = program.C; + int pc = 0; + std::vector 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 GetDiffPeriodicity(const std::vector& ints) + { + if (ints.empty()) + return {}; + + const int MIN_LOOP_COUNT = 3; + if ((ints.size() - 1) % MIN_LOOP_COUNT != 0) + return {}; + + std::vector 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 result = RunProgram(input); + std::cout << result << std::endl; + return 0; + } + + OUTPUT2(input) + { + int64_t aRegister = 0; + int currentDepth = 0; + std::vector diffs{1}; + std::vector aRegisterSolution{}; + Program program = input; + int offset = 0; + while (true) + { + program.A = aRegister; + std::vector vec = RunProgram(program); + + if (vec == input.instructions) + { + return aRegister; + } + + if (vec.size() > currentDepth && vec[currentDepth] == program.instructions[currentDepth]) + { + aRegisterSolution.emplace_back(aRegister); + } + + std::vector 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; + } +} diff --git a/src/2024/Day18.cpp b/src/2024/Day18.cpp new file mode 100644 index 0000000..cb84203 --- /dev/null +++ b/src/2024/Day18.cpp @@ -0,0 +1,96 @@ +#include "common/aoc.h" + +namespace day18 +{ + using Memory = std::map; + 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 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& memory; + int size; + int time; + }; + + std::vector> Branch(const Map& map, const Index2D& current) + { + std::vector> 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; + } +} diff --git a/src/2024/Day19.cpp b/src/2024/Day19.cpp new file mode 100644 index 0000000..353a590 --- /dev/null +++ b/src/2024/Day19.cpp @@ -0,0 +1,116 @@ +#include "common/aoc.h" + +namespace day19 +{ + struct Towels + { + std::vector patterns; + std::vector 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& patterns, + int offset, + std::set& 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& patterns, + int offset, + std::map& 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 memoization; + if (HasCombination(towel, input.patterns, 0, memoization)) + sum++; + } + + return sum; + } + + OUTPUT2(input) + { + int64_t sum = 0; + + for (auto& towel : input.towels) + { + std::map memoization; + sum += AmountOfCombinations(towel, input.patterns, 0, memoization); + } + + return sum; + } +} diff --git a/src/2024/Day20.cpp b/src/2024/Day20.cpp new file mode 100644 index 0000000..1d0e297 --- /dev/null +++ b/src/2024/Day20.cpp @@ -0,0 +1,93 @@ +#include "common/aoc.h" + +namespace day20 +{ + REGISTER_DAY(2024, Day20, Array2D, 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> DFS(const Array2D& map, const Index2D& initial) + { + std::vector path; + Index2D prev = Index2D{-1, -1}; + std::pair 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& map, int cheatTime, int diff) + { + int sum = 0; + std::pair> path = DFS(map, map.Find('S')); + + std::vector 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); + } +} diff --git a/src/2024/Day21.cpp b/src/2024/Day21.cpp new file mode 100644 index 0000000..7000d14 --- /dev/null +++ b/src/2024/Day21.cpp @@ -0,0 +1,213 @@ +#include "common/aoc.h" + +namespace day21 +{ + REGISTER_DAY(2024, Day21, std::vector, 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> PossiblePaths(const Index2D& from, const Index2D& to, bool keypad) + { + std::set visited; + std::queue> openPath; + openPath.emplace(std::vector{from}); + std::vector> 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 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& 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> paths = PossiblePaths(from, to, depth == 0); + int64_t min = std::numeric_limits::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& input, int depth) + { + std::map 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); + } +} diff --git a/src/2024/Day22.cpp b/src/2024/Day22.cpp new file mode 100644 index 0000000..0ec210a --- /dev/null +++ b/src/2024/Day22.cpp @@ -0,0 +1,80 @@ +#include "common/aoc.h" + +namespace day22 +{ + REGISTER_DAY(2024, Day22, std::vector, 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& 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 monkeySequences(MAX_INDEX, 0); + int best = 0; + for (auto num : input) + { + std::vector alreadyFoundSequence(MAX_INDEX, false); + std::vector 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; + } +} diff --git a/src/2024/Day23.cpp b/src/2024/Day23.cpp new file mode 100644 index 0000000..3b0ee4d --- /dev/null +++ b/src/2024/Day23.cpp @@ -0,0 +1,105 @@ +#include "common/aoc.h" + +namespace day23 +{ + struct Network + { + std::map> 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& computers, std::set& 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> 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{computer.first, *it, *it2}); + } + } + } + return groupsOfThree.size(); + } + + OUTPUT2(input) + { + std::set largest; + for (const auto& computer : input.graph) + { + std::set 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; + } +} diff --git a/src/2024/Day24.cpp b/src/2024/Day24.cpp new file mode 100644 index 0000000..f0e0c4b --- /dev/null +++ b/src/2024/Day24.cpp @@ -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 gates; + std::vector 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& 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 outputs; + for (const auto& gate : gates.gateOperation) + { + outputs.emplace(gate.res); + } + + std::string carry{Find(gates, "x00", "y00", "AND")}; + + std::set 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; + } +} diff --git a/src/2024/Day25.cpp b/src/2024/Day25.cpp new file mode 100644 index 0000000..44915ed --- /dev/null +++ b/src/2024/Day25.cpp @@ -0,0 +1,100 @@ +#include "common/aoc.h" + +namespace day25 +{ + struct KeysLocks + { + int height{0}; + std::vector> keys; + std::vector> 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 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(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; + } +} diff --git a/src/common/aoc.h b/src/common/aoc.h new file mode 100644 index 0000000..4f770f6 --- /dev/null +++ b/src/common/aoc.h @@ -0,0 +1,304 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + 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 + class DayT : public Day + { + protected: + std::vector*> tests; + + std::string name; + bool isExample = true; + + public: + DayT(const std::string& name) + : name{name} + { + } + + void AddTest(Test* 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; + Days days; + + Day* AddDay(Day* day) + { + days.emplace(day); + return day; + } + + template + Test* AddTest(std::string year, std::string day, Test* 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* dayT = dynamic_cast*>(*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 + static DayImpl* RegisterDay(DayImpl* day) + { + return (DayImpl*)GetInstance()->AddDay(day); + } + + template + static Test* RegisterTest(std::string year, + std::string day, + Test* 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 \ + { \ + 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* testName = aoc::Registry::RegisterTest( \ + #year, #day, new aoc::Test(#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) diff --git a/src/common/lib/Array2D.h b/src/common/lib/Array2D.h new file mode 100644 index 0000000..635ece6 --- /dev/null +++ b/src/common/lib/Array2D.h @@ -0,0 +1,506 @@ +#pragma once + +#include +#include +#include + +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 +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& array2D; + Index2D index; + + ConstIterator(const Array2D& 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& array2D; + Index2D index; + + Iterator(Array2D& 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 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(width * height, fill); + array2D.resize(width * height); + } + + Array2D(int width, int height, const std::vector& 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 GetNeighbors(const Index2D& index, bool includeDiagonals) const + { + std::vector 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 + 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::Iterator begin() + { + return Iterator{*this, Index2D{0, 0}}; + } + + typename Array2D::Iterator end() + { + return Iterator{*this, Index2D{0, height}}; + } + + typename Array2D::ConstIterator begin() const + { + return ConstIterator{*this, Index2D{0, 0}}; + } + + typename Array2D::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; + } +}; diff --git a/src/common/lib/Array3D.h b/src/common/lib/Array3D.h new file mode 100644 index 0000000..9934994 --- /dev/null +++ b/src/common/lib/Array3D.h @@ -0,0 +1,424 @@ +#pragma once + +#include +#include + +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 +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& array3D; + Index3D index; + + ConstIterator(const Array3D& 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& array3D; + Index3D index; + + Iterator(Array3D& 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 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& 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 + 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::Iterator begin() + { + return Iterator{*this, Index3D{0, 0, 0}}; + } + + typename Array3D::Iterator end() + { + return Iterator{*this, Index3D{0, 0, length}}; + } + + typename Array3D::ConstIterator begin() const + { + return ConstIterator{*this, Index3D{0, 0, 0}}; + } + + typename Array3D::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; + } +}; diff --git a/src/common/lib/Array4D.h b/src/common/lib/Array4D.h new file mode 100644 index 0000000..cb7bd31 --- /dev/null +++ b/src/common/lib/Array4D.h @@ -0,0 +1,320 @@ +#pragma once + +#include +#include + +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 +struct Array4D +{ + int width; + int height; + int length; + int time; + std::vector 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& 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 + 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::iterator begin() + { + return array4D.begin(); + } + + typename std::vector::iterator end() + { + return array4D.end(); + } + + typename std::vector::const_iterator begin() const + { + return array4D.cbegin(); + } + + typename std::vector::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; + } +}; diff --git a/src/common/lib/Graph.h b/src/common/lib/Graph.h new file mode 100644 index 0000000..79cb8f7 --- /dev/null +++ b/src/common/lib/Graph.h @@ -0,0 +1,153 @@ +#pragma once + +#include +#include + +template +class Graph +{ + std::map> graph; + + // Data based on the graph + std::set 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 visited; + return DFSLong(to, visited, from, Value{}); + } + + Value DFS(const Key& from, const Key& to) + { + std::set visited; + return DFS(to, visited, from, Value{}); + } + + Value Dijkstras(const Key& from, const Key& to) const + { + std::map visited; + std::multimap 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> DijkstrasWithPath(const Key& from, const Key& to) const + { + std::map visited; + std::multimap> openSet; + openSet.emplace(Value{}, std::vector{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 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& GetPaths(const Key& from) const + { + return graph.find(from)->second; + } + + const std::set& GetNodes() const + { + return nodes; + } + +private: + Value DFSLong(const Key& to, std::set& 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& 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{}; + } +}; diff --git a/src/common/lib/Helper.h b/src/common/lib/Helper.h new file mode 100644 index 0000000..dd176f1 --- /dev/null +++ b/src/common/lib/Helper.h @@ -0,0 +1,643 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Array2D.h" +#include "Graph.h" + +#define BIT(x) (1 << (x)) + +struct Helper +{ + template + 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 + static T Sum(const S& container) + { + return std::accumulate(container.begin(), container.end(), (T)0, std::plus()); + } + + template + static T Sum(const Container& container, S func) + { + return std::accumulate(container.begin(), container.end(), (T)0, func); + } + + template + static T Product(const S& container) + { + return std::accumulate(container.begin(), container.end(), (T)1, std::multiplies()); + } + + template + static T Product(const Container& container, S func) + { + return std::accumulate(container.begin(), container.end(), (T)1, func); + } + + template + 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 + 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 + static T Sign(const T& t) + { + return t > 0 ? 1 : t < 0 ? -1 : 0; + } + + template + static std::set GetUnion(const std::vector>& list) + { + std::set uniq; + for (auto l : list) + { + for (auto t : l) + { + uniq.emplace(t); + } + } + return uniq; + } + + template + static std::set GetIntersection(const std::vector>& list) + { + std::set 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 + static std::set GetIntersection(const std::set& s1, const std::set& s2) + { + std::set 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 + static bool IsSubset(const std::set& smallSet, const std::set& 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>(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 + static int AStar(const Input& input, const std::vector& initial, Heuristic heuristic, Branch branch, Goal goal) + { + std::map visited; + std::multimap> open; + for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair{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{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>(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 + static std::pair> AStarPath( + const Input& input, const std::vector& initial, Heuristic heuristic, Branch branch, Goal goal) + { + std::map visited; + std::multimap>> open; + for (int i = 0; i < initial.size(); i++) open.emplace(0, std::pair>{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>{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>(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 + static std::pair> AStarPath( + const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal) + { + return AStarPath(input, std::vector{initial}, heuristic, branch, goal); + } + + // Input - Puzzle input + // State - State of the path finder + // Branch - std::vector>(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 + static std::pair> DijkstrasPath(const Input& input, + const std::vector& 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>(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 + static std::pair> DijkstrasPath(const Input& input, + const State& initial, + Branch branch, + Goal goal) + { + return AStarPath(input, std::vector{initial}, [](const Input&, const State&) { return 0; }, branch, goal); + } + + // Input - Puzzle input + // State - State of the path finder + // Branch - std::vector>(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 + static std::set DijkstrasAllVisited(const Input& input, const State& initial, Branch branch, Goal goal) + { + std::set totalVisited; + int bestCost = std::numeric_limits::max(); + std::map visited; + std::multimap> 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>(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 + static int AStar(const Input& input, const State& initial, Heuristic heuristic, Branch branch, Goal goal) + { + return AStar(input, std::vector{initial}, heuristic, branch, goal); + } + + // Input - Puzzle input + // State - State of the path finder + // Branch - std::vector>(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 + static int Dijkstras(const Input& input, const std::vector& 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>(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 + static int Dijkstras(const Input& input, const State& initial, Branch branch, Goal goal) + { + return AStar(input, std::vector{initial}, [](const Input&, const State&) { return 0; }, branch, goal); + } + + template + 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 + static std::vector Repeat(const std::vector& vec, int count) + { + std::vector ret; + for (int i = 0; i < count; i++) + { + for (const auto& t : vec) + { + ret.emplace_back(t); + } + } + return ret; + } + + static std::vector GetAllRegexMatches(const std::string& str, const std::string& regex) + { + std::vector 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 GetNeighborDirections() + { + return std::vector{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& mods, const std::vector& 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 + 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 + static int TSP(const Graph& graph, + const std::set& nodesLeft, + const std::vector& order, + Compare compare, + Eval eval) + { + if (nodesLeft.empty()) + { + return eval(graph, order); + } + + bool hasSet = false; + Value tsp; + + for (auto& node : nodesLeft) + { + std::set newNodesLeft = nodesLeft; + newNodesLeft.erase(node); + std::vector 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&) + // Given the graph and order of the TSP, what is the total cost + template + static int TSP(const Graph& graph, const Key& start, Compare compare, Eval eval) + { + std::set nodes = graph.GetNodes(); + nodes.erase(nodes.find(start)); + return TSP(graph, nodes, std::vector{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&) + // Given the graph and order of the TSP, what is the total cost + template + static int TSP(const Graph& graph, Compare compare, Eval eval) + { + return TSP(graph, graph.GetNodes(), std::vector{}, compare, eval); + } +}; + +template +static std::ostream& operator<<(std::ostream& stream, const std::vector& container) +{ + return Helper::Print(stream, container); +} + +template +static std::ostream& operator<<(std::ostream& stream, const std::set& container) +{ + return Helper::Print(stream, container); +} + +template +static std::ostream& operator<<(std::ostream& stream, const std::map& container) +{ + stream << "{"; + int i = 0; + for (const auto& [t, s] : container) + { + if (i != 0) + stream << ", "; + stream << t << " => " << s; + i++; + } + stream << "}"; + return stream; +} + +template +static std::ostream& operator<<(std::ostream& stream, const std::pair& pair) +{ + return stream << "(" << pair.first << ", " << pair.second << ")"; +} diff --git a/src/common/lib/Input.h b/src/common/lib/Input.h new file mode 100644 index 0000000..336d853 --- /dev/null +++ b/src/common/lib/Input.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include +#include + +#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 ReadArray2D(std::istream& input) + { + std::vector 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(width, height, data); + } + + static Array2D ReadDigitsAsArray2D(std::istream& input) + { + std::vector 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(width, height, data); + } + + static std::vector ReadInts(std::istream& input) + { + return ReadInts(input, '\n'); + } + + static std::vector ReadInt64s(std::istream& input) + { + return ReadInt64s(input, '\n'); + } + + static std::vector ReadInts(std::istream& input, char delimiter) + { + std::vector ints; + std::string str; + while (getline(input, str, delimiter)) ints.emplace_back(std::atoi(str.c_str())); + return ints; + } + + static std::vector ReadInt64s(std::istream& input, char delimiter) + { + std::vector ints; + std::string str; + while (getline(input, str, delimiter)) ints.emplace_back(std::atoll(str.c_str())); + return ints; + } + + static std::vector ReadLines(std::istream& input) + { + std::vector 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; + } +}; diff --git a/src/common/lib/Json.cpp b/src/common/lib/Json.cpp new file mode 100644 index 0000000..2831ce2 --- /dev/null +++ b/src/common/lib/Json.cpp @@ -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& 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(); + 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(); + 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(true); + return true; + } + else if (JsonUtil::IsKeyword(str, pos, "false")) + { + json.value.emplace(false); + return true; + } + else if (JsonUtil::IsKeyword(str, pos, "null")) + { + json.value.emplace(); + 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(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(i); + return true; + } + return false; +} \ No newline at end of file diff --git a/src/common/lib/Json.h b/src/common/lib/Json.h new file mode 100644 index 0000000..569e740 --- /dev/null +++ b/src/common/lib/Json.h @@ -0,0 +1,415 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +class Json; + +class JsonObject +{ +private: + std::map 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& 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::iterator begin() + { + return values.begin(); + } + + std::map::iterator end() + { + return values.end(); + } + + std::map::const_iterator begin() const + { + return values.cbegin(); + } + + std::map::const_iterator end() const + { + return values.cend(); + } +}; + +class JsonArray +{ +private: + std::vector 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::iterator begin() + { + return values.begin(); + } + + std::vector::iterator end() + { + return values.end(); + } + + std::vector::const_iterator begin() const + { + return values.cbegin(); + } + + std::vector::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; + +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(value); + } + static JsonObject ref; + return ref; + } + + JsonObject& GetJsonObject() + { + if (IsJsonObject()) + { + return std::get(value); + } + static JsonObject ref; + return ref; + } + + const JsonArray& GetJsonArray() const + { + if (IsJsonArray()) + { + return std::get(value); + } + static JsonArray ref; + return ref; + } + + JsonArray& GetJsonArray() + { + if (IsJsonArray()) + { + return std::get(value); + } + static JsonArray ref; + return ref; + } + + const std::string& GetString() const + { + if (IsString()) + { + return std::get(value); + } + static std::string ref; + return ref; + } + + std::string& GetString() + { + if (IsString()) + { + return std::get(value); + } + static std::string ref; + return ref; + } + + float GetFloat() const + { + if (IsFloat()) + { + return (float)std::get(value); + } + return 0.0f; + } + + double GetDouble() const + { + if (IsString()) + { + return std::get(value); + } + return 0.0; + } + + uint64_t GetUInt64() const + { + if (IsInt()) + { + return std::get(value); + } + return 0; + } + + int64_t GetInt64() const + { + if (IsInt()) + { + return (int64_t)std::get(value); + } + return 0; + } + + int32_t GetUInt32() const + { + if (IsInt()) + { + return (uint32_t)std::get(value); + } + return 0; + } + + int32_t GetInt32() const + { + if (IsInt()) + { + return (int32_t)std::get(value); + } + return 0; + } + + uint16_t GetUInt16() const + { + if (IsInt()) + { + return (uint16_t)std::get(value); + } + return 0; + } + + int16_t GetInt16() const + { + if (IsInt()) + { + return (int16_t)std::get(value); + } + return 0; + } + + uint8_t GetUInt8() const + { + if (IsInt()) + { + return (uint8_t)std::get(value); + } + return 0; + } + + int8_t GetInt8() const + { + if (IsInt()) + { + return (int8_t)std::get(value); + } + return 0; + } + + bool GetBool() const + { + if (IsBool()) + { + return std::get(value); + } + return false; + } + + bool IsJsonObject() const + { + return std::holds_alternative(value); + } + + bool IsJsonArray() const + { + return std::holds_alternative(value); + } + + bool IsString() const + { + return std::holds_alternative(value); + } + + bool IsFloat() const + { + return std::holds_alternative(value); + } + + bool IsInt() const + { + return std::holds_alternative(value); + } + + bool IsBool() const + { + return std::holds_alternative(value); + } + + bool IsNull() const + { + return std::holds_alternative(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 FromString(const std::string& str) + { + uint64_t pos = 0; + Json json; + bool res = Read(str, pos, json); + return {res, json}; + } + + static std::pair FromStream(std::istream& stream) + { + std::string s(std::istreambuf_iterator(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); +}; diff --git a/src/common/lib/JsonUtil.cpp b/src/common/lib/JsonUtil.cpp new file mode 100644 index 0000000..3c28380 --- /dev/null +++ b/src/common/lib/JsonUtil.cpp @@ -0,0 +1,115 @@ +#include "JsonUtil.h" + +#include +#include + +#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(str.begin() + stringStart, str.begin() + pos - 1); + return true; +} diff --git a/src/common/lib/JsonUtil.h b/src/common/lib/JsonUtil.h new file mode 100644 index 0000000..a4f2ca5 --- /dev/null +++ b/src/common/lib/JsonUtil.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include + +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); +}; \ No newline at end of file diff --git a/src/common/lib/Mapper.h b/src/common/lib/Mapper.h new file mode 100644 index 0000000..9e851e1 --- /dev/null +++ b/src/common/lib/Mapper.h @@ -0,0 +1,75 @@ +#pragma once + +#include + +template +struct Mapper +{ + std::map map1; + std::map map2; + + Mapper() + { + } + + Mapper(const std::map& 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>& 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; + } +}; diff --git a/src/common/lib/Math.h b/src/common/lib/Math.h new file mode 100644 index 0000000..41bc33d --- /dev/null +++ b/src/common/lib/Math.h @@ -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; + } +}; diff --git a/src/common/lib/Timer.h b/src/common/lib/Timer.h new file mode 100644 index 0000000..8ca9533 --- /dev/null +++ b/src/common/lib/Timer.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +class Timer +{ +private: + using Time = std::chrono::steady_clock; + using TimeStamp = std::chrono::time_point; + using Duration = std::chrono::duration; + + 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(); + } +}; diff --git a/src/common/main.cpp b/src/common/main.cpp new file mode 100644 index 0000000..e905385 --- /dev/null +++ b/src/common/main.cpp @@ -0,0 +1,64 @@ +#include +#include +#include + +#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; + } +}