├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── data └── sample │ ├── day0_input.txt │ ├── test_cases │ ├── day0_test1.txt │ ├── day0_test2.txt │ ├── day0_test3.txt │ └── day0_test4.txt │ └── test_index_0.txt ├── include ├── common │ └── constants.h ├── runner │ ├── aoc_test.h │ ├── aoc_tests.h │ └── file_utils.h └── solutions │ ├── aoc_day.h │ ├── aoc_day_0.h │ └── aoc_days.h ├── mkdirs.sh ├── src ├── aoc.cpp ├── runner │ ├── aoc_test.cpp │ ├── aoc_tests.cpp │ └── file_utils.cpp └── solutions │ ├── aoc_day.cpp │ ├── aoc_day_0.cpp │ └── aoc_days.cpp └── testing.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Brian Cooper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEBUG= 2 | #Uncomment the below line to dispaly the runner debug 3 | #DEBUG+= -DDEBUG_RUNNER 4 | #Template for other debug flags to be added later 5 | #DEBUG+- -DDEBUG_OTHER 6 | 7 | #If adding another include directory, be sure to add it here 8 | CPPFLAGS=-g ${DEBUG} -Iinclude/common -Iinclude/runner -Iinclude/solutions 9 | 10 | .DEFAULT_GOAL := all 11 | 12 | # Runner library; contains a lot of common code for file parsing and handling test files 13 | build/runner/aoc_test.o: src/runner/aoc_test.cpp \ 14 | include/runner/aoc_test.h \ 15 | include/common/constants.h 16 | g++ ${CPPFLAGS} -o build/runner/aoc_test.o -c src/runner/aoc_test.cpp 17 | 18 | build/runner/aoc_tests.o: src/runner/aoc_tests.cpp \ 19 | include/runner/aoc_tests.h \ 20 | include/runner/aoc_test.h \ 21 | include/common/constants.h 22 | g++ ${CPPFLAGS} -o build/runner/aoc_tests.o -c src/runner/aoc_tests.cpp 23 | 24 | build/runner/file_utils.o: src/runner/file_utils.cpp \ 25 | include/runner/file_utils.h \ 26 | include/common/constants.h 27 | g++ ${CPPFLAGS} -o build/runner/file_utils.o -c src/runner/file_utils.cpp 28 | 29 | bin/lib/librunner.a: build/runner/aoc_test.o \ 30 | build/runner/aoc_tests.o \ 31 | build/runner/file_utils.o 32 | ar rcs bin/lib/librunner.a build/runner/aoc_test.o build/runner/aoc_tests.o build/runner/file_utils.o 33 | 34 | # Solutions - These are the programs for the daily solutions 35 | build/solutions/aoc_day.o: src/solutions/aoc_day.cpp \ 36 | include/solutions/aoc_day.h \ 37 | include/common/constants.h 38 | g++ ${CPPFLAGS} -o build/solutions/aoc_day.o -c src/solutions/aoc_day.cpp 39 | 40 | build/solutions/aoc_days.o: src/solutions/aoc_days.cpp \ 41 | include/solutions/aoc_days.h \ 42 | include/solutions/aoc_day.h \ 43 | include/common/constants.h 44 | g++ ${CPPFLAGS} -o build/solutions/aoc_days.o -c src/solutions/aoc_days.cpp 45 | 46 | #Generic line to compile a daily solution. 47 | #Be sure to add the .o file to the libsoluations.a target 48 | build/solutions/aoc_day_0.o: src/solutions/aoc_day_0.cpp \ 49 | include/solutions/aoc_day_0.h \ 50 | include/solutions/aoc_day.h \ 51 | include/common/constants.h 52 | g++ ${CPPFLAGS} -o build/solutions/aoc_day_0.o -c src/solutions/aoc_day_0.cpp 53 | 54 | bin/lib/libsolutions.a: build/solutions/aoc_day.o \ 55 | build/solutions/aoc_days.o \ 56 | build/solutions/aoc_day_0.o 57 | ar rcs bin/lib/libsolutions.a build/solutions/aoc_day.o build/solutions/aoc_days.o build/solutions/aoc_day_0.o 58 | 59 | # The aoc executable 60 | build/aoc.o: src/aoc.cpp \ 61 | include/solutions/aoc_days.h \ 62 | include/runner/aoc_tests.h \ 63 | include/runner/file_utils.h \ 64 | include/common/constants.h 65 | g++ ${CPPFLAGS} -o build/aoc.o -c src/aoc.cpp 66 | 67 | #If adding a new library, be sure to add it in the correct order in the compile line 68 | bin/aoc: build/aoc.o \ 69 | bin/lib/librunner.a \ 70 | bin/lib/libsolutions.a 71 | g++ ${CPPFLAGS} -o bin/aoc build/aoc.o -Lbin/lib -lsolutions -lrunner 72 | 73 | clean: 74 | rm -f build/runner/aoc_test.o \ 75 | build/runner/aoc_tests.o \ 76 | build/runner/file_utils.o \ 77 | build/solutions/aoc_day.o \ 78 | build/solutions/aoc_day_0.o \ 79 | build/solutions/aoc_days.o \ 80 | build/aoc.o \ 81 | bin/lib/librunner.a \ 82 | bin/lib/libsolutions.a \ 83 | bin/aoc 84 | 85 | all: build/runner/aoc_test.o \ 86 | build/runner/aoc_tests.o \ 87 | build/runner/file_utils.o \ 88 | build/solutions/aoc_day.o \ 89 | build/solutions/aoc_day_0.o \ 90 | build/solutions/aoc_days.o \ 91 | build/aoc.o \ 92 | bin/lib/librunner.a \ 93 | bin/lib/libsolutions.a \ 94 | bin/aoc 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # advent-of-code-cpp-starter 2 | This is a simple C++ runner for doing Advent-of-Code challenges. It's designed to have one executable which is given parameters for the day and part, and then it will run that particular solution. 3 | 4 | It's designed for C++ under Linux. I've tested it with CentOS 8 and GCC version 8.3.1. 5 | 6 | ## Building the starter: 7 | After checking out the project, it's just 2 commands to build 8 | 9 | ./mkdirs.sh 10 | make 11 | 12 | `mkdirs.sh` will create the bin and build directories and their subdirectories. It only needs to be run the first time. 13 | 14 | ### Working on a Day's Solution 15 | 16 | The starter comes with a Day 0 implementation, which is based on Advent of Code 2018, day 1, part 1. The part 1 solution matches the problem, and the part 2 solution the same as part 1, except it gives the negation of the part 1 result for a solution. Looking at [`aoc_day_0.h`](include/solutions/aoc_day_0.h), [`/aoc_day_0.cpp`](src/solutions/aoc_day_0.cpp), and [`aoc_days.cpp`](src/solutions/aoc_days.cpp) will show all the coding needed to hook in a new day. 17 | 18 | Each day's solution will inherit from the AocDay class (include/solutions/aoc_day.h) as is done in include/solutions/aoc_day_0.h . 19 | 20 | There are two functions to override in the child class: 21 | 22 | virtual string part1(string filename, vector extra_args); 23 | virtual string part2(string filename, vector extra_args); 24 | 25 | The base AocDay class has default implementations of these functions, so you don't even have to define a part2 function until after part1 is done, if you'd prefer. 26 | 27 | Each function takes two parameters - the filename for the input file and a vector of extra arguments, which I'll describe below. 28 | 29 | The return value is a string for the solution. *"Why a string and not an int/long?"* you might ask. Although Advent of code 2019 had only numeric solutions, I found that in 2018 there were times when the solution was non-numeric (day 7 part 1, day 13 parts 1 and 2). So, this function returns a string back to the driver program. 30 | 31 | ### Extra arguments 32 | The parameter of `extra_args` is useful to prevent code changes for hard-coding limits or other constants. For example, let's say the test cases presented in the problem description show a result after 10 iterations, but then the actual answer is supposed to run 1000000 times. You can pass the `10` or `1000000` value as an extra arguent instead of having to change a constant and recompile. These are also passed in as strings, which can then be converted to ints/longs/whatever as needed. 33 | 34 | ### Makefile changes 35 | I'll preface this by saying that I don't like dealing with Makefiles. I'm sure I could make some of this prettier, but it is what it is. 36 | 37 | The Makefile for this compiles a couple of libraries - librunner.a for some of the basic test runner functionality, and libsolutions.a for the daily solutions. As things get more complex, I'm sure more libraries will get added. If you want to see what I mean, there's a more complex example in my intCode repo (https://github.com/bcooperstl/intCode). 38 | 39 | For each day's solution, the Makefile file will need to be modified to add in a .o file target that corresponds to the .cpp file for that day. That .o file must also be added to libsolutions.a . 40 | 41 | The final executable is built as bin/aoc 42 | 43 | ### Useful-ish Helper Functions 44 | There are some helper functions in the `FileUtils` class to read in and parse an input file. 45 | 46 | `bool read_as_list_of_strings(string filename, vector & lines)` - Read the file given by `filename` and return the lines of the file in the `lines` vector. 47 | `bool read_as_list_of_split_strings(string filename, vector> & split_strings, char delimiter, char quote_char, char comment_char)` - Read the file given by filename, return it as a list of list of strings in the `split_strings` vector. `delimiter` identfies a delimiter to identify how to split the strings. `quote_char` gives a way to allow the delimiter to appear in a string by quoting around it. `comment_char` allows for a line to be skipped from the output if it starts with this character. This probably sounds like overkill, but I use a bunch of this with my test file format. See [testing.md](testing.md) for an example and more details. 48 | `bool read_as_list_of_split_longs(string filename, vector> & split_longs, char delimiter, char quote_char, char comment_char)` - Same as `read_as_list_of_split_strings` but with longs. Think of it as an easy way to read in an intcode program from 2019, if you will. 49 | 50 | 51 | ## Running a solution 52 | There are three modes to run this program as shown in the usage. This section describes how to run one input file through. The [testing.md](testing.md) file describes the two other modes. 53 | The command line is: 54 | 55 | bin/aoc -d day -p part -f filename [extra_args...] 56 | 57 | Everything should be straight forward - give it the day, part (1 or 2), input file, and optionally any extra arguments. It'll spit out the result or tell you if there's an error. 58 | 59 | For example: 60 | 61 | [brian@dev1 advent-of-code-cpp-starter]$ bin/aoc -d 0 -p 1 -f data/sample/day0_input.txt 62 | ***Day 0 Part 1 for file data/sample/day0_input.txt has result 569 63 | -------------------------------------------------------------------------------- /data/sample/day0_input.txt: -------------------------------------------------------------------------------- 1 | +2 2 | +11 3 | +4 4 | -16 5 | -4 6 | +15 7 | +19 8 | -8 9 | -17 10 | +12 11 | +8 12 | +8 13 | +18 14 | -10 15 | -17 16 | -6 17 | +10 18 | +10 19 | +19 20 | +11 21 | +12 22 | -17 23 | +4 24 | -1 25 | +12 26 | +6 27 | +13 28 | +14 29 | +6 30 | +19 31 | +2 32 | +19 33 | -7 34 | -15 35 | +6 36 | -12 37 | -15 38 | -18 39 | +17 40 | +8 41 | +4 42 | +14 43 | +1 44 | +15 45 | -6 46 | -6 47 | +13 48 | +9 49 | -2 50 | +1 51 | +15 52 | -12 53 | +18 54 | +7 55 | -14 56 | +15 57 | -16 58 | +6 59 | -12 60 | -11 61 | -12 62 | -1 63 | -15 64 | -8 65 | -18 66 | -15 67 | +11 68 | +13 69 | -7 70 | +15 71 | +19 72 | +2 73 | +17 74 | -9 75 | -15 76 | -11 77 | -8 78 | +16 79 | +17 80 | -6 81 | +13 82 | +16 83 | -20 84 | +19 85 | +8 86 | +14 87 | +5 88 | +11 89 | -5 90 | +6 91 | +10 92 | +3 93 | -7 94 | -5 95 | -14 96 | +9 97 | +6 98 | +13 99 | +4 100 | -3 101 | -13 102 | -6 103 | -18 104 | -23 105 | -2 106 | +16 107 | +12 108 | -4 109 | +12 110 | +3 111 | -4 112 | +9 113 | -14 114 | +1 115 | -17 116 | -21 117 | +11 118 | -8 119 | +7 120 | -16 121 | -20 122 | -3 123 | -10 124 | -17 125 | -11 126 | +6 127 | -15 128 | -11 129 | +17 130 | +12 131 | -13 132 | -11 133 | -12 134 | -18 135 | -1 136 | +10 137 | -16 138 | -1 139 | -8 140 | +18 141 | +4 142 | +2 143 | -11 144 | -18 145 | +8 146 | +18 147 | -17 148 | +7 149 | +3 150 | +17 151 | +7 152 | +1 153 | -16 154 | +10 155 | -6 156 | -10 157 | +31 158 | -5 159 | -14 160 | +15 161 | +19 162 | +6 163 | -1 164 | +17 165 | -4 166 | +16 167 | -6 168 | +9 169 | -26 170 | -18 171 | +17 172 | -9 173 | -5 174 | -19 175 | +2 176 | +13 177 | +41 178 | -18 179 | +10 180 | -26 181 | -12 182 | +4 183 | -3 184 | -23 185 | -9 186 | -29 187 | -13 188 | +15 189 | +4 190 | -21 191 | +14 192 | +23 193 | -24 194 | -4 195 | -12 196 | -8 197 | -18 198 | -16 199 | -14 200 | +17 201 | -8 202 | -16 203 | +13 204 | -15 205 | +7 206 | +9 207 | -17 208 | -4 209 | -7 210 | -12 211 | +4 212 | +10 213 | -13 214 | -6 215 | -8 216 | +10 217 | +1 218 | +9 219 | -15 220 | +14 221 | -15 222 | +11 223 | -18 224 | -18 225 | +2 226 | +10 227 | -8 228 | -10 229 | +5 230 | -4 231 | +12 232 | -4 233 | -2 234 | +15 235 | +17 236 | +16 237 | +19 238 | +17 239 | +13 240 | +8 241 | -10 242 | +13 243 | +8 244 | -13 245 | -14 246 | +5 247 | -3 248 | -15 249 | -16 250 | -9 251 | -5 252 | -10 253 | +8 254 | -2 255 | +1 256 | -14 257 | -7 258 | -3 259 | -12 260 | -3 261 | +10 262 | -18 263 | +22 264 | +8 265 | -13 266 | -3 267 | -18 268 | -12 269 | -9 270 | +1 271 | -10 272 | +8 273 | -13 274 | -12 275 | -9 276 | -2 277 | +12 278 | -14 279 | +8 280 | -19 281 | -6 282 | -11 283 | +12 284 | +15 285 | +17 286 | +7 287 | -14 288 | +11 289 | +9 290 | -10 291 | -21 292 | +3 293 | -19 294 | -6 295 | -6 296 | +7 297 | +15 298 | -13 299 | -8 300 | +10 301 | -18 302 | +15 303 | -9 304 | -19 305 | +5 306 | +11 307 | -7 308 | -14 309 | +7 310 | +9 311 | +6 312 | +6 313 | +13 314 | +8 315 | +13 316 | +19 317 | -8 318 | -3 319 | -4 320 | -18 321 | +12 322 | -2 323 | +18 324 | +13 325 | -1 326 | +17 327 | -7 328 | +12 329 | +8 330 | +2 331 | +19 332 | -11 333 | +22 334 | +14 335 | +9 336 | -12 337 | +24 338 | +1 339 | +5 340 | +20 341 | -18 342 | +17 343 | -9 344 | +12 345 | -4 346 | -15 347 | -10 348 | +4 349 | -7 350 | +11 351 | +13 352 | +10 353 | +21 354 | -14 355 | -1 356 | -13 357 | -6 358 | +14 359 | +7 360 | +7 361 | +10 362 | +11 363 | -22 364 | +6 365 | +17 366 | +26 367 | -7 368 | +8 369 | -5 370 | +29 371 | -54 372 | -16 373 | +13 374 | +10 375 | -1 376 | -23 377 | +12 378 | -20 379 | -20 380 | -27 381 | +1 382 | -2 383 | -4 384 | -26 385 | -2 386 | -36 387 | -18 388 | +14 389 | -3 390 | -18 391 | -1 392 | +17 393 | +15 394 | -12 395 | +9 396 | +1 397 | +23 398 | -22 399 | -6 400 | -20 401 | +19 402 | +3 403 | -17 404 | -6 405 | -41 406 | -5 407 | -5 408 | -5 409 | -19 410 | -9 411 | -13 412 | +3 413 | +2 414 | +10 415 | +5 416 | -6 417 | -18 418 | -19 419 | +10 420 | +17 421 | +6 422 | -17 423 | +5 424 | -7 425 | +10 426 | +6 427 | -5 428 | -14 429 | +1 430 | -17 431 | -19 432 | +7 433 | +14 434 | -19 435 | +6 436 | +12 437 | -16 438 | +13 439 | -1 440 | +16 441 | +14 442 | -13 443 | -4 444 | +2 445 | -17 446 | -1 447 | -7 448 | -5 449 | -3 450 | -14 451 | -3 452 | +5 453 | +11 454 | -12 455 | -5 456 | -14 457 | +2 458 | +2 459 | -15 460 | +12 461 | -3 462 | +15 463 | -2 464 | +10 465 | +7 466 | -16 467 | +12 468 | -15 469 | +1 470 | -3 471 | -4 472 | -19 473 | +7 474 | -5 475 | +22 476 | +8 477 | +17 478 | +16 479 | +15 480 | +11 481 | +3 482 | -16 483 | -10 484 | +7 485 | +12 486 | +16 487 | +13 488 | -10 489 | +15 490 | -1 491 | -15 492 | +22 493 | +1 494 | +3 495 | +20 496 | +24 497 | +12 498 | +17 499 | -19 500 | -12 501 | +7 502 | -11 503 | -19 504 | +4 505 | +4 506 | -21 507 | +22 508 | +1 509 | +45 510 | +3 511 | -9 512 | +41 513 | -1 514 | +94 515 | +2 516 | +7 517 | -37 518 | +3 519 | +217 520 | +13 521 | +10 522 | -14 523 | +5 524 | -3 525 | -14 526 | -26 527 | -39 528 | +80 529 | +33 530 | +9 531 | -13 532 | +7 533 | +7 534 | -13 535 | -14 536 | +1 537 | +37 538 | +33 539 | +7 540 | +14 541 | +1 542 | -5 543 | -11 544 | -8 545 | -12 546 | +4 547 | +14 548 | -17 549 | +1 550 | -10 551 | -20 552 | -25 553 | +27 554 | +31 555 | -8 556 | +1 557 | +53 558 | +22 559 | +9 560 | -14 561 | -49 562 | +13 563 | -52 564 | -64 565 | -100 566 | +34 567 | +198 568 | +605 569 | +76787 570 | -8 571 | +13 572 | +4 573 | -8 574 | +5 575 | +9 576 | -7 577 | -13 578 | -6 579 | -4 580 | -20 581 | +6 582 | +12 583 | -4 584 | +7 585 | +5 586 | -14 587 | +10 588 | +17 589 | +18 590 | +18 591 | +5 592 | +6 593 | -12 594 | -6 595 | -8 596 | -13 597 | -5 598 | -8 599 | -17 600 | +6 601 | +2 602 | +3 603 | +18 604 | -1 605 | +20 606 | +14 607 | -18 608 | +21 609 | -12 610 | +3 611 | +8 612 | +6 613 | +21 614 | -3 615 | +1 616 | +9 617 | +16 618 | +15 619 | -7 620 | +1 621 | -16 622 | +17 623 | -5 624 | -15 625 | -14 626 | +3 627 | +3 628 | -4 629 | +2 630 | +1 631 | +16 632 | +19 633 | +12 634 | -4 635 | +14 636 | -8 637 | +17 638 | -7 639 | -1 640 | -3 641 | -11 642 | -12 643 | +7 644 | +9 645 | -10 646 | -13 647 | +1 648 | -8 649 | +4 650 | -12 651 | +13 652 | -10 653 | -8 654 | -3 655 | -19 656 | -12 657 | +7 658 | +9 659 | -1 660 | -9 661 | -4 662 | -19 663 | -12 664 | +17 665 | -12 666 | +19 667 | +18 668 | -12 669 | -7 670 | -10 671 | +5 672 | -21 673 | +4 674 | -6 675 | +4 676 | -15 677 | -9 678 | +19 679 | -17 680 | +21 681 | +12 682 | +24 683 | +13 684 | +18 685 | -16 686 | -11 687 | +10 688 | +18 689 | +7 690 | +16 691 | -10 692 | -4 693 | -13 694 | +29 695 | -13 696 | -9 697 | +3 698 | -22 699 | -13 700 | +67 701 | -10 702 | +9 703 | +6 704 | +2 705 | +8 706 | +6 707 | +18 708 | -16 709 | -15 710 | +9 711 | +2 712 | +3 713 | -4 714 | +7 715 | +18 716 | +12 717 | -10 718 | -15 719 | +1 720 | +11 721 | -9 722 | -19 723 | +15 724 | +8 725 | +11 726 | +2 727 | +3 728 | +9 729 | +14 730 | -19 731 | +16 732 | -5 733 | -12 734 | +8 735 | +17 736 | +4 737 | +10 738 | -13 739 | +19 740 | +9 741 | -19 742 | -11 743 | +8 744 | +6 745 | +3 746 | +5 747 | -16 748 | +3 749 | +7 750 | +11 751 | -1 752 | +5 753 | -7 754 | -12 755 | +13 756 | -6 757 | +1 758 | +18 759 | +6 760 | +1 761 | +14 762 | -7 763 | -2 764 | -13 765 | +18 766 | -1 767 | +18 768 | -5 769 | +3 770 | -14 771 | -17 772 | +1 773 | +13 774 | +16 775 | +14 776 | +4 777 | -13 778 | -12 779 | -1 780 | +2 781 | +3 782 | +14 783 | -1 784 | -6 785 | +12 786 | +8 787 | -11 788 | +6 789 | -7 790 | +19 791 | -16 792 | +5 793 | +18 794 | +11 795 | -16 796 | -8 797 | -3 798 | +13 799 | +6 800 | +7 801 | -14 802 | +9 803 | +14 804 | -7 805 | +9 806 | -14 807 | +1 808 | -6 809 | +16 810 | -2 811 | -12 812 | -5 813 | -8 814 | +7 815 | -19 816 | +13 817 | +2 818 | +7 819 | +13 820 | -8 821 | +7 822 | +15 823 | +9 824 | +10 825 | -5 826 | +4 827 | +7 828 | -1 829 | +12 830 | +13 831 | +13 832 | +10 833 | +13 834 | +13 835 | -6 836 | +12 837 | +10 838 | -5 839 | +9 840 | +11 841 | -16 842 | +8 843 | -11 844 | -15 845 | -15 846 | +10 847 | -17 848 | +2 849 | -6 850 | -11 851 | -14 852 | -13 853 | -14 854 | -5 855 | -8 856 | -1 857 | -7 858 | -7 859 | +21 860 | -11 861 | -5 862 | +3 863 | +5 864 | -15 865 | +20 866 | +8 867 | -17 868 | +7 869 | -13 870 | +35 871 | +6 872 | -3 873 | -1 874 | -16 875 | +8 876 | -5 877 | -10 878 | +14 879 | +20 880 | +13 881 | +10 882 | -20 883 | -17 884 | +13 885 | -2 886 | +9 887 | +19 888 | -8 889 | +4 890 | -2 891 | +12 892 | -15 893 | +12 894 | -4 895 | +20 896 | -7 897 | +2 898 | -5 899 | -19 900 | -3 901 | -1 902 | +3 903 | +6 904 | +7 905 | -20 906 | +19 907 | +15 908 | +10 909 | +3 910 | -7 911 | +1 912 | +13 913 | +4 914 | +6 915 | +5 916 | +4 917 | +4 918 | +7 919 | -19 920 | +16 921 | +2 922 | +4 923 | -8 924 | -7 925 | +18 926 | +15 927 | +6 928 | +1 929 | -13 930 | +14 931 | +18 932 | -7 933 | +8 934 | -16 935 | -10 936 | +13 937 | -12 938 | +13 939 | +14 940 | +14 941 | -8 942 | -19 943 | +4 944 | -15 945 | -16 946 | -16 947 | -8 948 | +10 949 | +17 950 | -12 951 | +10 952 | +8 953 | +19 954 | -13 955 | +14 956 | +12 957 | +3 958 | -10 959 | +2 960 | +14 961 | +1 962 | -2 963 | +3 964 | +16 965 | -3 966 | -12 967 | +5 968 | +4 969 | -19 970 | +17 971 | -4 972 | -8 973 | +15 974 | -6 975 | +14 976 | +21 977 | -4 978 | -18 979 | +4 980 | -8 981 | +21 982 | -28 983 | +5 984 | -25 985 | -4 986 | -2 987 | +14 988 | -2 989 | -22 990 | -9 991 | -7 992 | -5 993 | -20 994 | -17 995 | -6 996 | +8 997 | -5 998 | -4 999 | -2 1000 | -15 1001 | -6 1002 | +13 1003 | +13 1004 | +13 1005 | -29 1006 | -16 1007 | +13 1008 | -38 1009 | +5 1010 | -16 1011 | +4 1012 | -15 1013 | +19 1014 | -31 1015 | +24 1016 | +6 1017 | -13 1018 | +20 1019 | -47 1020 | -6 1021 | +13 1022 | +14 1023 | -79 1024 | -14 1025 | +11 1026 | -4 1027 | +24 1028 | -18 1029 | -1 1030 | +20 1031 | -4 1032 | +24 1033 | -26 1034 | -25 1035 | -16 1036 | -6 1037 | -8 1038 | -7 1039 | +8 1040 | -77371 1041 | -------------------------------------------------------------------------------- /data/sample/test_cases/day0_test1.txt: -------------------------------------------------------------------------------- 1 | +1 2 | -2 3 | +3 4 | +1 5 | -------------------------------------------------------------------------------- /data/sample/test_cases/day0_test2.txt: -------------------------------------------------------------------------------- 1 | +1 2 | +1 3 | +1 4 | -------------------------------------------------------------------------------- /data/sample/test_cases/day0_test3.txt: -------------------------------------------------------------------------------- 1 | +1 2 | +1 3 | -2 4 | -------------------------------------------------------------------------------- /data/sample/test_cases/day0_test4.txt: -------------------------------------------------------------------------------- 1 | -1 2 | -2 3 | -3 4 | -------------------------------------------------------------------------------- /data/sample/test_index_0.txt: -------------------------------------------------------------------------------- 1 | #format is day,part,filename,expected result,any extra arguments (comma-separated) 2 | 0,1,test_cases/day0_test1.txt,3 3 | 0,1,test_cases/day0_test2.txt,3 4 | #This is a comment 5 | 0,1,test_cases/day0_test3.txt,0 6 | 0,1,test_cases/day0_test4.txt,-6 7 | 0,1,day0_input.txt,569 8 | 0,2,test_cases/day0_test1.txt,-3 9 | #The below test case shows that the answer can be quoted, if a comma is in the answer (happened in 2018) 10 | 0,2,test_cases/day0_test2.txt,'-3' 11 | 0,2,test_cases/day0_test3.txt,0 12 | #a,b,c are extra parms. Not worrying about them in this program, but showing how to do that 13 | #The extra parameters are useful when the sample will show a program runnign for 10 times, 14 | # but we really run it 1000000 times. This allows that value to be specified without recompiling 15 | 0,2,test_cases/day0_test4.txt,6,a,b,c 16 | 0,2,day0_input.txt,-569 17 | -------------------------------------------------------------------------------- /include/common/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONSTANTS_H__ 2 | #define __CONSTANTS_H__ 3 | 4 | // Test file format is a CSV file, using a single quote as the quote character and a # as the comment character 5 | #define TEST_INDEX_DELIM ',' 6 | #define TEST_INDEX_QUOTE '\'' 7 | #define TEST_INDEX_COMMENT '#' 8 | 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /include/runner/aoc_test.h: -------------------------------------------------------------------------------- 1 | #ifndef __AOC_TEST_H__ 2 | #define __AOC_TEST_H__ 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | class AocTest 10 | { 11 | private: 12 | int m_day; 13 | int m_part; 14 | string m_filename; 15 | string m_expected_result; 16 | vector m_extra_args; 17 | public: 18 | AocTest(int day, int part, string filename, string expected_result, vector extra_args); 19 | ~AocTest(); 20 | bool matches(int day, int part); 21 | bool compare_result(string actual_result); 22 | int get_day(); 23 | int get_part(); 24 | string get_filename(); 25 | string get_expected_result(); 26 | vector get_extra_args(); 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /include/runner/aoc_tests.h: -------------------------------------------------------------------------------- 1 | #ifndef __AOC_TESTS_H__ 2 | #define __AOC_TESTS_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "aoc_test.h" 8 | 9 | using namespace std; 10 | 11 | class AocTests 12 | { 13 | private: 14 | vector m_tests; 15 | string base_directory(string filename); 16 | public: 17 | AocTests(); 18 | ~AocTests(); 19 | bool load_tests(string filename); 20 | vector get_all_tests(); 21 | vector filter_tests(int day, int part); 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /include/runner/file_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILE_UTILS_H__ 2 | #define __FILE_UTILS_H__ 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | class FileUtils { 10 | private: 11 | vector split_line_to_strings(string input, char delimiter, char quote_char, char comment_char); 12 | public: 13 | bool read_as_list_of_strings(string filename, vector & lines); 14 | bool read_as_list_of_split_strings(string filename, vector> & split_strings, char delimiter, char quote_char, char comment_char); 15 | bool read_as_list_of_split_longs(string filename, vector> & split_longs, char delimiter, char quote_char, char comment); 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/solutions/aoc_day.h: -------------------------------------------------------------------------------- 1 | #ifndef __AOC_DAY_H__ 2 | #define __AOC_DAY_H__ 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | /* This is the superclass for all of the daily programs. 10 | * It will have two functions to override - one for part 1, and one for part 2 11 | * each of those functions takes two arguments 12 | * 1) the filename for the input file. 13 | 2) a vector of strings for extra arguments. This vector can be empty. 14 | */ 15 | 16 | class AocDay 17 | { 18 | protected: 19 | int m_day; 20 | public: 21 | AocDay(int day); 22 | ~AocDay(); 23 | virtual string part1(string filename, vector extra_args); 24 | virtual string part2(string filename, vector extra_args); 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/solutions/aoc_day_0.h: -------------------------------------------------------------------------------- 1 | #ifndef __AOC_DAY_0__ 2 | #define __AOC_DAY_0__ 3 | 4 | #include "aoc_day.h" 5 | 6 | class AocDay0 : public AocDay 7 | { 8 | private: 9 | vector read_input(string filename); 10 | public: 11 | AocDay0(); 12 | ~AocDay0(); 13 | string part1(string filename, vector extra_args); 14 | string part2(string filename, vector extra_args); 15 | }; 16 | 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/solutions/aoc_days.h: -------------------------------------------------------------------------------- 1 | #ifndef __AOC_DAYS_H__ 2 | #define __AOC_DAYS_H__ 3 | 4 | #include 5 | 6 | #include "aoc_day.h" 7 | 8 | using namespace std; 9 | 10 | class AocDays 11 | { 12 | protected: 13 | map m_days; 14 | public: 15 | AocDays(); 16 | ~AocDays(); 17 | AocDay * get_day(int day); 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /mkdirs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | mkdir -p build/runner build/solutions 4 | mkdir -p bin/lib 5 | -------------------------------------------------------------------------------- /src/aoc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // getopt 5 | 6 | #include "aoc_days.h" 7 | #include "aoc_tests.h" 8 | #include "file_utils.h" 9 | 10 | #define DASH_D 0x01 11 | #define DASH_P 0x02 12 | #define DASH_F 0x04 13 | #define DASH_T 0x08 14 | #define DASH_R 0x10 15 | 16 | #define OPTS_RUN_FILE (DASH_D | DASH_P | DASH_F) 17 | #define OPTS_RUN_TEST (DASH_D | DASH_P | DASH_T) 18 | #define OPTS_RUN_REGRESSION (DASH_R) 19 | 20 | void usage(string prog_name) 21 | { 22 | cerr << "Usage for " << prog_name << endl; 23 | cerr << " Run one file: " << prog_name << " -d day -p part -f filename [extra_args...]" << endl; 24 | cerr << " Run one day/part tests: " << prog_name << " -d day -p part -t test_index_filename" << endl; 25 | cerr << " Run full regression test: " << prog_name << " -r test_index_filename" << endl; 26 | } 27 | 28 | int main (int argc, char * argv[]) 29 | { 30 | AocDays days; 31 | AocTests tests; 32 | FileUtils fileutils; 33 | 34 | long day = 0; 35 | long part = 0; 36 | bool regression = false; 37 | string filename = ""; 38 | string test_filename = ""; 39 | string result = ""; 40 | vector extra_args; 41 | ostringstream test_summary; 42 | 43 | int given_opts = 0; 44 | int opt; 45 | 46 | // getopt parsing of command line parameters 47 | while ((opt = getopt(argc, argv, "d:p:f:t:r:")) != -1) 48 | { 49 | switch (opt) 50 | { 51 | case 'd': 52 | day = strtol(optarg, NULL, 10); 53 | given_opts |= DASH_D; 54 | break; 55 | case 'p': 56 | part = strtol(optarg, NULL, 10); 57 | given_opts |= DASH_P; 58 | break; 59 | case 'f': 60 | filename = string(optarg); 61 | given_opts |= DASH_F; 62 | break; 63 | case 't': 64 | test_filename = string(optarg); 65 | given_opts |= DASH_T; 66 | break; 67 | case 'r': 68 | test_filename = string(optarg); 69 | regression = true; 70 | given_opts |= DASH_R; 71 | break; 72 | default: 73 | cerr << "Invalid option " << opt << " given" << endl; 74 | usage(argv[0]); 75 | exit(8); 76 | break; 77 | } 78 | } 79 | 80 | // check for valid option combination and get any extra arguments if running from a file 81 | if (given_opts == OPTS_RUN_FILE) 82 | { 83 | for (int i=optind; ipart1(filename, extra_args); 107 | } 108 | else if (part == 2) 109 | { 110 | result = aoc_day->part2(filename, extra_args); 111 | } 112 | else 113 | { 114 | cerr << "Invalid part" << part << " specified!!!" << endl; 115 | exit(8); 116 | } 117 | cout << "***Day " << day << " Part " << part << " for file " << filename << " has result " << result << endl; 118 | } 119 | else // no filename; assume some type of testing mode 120 | { 121 | vector tests_to_run; 122 | if (!tests.load_tests(test_filename)) 123 | { 124 | cerr << "Error loading from test indext file " << test_filename << endl; 125 | exit(8); 126 | } 127 | if (regression) 128 | { 129 | cout << "Running full regression test for all days and parts!" << endl; 130 | tests_to_run = tests.get_all_tests(); 131 | } 132 | else 133 | { 134 | cout << "Running tests for day " << day << " part " << part << endl; 135 | tests_to_run = tests.filter_tests(day, part); 136 | } 137 | for (vector::iterator test_iter = tests_to_run.begin(); test_iter != tests_to_run.end(); ++test_iter) 138 | { 139 | AocTest test = *test_iter; 140 | AocDay * aoc_day = days.get_day(test.get_day()); 141 | if (!aoc_day) 142 | { 143 | cerr << "Program for day " << test.get_day() << " not found!!!" << endl; 144 | exit(8); 145 | } 146 | if (test.get_part() == 1) 147 | { 148 | result = aoc_day->part1(test.get_filename(), test.get_extra_args()); 149 | } 150 | else if (test.get_part() == 2) 151 | { 152 | result = aoc_day->part2(test.get_filename(), test.get_extra_args()); 153 | } 154 | else 155 | { 156 | cerr << "Invalid part" << test.get_part() << " specified!!!" << endl; 157 | exit(8); 158 | } 159 | if (result == test.get_expected_result()) 160 | { 161 | test_summary << "++Day " << test.get_day() << " Part " << test.get_part() << "-" << test.get_filename() << " passed with result " << result << endl; 162 | } 163 | else 164 | { 165 | test_summary << "--Day " << test.get_day() << " Part " << test.get_part() << "-" << test.get_filename() << " FAILED expected=" << test.get_expected_result() << " actual=" << result << endl; 166 | } 167 | } 168 | cout << test_summary.str(); 169 | } 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /src/runner/aoc_test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "aoc_test.h" 5 | 6 | using namespace std; 7 | 8 | AocTest::AocTest(int day, int part, string filename, string expected_result, vector extra_args):m_day(day),m_part(part),m_filename(filename),m_expected_result(expected_result),m_extra_args(extra_args) 9 | { 10 | } 11 | 12 | AocTest::~AocTest() 13 | { 14 | } 15 | 16 | bool AocTest::matches(int day, int part) 17 | { 18 | return (m_day == day) && (m_part == part); 19 | } 20 | 21 | bool AocTest::compare_result(string actual_result) 22 | { 23 | return m_expected_result == actual_result; 24 | } 25 | 26 | int AocTest::get_day() 27 | { 28 | return m_day; 29 | } 30 | 31 | int AocTest::get_part() 32 | { 33 | return m_part; 34 | } 35 | 36 | string AocTest::get_filename() 37 | { 38 | return m_filename; 39 | } 40 | 41 | string AocTest::get_expected_result() 42 | { 43 | return m_expected_result; 44 | } 45 | 46 | vector AocTest::get_extra_args() 47 | { 48 | return m_extra_args; 49 | } 50 | -------------------------------------------------------------------------------- /src/runner/aoc_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "aoc_tests.h" 5 | #include "aoc_test.h" 6 | #include "constants.h" 7 | #include "file_utils.h" 8 | 9 | using namespace std; 10 | 11 | AocTests::AocTests() 12 | { 13 | } 14 | 15 | AocTests::~AocTests() 16 | { 17 | } 18 | 19 | string AocTests::base_directory(string filename) 20 | { 21 | size_t found; 22 | found=filename.find_last_of("/\\"); 23 | if (found != string::npos) // found a directory character. return everything from the start through that character 24 | { 25 | return filename.substr(0,found+1); 26 | } 27 | else // no directory character found. just return an empty string to indicate the current directory 28 | { 29 | return ""; 30 | } 31 | } 32 | 33 | bool AocTests::load_tests(string filename) 34 | { 35 | vector> test_index_contents; 36 | string basedir = base_directory(filename); 37 | #ifdef DEBUG_RUNNER 38 | cout << "The base dicectory for test files is " << basedir << endl; 39 | #endif 40 | FileUtils fileutils; 41 | if (!fileutils.read_as_list_of_split_strings(filename, test_index_contents, TEST_INDEX_DELIM, TEST_INDEX_QUOTE, TEST_INDEX_COMMENT)) 42 | { 43 | cerr << "Error reading test index file " << filename << endl; 44 | return false; 45 | } 46 | 47 | for (vector>::iterator test_iter = test_index_contents.begin(); test_iter != test_index_contents.end(); ++test_iter) 48 | { 49 | vector test_parameters = *test_iter; 50 | // Skip a blank line 51 | if (test_parameters.size() == 1 && test_parameters[0].size() == 0) 52 | { 53 | continue; 54 | } 55 | 56 | vector::iterator parm_iter=test_parameters.begin(); 57 | // format is day,part,filename,expected[,...extra args] 58 | long day, part; 59 | string filename, full_filename, expected; 60 | vector extra_args; 61 | 62 | day = strtol((*parm_iter).c_str(), NULL, 10); 63 | ++parm_iter; 64 | 65 | part = strtol((*parm_iter).c_str(), NULL, 10); 66 | ++parm_iter; 67 | 68 | filename = *parm_iter; 69 | full_filename = basedir + filename; 70 | #ifdef DEBUG_RUNNER 71 | cout << "The full path to " << filename << " is " << full_filename << endl; 72 | #endif 73 | ++parm_iter; 74 | 75 | expected = *parm_iter; 76 | ++parm_iter; 77 | while (parm_iter != test_parameters.end()) 78 | { 79 | #ifdef DEBUG_RUNNER 80 | cout << "Adding extra parm " << *parm_iter << endl; 81 | #endif 82 | extra_args.push_back(*parm_iter); 83 | ++parm_iter; 84 | } 85 | m_tests.push_back(AocTest(day, part, full_filename, expected, extra_args)); 86 | } 87 | return true; 88 | } 89 | 90 | vector AocTests::get_all_tests() 91 | { 92 | return m_tests; 93 | } 94 | 95 | vector AocTests::filter_tests(int day, int part) 96 | { 97 | vector matching_tests; 98 | 99 | for (vector::iterator test_iter = m_tests.begin(); test_iter !=m_tests.end(); ++test_iter) 100 | { 101 | if ((*test_iter).matches(day, part)) 102 | { 103 | matching_tests.push_back(*test_iter); 104 | } 105 | } 106 | 107 | return matching_tests; 108 | } 109 | -------------------------------------------------------------------------------- /src/runner/file_utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "file_utils.h" 8 | 9 | using namespace std; 10 | 11 | // Really basic way to parse a line based on delimiters. 12 | // parameters: 13 | // input = the input string to split. assume newline is removed, or else it will be addded to the last item 14 | // delimiter = the value to split on 15 | // quote = an optional parameter - a quote character to indicate a that quoted sections will be used and ignore delimiters 16 | // comment_char = an optional parameter - if this is the first character in a line, that line is treated as a comment and skipped. 17 | vector FileUtils::split_line_to_strings(string input, char delimiter, char quote_char, char comment_char) 18 | { 19 | #ifdef DEBUG_RUNNER 20 | cout << "original input is [" << input << "]" << endl; 21 | #endif 22 | 23 | vector splits; 24 | 25 | char * pos = (char *)input.c_str(); 26 | 27 | if (comment_char && *pos == comment_char) 28 | { 29 | #ifdef DEBUG_RUNNER 30 | cout << "Comment line found" << endl; 31 | #endif 32 | return splits; 33 | } 34 | 35 | bool in_quote = false; 36 | ostringstream current; 37 | while (*pos != '\0') 38 | { 39 | if (quote_char && in_quote) 40 | { 41 | if (*pos == quote_char) 42 | { 43 | in_quote = false; 44 | #ifdef DEBUG_RUNNER 45 | cout << "Leaving quotes" << endl; 46 | #endif 47 | } 48 | else 49 | { 50 | current << (*pos); 51 | } 52 | } 53 | else 54 | { 55 | if (quote_char && *pos == quote_char) 56 | { 57 | in_quote = true; 58 | #ifdef DEBUG_RUNNER 59 | cout << "In quotes" << endl; 60 | #endif 61 | } 62 | else if (*pos == delimiter) 63 | { 64 | // ABCDE,ABCDE,ABCDE 65 | // first string goes from 0 to 4...construct as string(0,5). pos will be 5 for the comma. so 5-0=5 66 | // second string goes from 6 to 10...construct as string(6,5)...pos wil be 11 for the comma. so 11-6=5 67 | // third string goes from 12 to 16...construct as string(12,5) but need to do this out of loop as its the last string 68 | #ifdef DEBUG_RUNNER 69 | cout << "appending [" << current.str() << "] as a string" << endl; 70 | #endif 71 | splits.push_back(current.str()); 72 | current = ostringstream(); 73 | } 74 | else 75 | { 76 | current << (*pos); 77 | } 78 | } 79 | pos++; 80 | } 81 | // append the last string. pos will be pointed to the null terminator at 17, so string(12,5) would be pos(17)-start(12) 82 | #ifdef DEBUG_RUNNER 83 | cout << "appending [" << current.str() << "] as the last string" << endl; 84 | #endif 85 | splits.push_back(current.str()); 86 | return splits; 87 | } 88 | 89 | bool FileUtils::read_as_list_of_strings(string filename, vector & lines) 90 | { 91 | ifstream infile(filename); 92 | if (!infile) 93 | { 94 | cerr << "*****Error opening file " << filename << endl; 95 | return false; 96 | } 97 | string line; 98 | while (getline(infile, line)) 99 | { 100 | #ifdef DEBUG_RUNNER 101 | cout << "Read line [" << line << "] from file" << endl; 102 | #endif 103 | lines.push_back(line); 104 | } 105 | infile.close(); 106 | return true; 107 | } 108 | 109 | bool FileUtils::read_as_list_of_split_strings(string filename, vector> & split_strings, char delimiter, char quote_char, char comment_char) 110 | { 111 | vector lines; 112 | if (!read_as_list_of_strings(filename, lines)) 113 | { 114 | return false; 115 | } 116 | for (vector::iterator iter = lines.begin(); iter != lines.end(); ++iter) 117 | { 118 | vector results = split_line_to_strings(*iter, delimiter, quote_char, comment_char); 119 | if (results.size() > 0) 120 | split_strings.push_back(results); 121 | } 122 | return true; 123 | } 124 | 125 | bool FileUtils::read_as_list_of_split_longs(string filename, vector> & split_longs, char delimiter, char quote_char, char comment_char) 126 | { 127 | vector lines; 128 | if (!read_as_list_of_strings(filename, lines)) 129 | { 130 | return false; 131 | } 132 | for (vector::iterator iter = lines.begin(); iter != lines.end(); ++iter) 133 | { 134 | vector long_strings = split_line_to_strings(*iter, delimiter, quote_char, comment_char); 135 | vector longs; 136 | for (vector::iterator str_long_iter = long_strings.begin(); str_long_iter != long_strings.end(); ++str_long_iter) 137 | { 138 | string str_long = *str_long_iter; 139 | longs.push_back(strtol(str_long.c_str(), NULL, 10)); 140 | } 141 | split_longs.push_back(longs); 142 | } 143 | return true; 144 | } 145 | 146 | -------------------------------------------------------------------------------- /src/solutions/aoc_day.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "aoc_day.h" 6 | 7 | using namespace std; 8 | 9 | AocDay::AocDay(int day):m_day(day) 10 | { 11 | } 12 | 13 | AocDay::~AocDay() 14 | { 15 | } 16 | 17 | string AocDay::part1(string filename, vector extra_args) 18 | { 19 | cerr << "*****Part 1 implementation for day " << m_day << " is not defined*****" << endl; 20 | return ""; 21 | } 22 | 23 | string AocDay::part2(string filename, vector extra_args) 24 | { 25 | cerr << "*****Part 2 implementation for day " << m_day << " is not defined*****" << endl; 26 | return ""; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /src/solutions/aoc_day_0.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "aoc_day_0.h" 8 | #include "file_utils.h" 9 | 10 | using namespace std; 11 | 12 | AocDay0::AocDay0():AocDay(0) 13 | { 14 | } 15 | 16 | AocDay0::~AocDay0() 17 | { 18 | } 19 | 20 | vector AocDay0::read_input(string filename) 21 | { 22 | FileUtils fileutils; 23 | vector raw_lines; 24 | vector data; 25 | if (!fileutils.read_as_list_of_strings(filename, raw_lines)) 26 | { 27 | cerr << "Error reading in the data from " << filename << endl; 28 | return data; 29 | } 30 | for (vector::iterator iter = raw_lines.begin(); iter != raw_lines.end(); ++iter) 31 | { 32 | long l; 33 | string to_convert = *iter; 34 | l = strtol(to_convert.c_str(), NULL, 10); 35 | data.push_back(l); 36 | } 37 | return data; 38 | } 39 | 40 | string AocDay0::part1(string filename, vector extra_args) 41 | { 42 | vector data = read_input(filename); 43 | long sum = 0; 44 | for (vector::iterator iter = data.begin(); iter != data.end(); ++iter) 45 | { 46 | sum+=*iter; 47 | } 48 | ostringstream out; 49 | out << sum; 50 | return out.str(); 51 | } 52 | 53 | string AocDay0::part2(string filename, vector extra_args) 54 | { 55 | if (extra_args.size() > 0) 56 | { 57 | cout << "There are " << extra_args.size() << " extra arguments given:" << endl; 58 | for (vector::iterator iter = extra_args.begin(); iter != extra_args.end(); ++iter) 59 | { 60 | cout << "[" << *iter << "]" << endl; 61 | } 62 | } 63 | 64 | vector data = read_input(filename); 65 | long sum = 0; 66 | for (vector::iterator iter = data.begin(); iter != data.end(); ++iter) 67 | { 68 | sum-=*iter; 69 | } 70 | ostringstream out; 71 | out << sum; 72 | return out.str(); 73 | } 74 | -------------------------------------------------------------------------------- /src/solutions/aoc_days.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "aoc_days.h" 4 | #include "aoc_day.h" 5 | // includes for each day will go here 6 | #include "aoc_day_0.h" 7 | 8 | using namespace std; 9 | 10 | AocDays::AocDays() 11 | { 12 | // adding each member to the map goes here 13 | m_days[0]=new AocDay0(); 14 | } 15 | 16 | AocDays::~AocDays() 17 | { 18 | // delete all of the days solutions 19 | for (map::iterator days_iter = m_days.begin(); days_iter != m_days.end(); ++days_iter) 20 | { 21 | delete days_iter->second; 22 | } 23 | m_days.clear(); 24 | } 25 | 26 | AocDay * AocDays::get_day(int day) 27 | { 28 | return m_days[day]; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /testing.md: -------------------------------------------------------------------------------- 1 | # Testing Utility 2 | 3 | There is testing functionality built into this starter to allow tests to be easily run repeatedly. It will check for expected results, and alert the developer whether tests pass or fail. 4 | 5 | Test can either be run for a single day and part, or for all the days as a whole (i.e. regression testing). A common test index file will identify all the tests to be run. The developer can split the test index file into multiple files, if so desired. 6 | 7 | ## Test File Format 8 | The sample test file is given in `data/sample/test_index_0.txt` and only contains tests against the day 0 solution provided with the starter. 9 | 10 | The format for a line in the file is: 11 | 12 | `day,part,filename,expected result[,any extra arguments (comma-separated)]` 13 | 14 | ### Simple Test Case 15 | Looking at a simple test case: 16 | `0,1,test_cases/day0_test1.txt,3` 17 | 18 | The day is day 0. 19 | The part is part 1. 20 | The input file is `test_cases/day0_test1.txt`. This is the file containing the test data (i.e. what you would run through as a single file in non-testing mode). 21 | It is given relative from the location of the index file. So, the index file is in `data/sample/test_index_0.txt`, 22 | and the test case file is in `data/sample/test_cases/day0_test1.txt`, so just the relative part of `test_cases/day0_test1.txt` is given. 23 | The expected result is 3 24 | 25 | ### Adding extra arguments 26 | If you need to pass in extra arguments for the test, such as the number of iterations to run, those can be given after the expected result, in a comma-separeted list. 27 | This is the same as if you were adding extra arguments to the end of run-one-file invocation. 28 | 29 | So for: 30 | `0,2,test_cases/day0_test4.txt,6,a,b,c` 31 | It's day 0, part 2, input file is `test_cases/day0_test4.txt`, the expected result is 6. There are then three extra arguments `a`,`b`, and `c` which are passed through to the `AocDay0::part2(string filename, vector extra_args)` function in the `extra_args` list. 32 | 33 | ### Quoting a delimiter 34 | Let's say an answer required a comma. (i.e. 2018, Day 13). Rather than have to rewrite this to use a different delimiter, you can just put the element in single quotes. 35 | 36 | So for : 37 | `13,1,test_cases/day13_test1.txt,'6,4'` 38 | It's day 13, part 1, input file is `test_cases/day13_test1.txt`, the expected result is `6,4`. The comma is preserved in the expected answer 39 | 40 | These is no way to escape a single quote. I'm hoping that it isn't necessary, so it probably will be around day 16 :-) 41 | 42 | ### Comments 43 | You can put comments in the test file. Any line that starts with a `#` is treated as a comment. 44 | 45 | ### Sample test file 46 | Here's the test of the `data/sample/test_index_0.txt` file, showing how the file could be put together. 47 | 48 | #format is day,part,filename,expected result,any extra arguments (comma-separated) 49 | 0,1,test_cases/day0_test2.txt,3 50 | #This is a comment 51 | 0,1,test_cases/day0_test3.txt,0 52 | 0,1,test_cases/day0_test4.txt,-6 53 | 0,1,day0_input.txt,569 54 | 0,2,test_cases/day0_test1.txt,-3 55 | #The below test case shows that the answer can be quoted, if a comma is in the answer (happened in 2018) 56 | 0,2,test_cases/day0_test2.txt,'-3' 57 | 0,2,test_cases/day0_test3.txt,0 58 | #a,b,c are extra parms. Not worrying about them in this program, but showing how to do that 59 | #The extra parameters are useful when the sample will show a program runnign for 10 times, 60 | # but we really run it 1000000 times. This allows that value to be specified without recompiling 61 | 0,2,test_cases/day0_test4.txt,6,a,b,c 62 | 0,2,day0_input.txt,-569 63 | 64 | ## Running Tests 65 | There are two modes to run tests: one day/part combination or full regression test of all the test cases in the index file. 66 | 67 | ### One day/part Mode 68 | The command line is: 69 | 70 | bin/aoc -d day -p part -t test_index_filename 71 | 72 | Everything should be straight forward - give it the day, part (1 or 2), and the tess index file. It'll run all the matching test cases and output the results at the end of the program. 73 | There is a pass/fail count given at the end as well. 74 | For example: 75 | 76 | [brian@dev1 advent-of-code-cpp-starter]$ bin/aoc -d 0 -p 1 -t data/sample/test_index_0.txt 77 | Running tests for day 0 part 1 78 | ++Day 0 Part 1-data/sample/test_cases/day0_test1.txt passed with result 3 79 | ++Day 0 Part 1-data/sample/test_cases/day0_test2.txt passed with result 3 80 | --Day 0 Part 1-data/sample/test_cases/day0_test3.txt FAILED expected=0 actual=9 81 | ++Day 0 Part 1-data/sample/test_cases/day0_test4.txt passed with result -6 82 | ++Day 0 Part 1-data/sample/day0_input.txt passed with result 569 83 | 84 | 85 | ### Regression Mode 86 | The command line is: 87 | 88 | bin/aoc -r test_index_filename 89 | 90 | The only input is the filename, and it will run the tests and show the results at the end. There is a pass/fail count given at the end as well. 91 | For example: 92 | 93 | [brian@dev1 advent-of-code-cpp-starter]$ bin/aoc -r data/sample/test_index_0.txt 94 | Running full regression test for all days and parts! 95 | ++Day 0 Part 1-data/sample/test_cases/day0_test1.txt passed with result 3 96 | ++Day 0 Part 1-data/sample/test_cases/day0_test2.txt passed with result 3 97 | --Day 0 Part 1-data/sample/test_cases/day0_test3.txt FAILED expected=0 actual=9 98 | ++Day 0 Part 1-data/sample/test_cases/day0_test4.txt passed with result -6 99 | ++Day 0 Part 1-data/sample/day0_input.txt passed with result 569 100 | ++Day 0 Part 2-data/sample/test_cases/day0_test1.txt passed with result -3 101 | ++Day 0 Part 2-data/sample/test_cases/day0_test2.txt passed with result -3 102 | ++Day 0 Part 2-data/sample/test_cases/day0_test3.txt passed with result 0 103 | ++Day 0 Part 2-data/sample/test_cases/day0_test4.txt passed with result 6 104 | ++Day 0 Part 2-data/sample/day0_input.txt passed with result -569 105 | ++++++9 of 10 tests passed++++++ 106 | ------1 of 10 TESTS FAILED------ 107 | 108 | #### One suggestion 109 | If you're going to be using the regression mode, after you have a correct solution for a day/part, it would be a good idea to put that in the test_index file so it could be picked up by future regressions. 110 | 111 | At least once in 2019, I thought I had done some successful code surgery on my intCode portions and simple tests passed, but I had a bug that I didn't realize until I manually re-ran a prior day's actual full input file. 112 | 113 | 114 | --------------------------------------------------------------------------------