Add advent of code 2024 day 14 and day 15 part 1 solutions (#6597)

I've had these kicking around for a year but never got around to pushing
them. They seem to cover a few things that previous examples didn't, so
I think we may as well include them.

---------

Co-authored-by: Carbon Infra Bot <carbon-external-infra@google.com>
This commit is contained in:
Richard Smith
2026-01-14 18:33:23 +00:00
committed by GitHub
co-authored by Carbon Infra Bot
parent 32aa7cb1fa
commit 7f0f402f95
7 changed files with 319 additions and 23 deletions
+8 -8
View File
@@ -11,19 +11,19 @@ utils = [
# A list of examples that should be excluded because they don't build any more.
# For example, to exclude day3_part2, use:
# excluded = [(3, 2)]
# excluded = ["day3_part2.carbon"]
excluded = []
# Produce a binary "dayX_partY" for each matching `.carbon` file.
# Produce a binary `dayX_partY` for each matching `.carbon` file. Each binary
# depends on `dayX_partY.carbon` and `dayX_common.carbon`.
[
carbon_binary(
name = "day{0}_part{1}".format(day, part),
name = carbon_file.removesuffix(".carbon"),
srcs = [
"day{0}_common.carbon".format(day),
"day{0}_part{1}.carbon".format(day, part),
carbon_file,
carbon_file.rsplit("_", 1)[0] + "_common.carbon",
] + utils,
)
for day in range(1, 14)
for part in range(1, 3)
if (day, part) not in excluded
for carbon_file in glob(["day*_part*.carbon"])
if carbon_file not in excluded
]
+49
View File
@@ -0,0 +1,49 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// https://adventofcode.com/2024/day/14
library "day14_common";
import Core library "io";
import library "io_utils";
fn ReadXY(xy: (i32, i32)*) -> bool {
return ReadInt(ref xy->0) and ConsumeChar(',') and ReadInt(ref xy->1);
}
fn SkipString(s: str) -> bool {
return SkipNChars(s.Size() as i32);
}
fn Mod(a: i32, d: i32) -> i32 {
let rem: i32 = a % d;
return if rem < 0 then rem + d else rem;
}
class Robot {
fn Read() -> Robot {
returned var me: Robot;
SkipString("p=");
ReadXY(&me.p);
SkipString(" v=");
ReadXY(&me.v);
SkipNewline();
return var;
}
fn Pos[self: Self](t: i32) -> (i32, i32) {
return (Mod((self.p.0 + self.v.0 * Mod(t, 101)), 101),
Mod((self.p.1 + self.v.1 * Mod(t, 103)), 103));
}
impl as Core.Copy {
fn Op[self: Self]() -> Self {
return {.p = self.p, .v = self.v};
}
}
var p: (i32, i32);
var v: (i32, i32);
}
+21
View File
@@ -0,0 +1,21 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// https://adventofcode.com/2024/day/14
import Core library "io";
import library "day14_common";
import library "io_utils";
fn Run() {
var q: array(array(i32, 3), 3) = ((0,0,0), (0,0,0), (0,0,0));
while (PeekChar() != CharOrEOF.EOF()) {
var r: Robot = Robot.Read();
let xy: (i32, i32) = r.Pos(100);
++q[if xy.0 < 50 then 0 else if xy.0 > 50 then 2 else 1]
[if xy.1 < 51 then 0 else if xy.1 > 51 then 2 else 1];
}
Core.Print(q[0][0] * q[0][2] * q[2][0] * q[2][2]);
}
+70
View File
@@ -0,0 +1,70 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// https://adventofcode.com/2024/day/14
import Core library "io";
import Core library "range";
import library "day14_common";
import library "io_utils";
// Returns m and n so that am + bn = gcd.
fn Euclid(a: i32, b: i32) -> {.m: i32, .n: i32, .gcd: i32} {
if (a < b) {
let reverse: {.m: i32, .n: i32, .gcd: i32} = Euclid(b, a);
return {.m = reverse.n, .n = reverse.m, .gcd = reverse.gcd};
}
if (b == 0) {
return {.m = 1, .n = 0, .gcd = a};
}
let next: {.m: i32, .n: i32, .gcd: i32} = Euclid(b, a % b);
return {.m = next.n, .n = next.m - next.n * (a / b), .gcd = next.gcd};
}
// Uses Chinese remainder theorem to find the least positive x that solves
// x = a mod p
// x = b mod q
// modulo pq / gcd(p,q), if one exists (if a = b mod gcd(p,q)).
fn CRT(a: i32, b: i32, p: i32, q: i32) -> i32 {
let e: {.m: i32, .n: i32, .gcd: i32} = Euclid(p, q);
let x: i32 = a * (q / e.gcd) * e.n + b * (p / e.gcd) * e.m;
return Mod(x, p * (q / e.gcd));
}
fn PrintState(r: array(Robot, 500), t: i32) {
var display: array(array(char, 101), 103);
for (y: i32 in Core.Range(103)) {
for (x: i32 in Core.Range(101)) {
display[y][x] = '.';
}
}
for (ri: Robot in r) {
let xy: (i32, i32) = ri.Pos(t);
display[xy.1][xy.0] = '#';
}
for (y: i32 in Core.Range(103)) {
for (x: i32 in Core.Range(101)) {
Core.PrintChar(display[y][x]);
}
Core.PrintChar('\n');
}
}
fn Run() {
var r: array(Robot, 500);
for (i: i32 in Core.Range(500)) {
r[i] = Robot.Read();
}
let first_match_x: i32 = 28;
let first_match_y: i32 = 84;
var t: i32 = CRT(first_match_x, first_match_y, 101, 103);
Core.Print(t);
PrintState(r, t);
}
+134
View File
@@ -0,0 +1,134 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// https://adventofcode.com/2024/day/15
library "day15_common";
import Core library "io";
import library "io_utils";
// TODO: Use a choice type.
class Square {
adapt char;
impl as Core.Copy {
fn Op[self: Self]() -> Self {
return (self as char).(Core.Copy.Op)() as Self;
}
}
impl as Core.EqWith(Square) {
fn Equal[self: Self](other: Self) -> bool {
// TODO: Use a `char` comparison when it's supported.
return (self as u8) == (other as u8);
}
fn NotEqual[self: Self](other: Self) -> bool {
// TODO: Use a `char` comparison when it's supported.
return (self as u8) != (other as u8);
}
}
fn Make(c: CharOrEOF) -> Square {
// TODO: Use `match`.
if (c == '#') { return ('#' as char) as Square; }
if (c == 'O') { return ('O' as char) as Square; }
if (c == '.') { return ('.' as char) as Square; }
if (c == '@') { return ('@' as char) as Square; }
return ('!' as char) as Square;
}
}
let Wall: Square = ('#' as char) as Square;
let Box: Square = ('O' as char) as Square;
let Empty: Square = ('.' as char) as Square;
let Robot: Square = ('@' as char) as Square;
class Grid {
fn Read() -> Grid {
returned var me: Grid;
var y: i32 = 0;
while (y < 50) {
var x: i32 = 0;
while (x < 50) {
me.data[x][y] = Square.Make(ReadChar());
if (me.data[x][y] == Robot) {
me.robot = (x, y);
}
++x;
}
SkipNewline();
++y;
}
return var;
}
fn Move[ref self: Self](d: (i32, i32)) {
var distance: i32 = 1;
while (self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] == Box) {
++distance;
}
if (self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] == Empty) {
self.data[self.robot.0][self.robot.1] = Empty;
self.data[self.robot.0 + distance * d.0][self.robot.1 + distance * d.1] = Box;
self.data[self.robot.0 + d.0][self.robot.1 + d.1] = Robot;
self.robot.0 += d.0;
self.robot.1 += d.1;
}
}
fn Print[self: Self]() {
var y: i32 = 0;
while (y < 50) {
var x: i32 = 0;
while (x < 50) {
Core.PrintChar(self.data[x][y] as char);
++x;
}
Core.PrintChar('\n');
++y;
}
Core.PrintChar('\n');
}
fn Signature[self: Self]() -> i32 {
var s: i32 = 0;
var y: i32 = 0;
while (y < 50) {
var x: i32 = 0;
while (x < 50) {
if (self.data[x][y] == Box) {
s += y * 100 + x;
}
++x;
}
++y;
}
return s;
}
var data: array(array(Square, 50), 50);
var robot: (i32, i32);
}
fn ReadAndExecuteActions(ref g: Grid) {
while (true) {
var m: CharOrEOF = ReadChar();
// To watch the progress, uncomment the following:
// g.Print();
// Core.PrintChar(((m as i32) as u8) as char);
// Core.PrintChar('\n');
// TODO: Use `match` once it's available.
if (m == '^') {
g.Move((0, -1));
} else if (m == '<') {
g.Move((-1, 0));
} else if (m == '>') {
g.Move((1, 0));
} else if (m == 'v') {
g.Move((0, 1));
} else if (m == CharOrEOF.EOF()) {
return;
}
}
}
+15
View File
@@ -0,0 +1,15 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// https://adventofcode.com/2024/day/15
import Core library "io";
import library "day15_common";
fn Run() {
var g: Grid = Grid.Read();
ReadAndExecuteActions(ref g);
Core.Print(g.Signature());
}
+22 -15
View File
@@ -90,8 +90,24 @@ fn UnreadChar(c: CharOrEOF) {
unread_char = Core.Optional(CharOrEOF).Some(c);
}
fn PeekChar() -> CharOrEOF {
var next: CharOrEOF = ReadChar();
UnreadChar(next);
return next;
}
fn ConsumeChar(c: char) -> bool {
var next: CharOrEOF = ReadChar();
if (next != c) {
UnreadChar(next);
return false;
}
return true;
}
fn ReadInt[N:! Core.IntLiteral()](ref p: Core.Int(N)) -> bool {
var read_any_digits: bool = false;
let negative: bool = ConsumeChar('-');
// TODO: `p = 0;` crashes the toolchain, see
// https://github.com/carbon-language/carbon-lang/issues/6500.
p = (0 as i32) as Core.Int(N);
@@ -108,6 +124,12 @@ fn ReadInt[N:! Core.IntLiteral()](ref p: Core.Int(N)) -> bool {
p += (c - '0') as Core.Int(N);
read_any_digits = true;
}
if (negative) {
p = -p;
if (not read_any_digits) {
UnreadChar('-');
}
}
return read_any_digits;
}
@@ -135,21 +157,6 @@ fn PrintInt[N:! Core.IntLiteral()](n_val: Core.Int(N)) {
Core.PrintChar('\n');
}
fn PeekChar() -> CharOrEOF {
var next: CharOrEOF = ReadChar();
UnreadChar(next);
return next;
}
fn ConsumeChar(c: char) -> bool {
var next: CharOrEOF = ReadChar();
if (next != c) {
UnreadChar(next);
return false;
}
return true;
}
fn SkipSpaces() -> bool {
var skipped_any_spaces: bool = false;
while (ConsumeChar(' ')) {