Solving Cube Conundrum in Rust (Advent of Code)

June 16, 2024 • 15 days ago

Advent of Code is a site that provides programming puzzles every Christmas. It’s often used as a way to learn a new programming language or prepare for technical interviews. In this post, I’d like to explore the “Cube Conundrum” problem from 2023. Each problem on Advent of Code typically has two parts.

Before looking at my solution, you might want to try the problem yourself. If you get stuck, you can always refer back to this post for guidance. Find the problem statement for “Cube Conundrum” here.

Part 1

Story - Part 1

--- Day 2: Cube Conundrum ---

You’re launched high into the atmosphere! The apex of your trajectory just barely reaches the surface of a large island floating in the sky. You gently land in a fluffy pile of leaves. It’s quite cold, but you don’t see much snow. An Elf runs over to greet you.

The Elf explains that you’ve arrived at Snow Island and apologizes for the lack of snow. He’ll be happy to explain the situation, but it’s a bit of a walk, so you have some time. They don’t get many visitors up here; would you like to play a game in the meantime?

As you walk, the Elf shows you a small bag and some cubes which are either red, green, or blue. Each time you play this game, he will hide a secret number of cubes of each color in the bag, and your goal is to figure out information about the number of cubes. […]

Advent of Code

The story begins with you being catapulted onto “Snow Island,” where you meet an Elf. This Elf wants to play a game with you.

Here’s how a single game works:

You play multiple games and record the information from these games along with a game ID.

Example:

input.txt
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green

Problem Statement

The Elf wants to know which of these games would be possible if you had only 12 red cubes, 13 green cubes, and 14 blue cubes.

We need to collect all the game id of valid games and then return the sum of these games.

In the given example, games 1, 2, and 5 could have occurred with the specified bag configuration. However, game 3 is not possible because the Elf displayed 20 red cubes at once, and game 4 is also impossible since the Elf displayed 15 blue cubes at once. Adding up the IDs of the feasible games results in a total of 8.

How to solve this problem.

To solve this problem there are steps we need to do:

  1. Import and parse the given input file
  2. Filter out valid games
  3. Collect the game idof these games
  4. Return the sum of the game id of all games.
Detailed Steps

Step 1: Import and Parse the Input File

  • In Rust, we can use the std::fs module to read from the file system API.
  • We store the input file in a variable.
  • Then we split the input file by lines since each line represents a single game to be processed.

Step 2: Parse Each Line to Retrieve the game id and List of Sets

  • We need to go through each line to extract the game id and a list of sets in each game.
  • Each set contains the number of red, green, and/or blue cubes.

Step 3: Filter Out Valid Games Using a Match Statement

  • The only valid colors are red, green, and blue.
  • The amount of each color should be less than or equal to:
    • 12 for red
    • 13 for green
    • 14 for blue
  • We use a match statement to filter out the valid games.

Step 4: Filter Out Valid Games and Sum the game id

  • We collect the game id of the games that satisfy the conditions.
  • Finally, we return the sum of the game id of all valid games.

Part 1
use std::fs;
fn main() {
part1();
}
fn part1() {
let content = fs::read_to_string("src/input.txt").expect("Failed to read the file");
let lines = content.lines();
let sum = lines
.map(|line| {
let split = line.split_once(": ").unwrap_or_default();
let game_id = split
.0
.split_once(" ")
.unwrap_or_default()
.1
.parse::<i32>()
.unwrap();
let game = split.1;
let game_res = game
.split("; ")
.map(|set| {
return set
.split(", ")
.map(|cubes| {
let cube_split = cubes.split_once(" ").unwrap();
let amount = cube_split.0.parse::<i32>().unwrap();
let color = cube_split.1;
match color {
"blue" => amount <= 14,
"red" => amount <= 12,
"green" => amount <= 13,
_ => false,
}
})
.all(|x| x == true);
})
.all(|x| x == true);
return (game_id, game_res);
})
.filter(|x| x.1 == true)
.map(|x| x.0)
.sum::<i32>();
println!("Output: {sum:?}")
}

The code above will output the sum of the game id of all valid games. In the given example, the output will be 8.

Part 2

Story - Part 2

The Elf says they’ve stopped producing snow because they aren’t getting any water! He isn’t sure why the water stopped; however, he can show you how to get to the water source to check it out for yourself. It’s just up ahead!

As you continue your walk, the Elf poses a second question: in each game you played, what is the fewest number of cubes of each color that could have been in the bag to make the game possible?

Advent of Code

In Part 2, we need to find the minimum number of cubes of each color that could have been in the bag to make each game possible. Then, we calculate the product of these minimums for each game and return the sum of these products.

For the input:

input.txt
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green

Problem Statement

Given the list of games, we need to:

  1. Determine the fewest number of red, green, and blue cubes needed for each game.
  2. Compute the product of these minimum counts for each game.
  3. Return the sum of these products.

In the given example that would be 2286.

How to solve this problem

First, we parse the input (like in part 1) and extract the number of each color cube needed in every game. Then, we find the maximum number of cubes needed for each color in each game. Finally, we compute the product of these values and sum them for all games.

Part 2
use std::fs;
fn main() {
part2();
}
fn part2() {
let content = fs::read_to_string("src/input.txt").expect("Failed to read the file");
let lines = content.lines();
let games = lines
.map(|line| {
let split = line.split_once(": ").unwrap_or_default();
let game = split.1;
let game_res = game.split("; ").map(|set| {
let s = set
.split(", ")
.map(|cubes| {
let cube_split = cubes.split_once(" ").unwrap();
let amount = cube_split.0.parse::<i32>().unwrap();
let color = cube_split.1;
match color {
"red" => [amount, 0, 0],
"green" => [0, amount, 0],
"blue" => [0, 0, amount],
_ => [0, 0, 0],
}
})
.fold([0, 0, 0], |acc, set| {
[acc[0] + set[0], acc[1] + set[1], acc[2] + set[2]]
});
return s;
});
let max_cubes = game_res.fold([0, 0, 0], |acc, g| {
let max_red = std::cmp::max(acc[0], g[0]);
let max_green = std::cmp::max(acc[1], g[1]);
let max_blue = std::cmp::max(acc[2], g[2]);
[max_red, max_green, max_blue]
});
return max_cubes;
})
.map(|g| g.iter().product::<i32>())
.sum::<i32>();
println!("Output: {games:?}")
}

The code above will output the sum of the product of the minimum number of cubes of each color needed for each game. In the given example, the output will be 2286.

Conclusion

In this post, we solved the “Cube Conundrum” problem from Advent of Code 2023 using Rust. I learned basics of the rust language and iterators methods like map, filter, fold, and sum. I hope you found this post helpful and informative. If you have any questions or suggestions, feel free to write me via email or twitter.

Happy coding!