Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solutions for advent of code, day 4 - 13. #4750

Merged
merged 13 commits into from
Jan 7, 2025
65 changes: 18 additions & 47 deletions examples/advent2024/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,50 +9,21 @@ utils = [
"sort.carbon",
]

carbon_binary(
name = "day1_part1",
srcs = [
"day1_common.carbon",
"day1_part1.carbon",
] + utils,
)

carbon_binary(
name = "day1_part2",
srcs = [
"day1_common.carbon",
"day1_part2.carbon",
] + utils,
)

carbon_binary(
name = "day2_part1",
srcs = [
"day2_common.carbon",
"day2_part1.carbon",
] + utils,
)

carbon_binary(
name = "day2_part2",
srcs = [
"day2_common.carbon",
"day2_part2.carbon",
] + utils,
)

carbon_binary(
name = "day3_part1",
srcs = [
"day3_common.carbon",
"day3_part1.carbon",
] + utils,
)

carbon_binary(
name = "day3_part2",
srcs = [
"day3_common.carbon",
"day3_part2.carbon",
] + 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 = []

# Produce a binary "dayX_partY" for each matching `.carbon` file.
[
zygoloid marked this conversation as resolved.
Show resolved Hide resolved
carbon_binary(
name = "day{0}_part{1}".format(day, part),
srcs = [
"day{0}_common.carbon".format(day),
"day{0}_part{1}.carbon".format(day, part),
] + utils,
)
for day in range(1, 14)
for part in range(1, 3)
if (day, part) not in excluded
]
6 changes: 6 additions & 0 deletions examples/advent2024/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ style and idioms. Instead, the purpose of these examples are to test the current
state of the toolchain against larger code examples than those that are present
in the toolchain's own tests, to find bugs in the toolchain, and to drive
feature development in the toolchain by presenting somewhat realistic testcases.

If one of these examples stops building after a change to the toolchain, please:

- Make sure that the build break is an expected consequence of the change.
- Update the `BUILD` file to exclude that example.
- File an issue and assign it to @zygoloid.
29 changes: 29 additions & 0 deletions examples/advent2024/day10_common.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// 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/10

library "day10_common";

import Core library "io";
import library "io_utils";

class Terrain {
Copy link
Contributor

@jonmeow jonmeow Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth having any documentation in these files? A lot of these get fairly complex, including when you get into hex representations of characters. Where do you see the balance of maintenance and discarding if the code needs updated? (maybe it's more on the latter side, due to a lack of tests?)

Copy link
Contributor Author

@zygoloid zygoloid Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding documentation: while we are asked not to distribute the test inputs or the problem description, I think it'd be fine to include links to the problems. I've gone ahead and done that, but that only helps for part 1 -- the part 2 description is unavailable unless you are logged in and have solved part 1.

I've been a bit wary of including too much description in these because of the request to not distribute the problems, but perhaps I'm being too cautious. I'm intending to do some more maintenance of these examples over time (using new Carbon features as they become available, for example), and will think about striking a better balance here as I rework these.

Regarding maintenance versus discarding: I've added some instructions to README.md for how to react to build breaks. I'm leaning towards disabling rather than deleting the examples when they break, but I don't mind much either way; it's easy to get them back from git history if we'd prefer to delete them immediately.

Copy link
Contributor

@jonmeow jonmeow Jan 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for the README.md changes (note the possible mistake, separately commented).

The day links don't help me though -- with the README, I know where to find those. To try to explain, here's how I think I'd give a good review of the code:

  • Make up for missing documentation:
    1. Read the advent day.
    2. Think about how I'd solve it, maybe solve myself.
    3. Compare my solution with your code to think about naming and structure.
  • Make up for missing tests:
    1. Check out your PR.
    2. Take the advent inputs, and run your PR over the inputs.

Specific reasons here, to try to help you understand:

  • Terrain isn't mentioned in the day 10 description, though after spending some time trying to review, I think I understand where the name came from.
  • 43 I'm lost on, so I'd need to invest more time.
  • 0x30 is probably a character, but I don't memorize these things and would need to look it up.

But, that's just one fragment of one day, and I feel I could spend a good few days trying to really understand the code. I understand your desire to avoid comments and tests, but that's also leading to me giving a very light review because I expect it's not worth my time to give deep review.

I don't know if implementing something like https://rosettacode.org/ tasks would be as interesting to you, but that may be an option for things where you could have documentation and tests, possibly making it more approachable to both review and to developers trying to learn about Carbon.

fn Read() -> Terrain {
returned var me: Terrain;
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
me.height[x][y] = ReadChar() - 0x30;
++x;
}
SkipNewline();
++y;
}
return var;
}

var height: [[i32; 43]; 43];
}
99 changes: 99 additions & 0 deletions examples/advent2024/day10_part1.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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/10

import Core library "io";

