day2_part2.carbon 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. // https://adventofcode.com/2024/day/2
  5. import Core library "io";
  6. import library "day2_common";
  7. import library "io_utils";
  8. // A three-element sliding window of recent levels.
  9. class Window {
  10. fn Make() -> Window { return {.data = (0,0,0), .size = 0}; }
  11. fn Add[addr self: Self*](n: i32) {
  12. self->data[2] = self->data[1];
  13. self->data[1] = self->data[0];
  14. self->data[0] = n;
  15. ++self->size;
  16. }
  17. var data: [i32; 3];
  18. var size: i32;
  19. }
  20. // Determines whether a transition from `from` to `to` is safe.
  21. fn IsSafe(from: i32, to: i32, want_increase: bool) -> bool {
  22. var is_increase: bool = to > from;
  23. return IsSafeDelta(from, to) and is_increase == want_increase;
  24. }
  25. class ReportState {
  26. fn Make(increasing: bool) -> ReportState {
  27. return {.increasing = increasing, .bad_edges = 0,
  28. .removable_single_bad_edge = false,
  29. .removable_double_bad_edge = false};
  30. }
  31. fn OnAdd[addr self: Self*](window: Window) {
  32. if (window.size < 2) {
  33. return;
  34. }
  35. // We label the three most recent levels as `a b c`, with `c` the most
  36. // recent. We might not have an `a`.
  37. let b: i32 = window.data[1];
  38. let c: i32 = window.data[0];
  39. // Keep a count of the unsafe edges.
  40. let b_to_c: bool = IsSafe(window.data[1], window.data[0], self->increasing);
  41. if (not b_to_c) {
  42. ++self->bad_edges;
  43. // We have a removable single bad edge if the first edge is unsafe.
  44. if (window.size == 2) {
  45. self->removable_single_bad_edge = true;
  46. }
  47. }
  48. if (window.size >= 3) {
  49. let a: i32 = window.data[2];
  50. if (IsSafe(a, c, self->increasing)) {
  51. let lhs: bool = IsSafe(a, b, self->increasing);
  52. let rhs: bool = b_to_c;
  53. if (not lhs and not rhs) {
  54. // If a->b and b->c are both unsafe, but a->c is safe,
  55. // then we have a removable double bad edge.
  56. self->removable_double_bad_edge = true;
  57. } else if (not lhs or not rhs) {
  58. // If a->b or b->c is unsafe but a->c is safe, then we have a
  59. // removable single bad edge.
  60. self->removable_single_bad_edge = true;
  61. }
  62. }
  63. }
  64. }
  65. fn OnFinish[addr self: Self*](window: Window) {
  66. if (window.size >= 2 and
  67. not IsSafe(window.data[1], window.data[0], self->increasing)) {
  68. // We have a removable single bad edge if the last edge is unsafe.
  69. self->removable_single_bad_edge = true;
  70. }
  71. }
  72. fn IsSafeWithRemoval[self: Self]() -> bool {
  73. return self.bad_edges == 0 or
  74. (self.bad_edges == 1 and self.removable_single_bad_edge) or
  75. (self.bad_edges == 2 and self.removable_double_bad_edge);
  76. }
  77. var increasing: bool;
  78. var bad_edges: i32;
  79. var removable_single_bad_edge: bool;
  80. var removable_double_bad_edge: bool;
  81. }
  82. fn ReadAndCheckReport() -> bool {
  83. var window: Window = Window.Make();
  84. var increasing: ReportState = ReportState.Make(true);
  85. var decreasing: ReportState = ReportState.Make(false);
  86. var n: i32;
  87. while (ReadInt(&n)) {
  88. window.Add(n);
  89. increasing.OnAdd(window);
  90. decreasing.OnAdd(window);
  91. SkipSpaces();
  92. }
  93. increasing.OnFinish(window);
  94. decreasing.OnFinish(window);
  95. return window.size > 0 and
  96. (increasing.IsSafeWithRemoval() or decreasing.IsSafeWithRemoval());
  97. }
  98. fn Run() {
  99. var safe_reports: i32 = 0;
  100. while (true) {
  101. if (ReadAndCheckReport()) {
  102. ++safe_reports;
  103. }
  104. if (not SkipNewline()) {
  105. break;
  106. }
  107. }
  108. Core.Print(safe_reports);
  109. }