# Advent of Code 2023 Day 2 in C#

To paraphrase the puzzle, Santa takes a walk with an Elf, and they play a game involving a bag of colorful cubes. In each game, there is an unknown number of each colored cubes in the bag, and the Elf pulls out a number of cubes, for example:

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


## Part 1

In part 1, we want to know which games would have been possible if the bag contained only 12 red cubes, 13 green cubes, and 14 blue cubes. The answer is the sum of the eligible game ids, in the above example 1 + 2 + 5 = 8.

## Part 2

To paraphrase the change in part 2, for each game, we're looking for the maximum number of cubes per color that were drawn, multiply them together, and then sum this number for all games. With the example above this would be 48 + 12 + 1560 + 630 + 36 = 2286.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

Console.WriteLine(GetSumOfInvalidIDs(input));
Console.WriteLine(GetCubeSetPower(input));
static int GetSumOfInvalidIDs(string input)
{
return input
.Split("\r\n")
.Sum(ProcessGamePart1);
}

static int GetCubeSetPower (string input)
{
return input
.Split("\r\n")
.Sum(ProcessGamePart2);
}

static int ProcessGamePart1(string game)
{
Dictionary<string, int> maxForColor = new()
{
{"red", 12},
{"green", 13},
{"blue", 14}
};

foreach (string colorCount in GetColorCounts(game))
{
var (numberOfBalls, color) = ParseColorCounts(colorCount.Trim());
if (numberOfBalls > maxForColor[color])
{
return 0;
}
}
return GetGameId(game);
}

static int ProcessGamePart2(string game)
{
Dictionary<string, int> maxSeenOfColor = new();
foreach (string colorCount in GetColorCounts(game))
{
var (numberOfBalls, color) = ParseColorCounts(colorCount.Trim());
maxSeenOfColor.TryGetValue(color, out int currentMax);
maxSeenOfColor[color] = Int32.Max(currentMax, numberOfBalls);
}
return maxSeenOfColor.Values.Aggregate(1,
(currentPower, nextColor) => currentPower * nextColor);
}

static int GetGameId(string game)
{
return Int32.Parse(game.Split(" ")[1].TrimEnd(':'));
}

static string[] GetColorCounts(string game)
{
// Each color is given all at once for each handful,
// so we only have to look at each count given.
return game.Split(":")[1].Split(new char[] { ',', ';' });
}

static (int, string) ParseColorCounts(string handful)
{
var parts = handful.Split(" ");
int numberOfBalls = Int32.Parse(parts[0]);
string color = parts[1];
return (numberOfBalls, color);
}


## Review Request

What could be improved upon? I am new to the language and am specifically interested in readability and idiomatic usage.

• What's with the distinction between , and ; in the summary of the game results? The semicolon seems to be used minimally as a way to separate repetition of the same color; which is making me wonder if the drawn cubes are being put back in the bag when you "cross" a semicolon. Commented Dec 6, 2023 at 3:01
• That’s correct, sorry about the unclear description Commented Dec 6, 2023 at 5:37

## Usings

Keep the using directives tidy - System.Text and System.Threading.Tasks are not needed but System.IO is missing.

## Potential bug due to unclear input specifications

The implementation will treat Game 1: 5 red, 10 red as a valid game. Which it isn't since there are only 12 red balls. It's not clear from the description if this is an expected input or not. If it's not a valid input then it should be rejected.

## Input processing

1. Hard-coded input file name. If you follow the Unix philosophy you'd be just reading from stdin and process the input as you go. This has the advantage that users have the flexibility to decide where the data is coming from.

2. Split("\r\n") will fail on a Unix file since the line endings are \n there. File.ReadAllLines already does the work for you and also removes the need to split the input several times. Otherwise if you do end up needing to split lines yourself then Environment.NewLine exists.

3. Overall you process and parse all input several times which is wasteful and also intermixes the parsing logic with the actual game logic. This makes it hard to read and will also make maintenance hard (in the real world you can usually bet that the input specs and/or the game logic will be changing as time goes on).

The better way to do this would be to create a data structure representing the games, parse the input into this structure and the apply the game logic on the structure. This has several advantages: input is processed just once, different input formats or slight changes in input format can be easily supported without having to touch the game logic and you can separate input validation from game logic.