|
|
|
|
@ -1,13 +1,5 @@
|
|
|
|
|
use std::{fs::File, path::Path, io::Read};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// #[derive(Clone, Copy, Debug)]
|
|
|
|
|
// enum CubeType
|
|
|
|
|
// {
|
|
|
|
|
// RED,
|
|
|
|
|
// GREEN,
|
|
|
|
|
// BLUE,
|
|
|
|
|
// }
|
|
|
|
|
use std::{fs::File, path::Path, io::Read};
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
|
enum TokenType
|
|
|
|
|
@ -47,10 +39,62 @@ impl CubeSample
|
|
|
|
|
|
|
|
|
|
fn main()
|
|
|
|
|
{
|
|
|
|
|
let input = load_data("data/test_input");
|
|
|
|
|
let input = load_data("data/input");
|
|
|
|
|
|
|
|
|
|
let games = parse_games(&input);
|
|
|
|
|
|
|
|
|
|
// PART 1:
|
|
|
|
|
// Determine which games would have been possible if the bag had been loaded with
|
|
|
|
|
// only 12 red cubes, 13 green cubes, and 14 blue cubes.
|
|
|
|
|
// What is the sum of the IDs of those games?
|
|
|
|
|
let actual_cube_nums = CubeSample { red: 12, green: 13, blue: 14};
|
|
|
|
|
let mut sum = 0;
|
|
|
|
|
for game_idx in 0..games.len()
|
|
|
|
|
{
|
|
|
|
|
if game_is_possible(&games[game_idx], actual_cube_nums)
|
|
|
|
|
{
|
|
|
|
|
sum += game_idx + 1; // Game IDs start at 1, not 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("\nSum of valid game IDs: {}", sum);
|
|
|
|
|
|
|
|
|
|
let mut power_sum = 0;
|
|
|
|
|
for game in games
|
|
|
|
|
{
|
|
|
|
|
let min_cubes = find_min_cubes_for_game(&game);
|
|
|
|
|
|
|
|
|
|
let tokens = tokenize_data(&input);
|
|
|
|
|
println!("TOKENS:\n\t{:#?}", tokens);
|
|
|
|
|
let power_set = min_cubes.red * min_cubes.green * min_cubes.blue; // This could be made a method of CubeSample
|
|
|
|
|
// println!("DEBUG: Power Set: {}", power_set);
|
|
|
|
|
|
|
|
|
|
power_sum += power_set;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
println!("Power sum of minimum cube sets: {}", power_sum);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_min_cubes_for_game(game: &Vec<CubeSample>) -> CubeSample
|
|
|
|
|
{
|
|
|
|
|
let mut min_cubes = CubeSample::new();
|
|
|
|
|
for sample in game
|
|
|
|
|
{
|
|
|
|
|
if min_cubes.red < sample.red
|
|
|
|
|
{
|
|
|
|
|
min_cubes.red = sample.red;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if min_cubes.green < sample.green
|
|
|
|
|
{
|
|
|
|
|
min_cubes.green = sample.green;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if min_cubes.blue < sample.blue
|
|
|
|
|
{
|
|
|
|
|
min_cubes.blue = sample.blue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
min_cubes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parse_games(game_data: &str) -> Vec<Vec<CubeSample>>
|
|
|
|
|
@ -59,26 +103,65 @@ fn parse_games(game_data: &str) -> Vec<Vec<CubeSample>>
|
|
|
|
|
|
|
|
|
|
// Format:
|
|
|
|
|
// Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
|
|
|
|
|
// let mut current = 0;
|
|
|
|
|
// let mut prev = usize::MAX;
|
|
|
|
|
// let mut games: Vec<Vec<CubeSample>> = Vec::new();
|
|
|
|
|
|
|
|
|
|
// while current < tokens.len()
|
|
|
|
|
// {
|
|
|
|
|
// match tokens[current].ttype
|
|
|
|
|
// {
|
|
|
|
|
// TokenType::GAME => games.push(Vec::new()),
|
|
|
|
|
// TokenType::COLON => (), // Ignore ID since the index will match this value - 1
|
|
|
|
|
// TokenType::COMMA => (), // Don't need to handle the commas either
|
|
|
|
|
// TokenType::NUMBER => last_num = tokens[current].value,
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// prev = current;
|
|
|
|
|
// current += 1;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
vec![]
|
|
|
|
|
let mut current = 0;
|
|
|
|
|
let mut prev = usize::MAX;
|
|
|
|
|
let mut games: Vec<Vec<CubeSample>> = Vec::new();
|
|
|
|
|
|
|
|
|
|
while current < tokens.len()
|
|
|
|
|
{
|
|
|
|
|
// let mut sample_idx: usize = 0;
|
|
|
|
|
// let mut cube_idx: usize = 0;
|
|
|
|
|
match tokens[current].ttype
|
|
|
|
|
{
|
|
|
|
|
TokenType::Game =>
|
|
|
|
|
{
|
|
|
|
|
// Move on to the next game
|
|
|
|
|
games.push(Vec::new());
|
|
|
|
|
let len = games.len();
|
|
|
|
|
games[len - 1].push(CubeSample::new());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
TokenType::Semicolon =>
|
|
|
|
|
{
|
|
|
|
|
// Move to a new cube sample
|
|
|
|
|
let len = games.len();
|
|
|
|
|
games[len - 1].push(CubeSample::new());
|
|
|
|
|
}
|
|
|
|
|
TokenType::CubeRed =>
|
|
|
|
|
{
|
|
|
|
|
let len = games.len();
|
|
|
|
|
let game = &mut games[len - 1];
|
|
|
|
|
let len = game.len();
|
|
|
|
|
game[len - 1].red = tokens[prev].value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TokenType::CubeGreen =>
|
|
|
|
|
{
|
|
|
|
|
let len = games.len();
|
|
|
|
|
let game = &mut games[len - 1];
|
|
|
|
|
let len = game.len();
|
|
|
|
|
game[len - 1].green = tokens[prev].value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TokenType::CubeBlue =>
|
|
|
|
|
{
|
|
|
|
|
let len = games.len();
|
|
|
|
|
let game = &mut games[len - 1];
|
|
|
|
|
let len = game.len();
|
|
|
|
|
game[len - 1].blue = tokens[prev].value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TokenType::Colon => (), // Ignore ID since the index will match this value - 1
|
|
|
|
|
TokenType::Comma => (), // Don't need to handle the commas either
|
|
|
|
|
TokenType::Number => (), // We'll get this by directly accessing the prev token
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prev = current;
|
|
|
|
|
current += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
games
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn tokenize_data(game_data: &str) -> Vec<DataToken>
|
|
|
|
|
@ -204,8 +287,25 @@ fn tokenize_data(game_data: &str) -> Vec<DataToken>
|
|
|
|
|
|
|
|
|
|
fn game_is_possible(game_data: &Vec<CubeSample>, cubes: CubeSample) -> bool
|
|
|
|
|
{
|
|
|
|
|
for sample in game_data
|
|
|
|
|
{
|
|
|
|
|
if sample.red > cubes.red
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sample.green > cubes.green
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if sample.blue > cubes.blue
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
false
|
|
|
|
|
true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_data(file_name: &str) -> String
|
|
|
|
|
|