import library "day10_common";
import library "io_utils";

// TODO: Add this to the prelude.
fn PopCount(n: u256) -> i32 {
var bit: u256 = 1;
var total: i32 = 0;
while (bit != 0) {
if (n & bit != 0) {
++total;
}
bit = bit << 1;
}
return total;
}

class Reachable {
fn Make(terrain: Terrain) -> Reachable {
returned var me: Reachable;
var next: u256 = 1;
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
if (terrain.height[x][y] == 0) {
me.trailheads[x][y] = next;
next = next << 1;
}
++x;
}
++y;
}
return var;
}

fn AddLevel[addr self: Self*](terrain: Terrain, level: i32) {
let adj: [(i32, i32); 4] = ((-1, 0), (0, -1), (1, 0), (0, 1));
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
if (terrain.height[x][y] == level) {
var reach: u256 = 0;
var i: i32 = 0;
while (i < 4) {
let adj_x: i32 = x + adj[i].0;
let adj_y: i32 = y + adj[i].1;
if (adj_x >= 0 and adj_x < 43 and
adj_y >= 0 and adj_y < 43 and
terrain.height[adj_x][adj_y] == level - 1) {
reach = reach | self->trailheads[adj_x][adj_y];
}
++i;
}
self->trailheads[x][y] = reach;
}
++x;
}
++y;
}
}

fn Count[self: Self](terrain: Terrain, level: i32) -> i32 {
var total: i32 = 0;
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
if (terrain.height[x][y] == level) {
total += PopCount(self.trailheads[x][y]);
}
++x;
}
++y;
}
return total;
}

var trailheads: [[u256; 43]; 43];
}

fn Run() {
var terrain: Terrain = Terrain.Read();
var reachable: Reachable = Reachable.Make(terrain);
var i: i32 = 1;
while (i <= 9) {
reachable.AddLevel(terrain, i);
++i;
}
Core.Print(reachable.Count(terrain, 9));
}
72 changes: 72 additions & 0 deletions examples/advent2024/day10_part2.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// 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/10

import Core library "io";

import library "day10_common";
import library "io_utils";

class PathsToTop {
fn Make(terrain: Terrain) -> PathsToTop {
returned var me: PathsToTop;
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
// TODO: We shouldn't need an explicit cast here.
me.paths[x][y] =
if terrain.height[x][y] == 9 then 1 as i64 else 0;
++x;
}
++y;
}
return var;
}

fn AddLevel[addr self: Self*](terrain: Terrain, level: i32) -> i64 {
var total: i64 = 0;
let adj: [(i32, i32); 4] = ((-1, 0), (0, -1), (1, 0), (0, 1));
var y: i32 = 0;
while (y < 43) {
var x: i32 = 0;
while (x < 43) {
if (terrain.height[x][y] == level) {
var paths: i64 = 0;
var i: i32 = 0;
while (i < 4) {
let adj_x: i32 = x + adj[i].0;
let adj_y: i32 = y + adj[i].1;
if (adj_x >= 0 and adj_x < 43 and
adj_y >= 0 and adj_y < 43 and
terrain.height[adj_x][adj_y] == level + 1) {
paths = paths + self->paths[adj_x][adj_y];
}
++i;
}
self->paths[x][y] = paths;
total = total + paths;
}
++x;
}
++y;
}
return total;
}

var paths: [[i64; 43]; 43];
}

fn Run() {
var terrain: Terrain = Terrain.Read();
var paths: PathsToTop = PathsToTop.Make(terrain);
var i: i32 = 8;
var total: i64;
while (i >= 0) {
total = paths.AddLevel(terrain, i);
--i;
}
PrintInt64(total);
}
31 changes: 31 additions & 0 deletions examples/advent2024/day11_common.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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/11

library "day11_common";

import Core library "io";
import library "io_utils";

fn Next(n: i64) -> (i64, i64) {
if (n == 0) { return (1, -1); }
var pow10: i64 = 10;
var pow100: i64 = 1;
while (n / pow100 >= 100) {
pow100 = pow100 * 100;
pow10 = pow10 * 10;
}
if (n / pow100 >= 10) {
return (n / pow10, n % pow10);
}
return (n * 2024, -1);
}

fn Count(n: i64, depth: i32) -> i32 {
if (n == -1) { return 0; }
if (depth == 0) { return 1; }
let next: (i64, i64) = Next(n);
return Count(next.0, depth - 1) + Count(next.1, depth - 1);
}
20 changes: 20 additions & 0 deletions examples/advent2024/day11_part1.carbon
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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/11

import Core library "io";

import library "day11_common";
import library "io_utils";

fn Run() {
var n: i64;
var total: i32 = 0;
while (ReadInt64(&n)) {
total = total + Count(n, 25);
SkipSpaces();
}
Core.Print(total);
}
Loading
Loading