1
0
Fork 0

Created 2 day puzzle solution

This commit is contained in:
Maxim Lebedev 2023-12-03 03:16:45 +06:00
parent 58a0fbf077
commit 17d20ae88d
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
5 changed files with 356 additions and 0 deletions

50
day2_test.go Normal file
View File

@ -0,0 +1,50 @@
package adventofcode_test
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"source.toby3d.me/toby3d/adventofcode/internal/day2/part1"
)
/*
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.
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?
*/
func ExampleDay2() {
input, err := os.ReadFile(filepath.Join("internal", "day2", "testdata", "input"))
if err != nil {
log.Fatalln("cannot open input file:", err)
}
result, err := part1.GetSecretNumber(bytes.NewReader(input), part1.Bag{
Red: 12,
Green: 13,
Blue: 14,
})
if err != nil {
log.Fatalln("cannot get secret number:", err)
}
fmt.Println(result)
// Output: 3035
}

2
go.mod
View File

@ -1,3 +1,5 @@
module source.toby3d.me/toby3d/adventofcode
go 1.21.4
require github.com/google/go-cmp v0.6.0 // indirect

2
go.sum
View File

@ -0,0 +1,2 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -0,0 +1,122 @@
package part1
import (
"bufio"
"fmt"
"io"
"strconv"
"strings"
)
type (
Bag struct {
Red uint8
Green uint8
Blue uint8
}
Game struct {
Rounds []Round
ID uint8
}
Round struct {
Red uint8
Green uint8
Blue uint8
}
)
func ParseGame(str string) (*Game, error) {
parts := strings.Split(str, ": ")
if len(parts) != 2 {
return nil, fmt.Errorf("bad input format: want 'Game 0: 0 color; 0 color, 0 color...', got '%s'", str)
}
id, err := strconv.ParseUint(strings.TrimPrefix(parts[0], "Game "), 10, 8)
if err != nil {
return nil, fmt.Errorf("cannot parse game id: %w", err)
}
rawRounds := strings.Split(parts[1], "; ")
rounds := make([]Round, len(rawRounds))
for i := range rawRounds {
for _, record := range strings.Split(rawRounds[i], ", ") {
recordParts := strings.Split(record, " ")
if len(recordParts) != 2 {
return nil, fmt.Errorf("cannot split record '%s' on cound and color", record)
}
count, err := strconv.ParseUint(recordParts[0], 10, 8)
if err != nil {
return nil, fmt.Errorf("cannot parse '%s' as cubes cound: %w", recordParts[0], err)
}
switch recordParts[1] {
default:
continue
case "blue":
rounds[i].Blue += uint8(count)
case "green":
rounds[i].Green += uint8(count)
case "red":
rounds[i].Red += uint8(count)
}
}
}
return &Game{
ID: uint8(id),
Rounds: rounds,
}, nil
}
func (b Bag) Cubes() uint8 {
return b.Blue + b.Green + b.Red
}
func (r Round) Cubes() uint8 {
return r.Blue + r.Green + r.Red
}
func (r Round) IsPossible(bag Bag) bool {
return r.Cubes() <= bag.Cubes() &&
r.Blue <= bag.Blue &&
r.Green <= bag.Green &&
r.Red <= bag.Red
}
func (g Game) IsPossible(bag Bag) bool {
for i := range g.Rounds {
if g.Rounds[i].IsPossible(bag) {
continue
}
return false
}
return true
}
func GetSecretNumber(r io.Reader, target Bag) (uint, error) {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)
var result uint
for scanner.Scan() {
line := scanner.Text()
game, err := ParseGame(line)
if err != nil {
return 0, fmt.Errorf("cannot parse game from line '%s': %w", line, err)
}
if !game.IsPossible(target) {
continue
}
result += uint(game.ID)
}
return result, nil
}

View File

@ -0,0 +1,180 @@
package part1_test
import (
"fmt"
"log"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"source.toby3d.me/toby3d/adventofcode/internal/day2/part1"
)
func TestParseGame(t *testing.T) {
t.Parallel()
for name, tc := range map[string]struct {
input string
expect part1.Game
}{
"1": {
input: "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green",
expect: part1.Game{ID: 1, Rounds: []part1.Round{
{Blue: 3, Red: 4},
{Red: 1, Green: 2, Blue: 6},
{Green: 2},
}},
},
"2": {
input: "Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue",
expect: part1.Game{ID: 2, Rounds: []part1.Round{
{Blue: 1, Green: 2},
{Green: 3, Blue: 4, Red: 1},
{Green: 1, Blue: 1},
}},
},
"3": {
input: "Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red",
expect: part1.Game{ID: 3, Rounds: []part1.Round{
{Green: 8, Blue: 6, Red: 20},
{Blue: 5, Red: 4, Green: 13},
{Green: 5, Red: 1},
}},
},
"4": {
input: "Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red",
expect: part1.Game{ID: 4, Rounds: []part1.Round{
{Green: 1, Red: 3, Blue: 6},
{Green: 3, Red: 6},
{Green: 3, Blue: 15, Red: 14},
}},
},
"5": {
input: "Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green",
expect: part1.Game{ID: 5, Rounds: []part1.Round{
{Red: 6, Blue: 1, Green: 3},
{Blue: 2, Red: 1, Green: 2},
}},
},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
actual, err := part1.ParseGame(tc.input)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(*actual, tc.expect); diff != "" {
t.Error(diff)
}
})
}
}
func TestGame_IsPossible(t *testing.T) {
t.Parallel()
bag := part1.Bag{
Red: 12,
Green: 13,
Blue: 14,
}
for name, tc := range map[string]struct {
input part1.Game
expect bool
}{
"1": {
input: part1.Game{ID: 1, Rounds: []part1.Round{
{Blue: 3, Red: 4},
{Red: 1, Green: 2, Blue: 6},
{Green: 2},
}},
expect: true,
},
"2": {
input: part1.Game{ID: 2, Rounds: []part1.Round{
{Blue: 1, Green: 2},
{Green: 3, Blue: 4, Red: 1},
{Green: 1, Blue: 1},
}},
expect: true,
},
"3": {
input: part1.Game{ID: 3, Rounds: []part1.Round{
{Green: 8, Blue: 6, Red: 20},
{Blue: 5, Red: 4, Green: 13},
{Green: 5, Red: 1},
}},
expect: false,
},
"4": {
input: part1.Game{ID: 4, Rounds: []part1.Round{
{Green: 1, Red: 3, Blue: 6},
{Green: 3, Red: 6},
{Green: 3, Blue: 15, Red: 14},
}},
expect: false,
},
"5": {
input: part1.Game{ID: 5, Rounds: []part1.Round{
{Red: 6, Blue: 1, Green: 3},
{Blue: 2, Red: 1, Green: 2},
}},
expect: true,
},
} {
name, tc := name, tc
t.Run(name, func(t *testing.T) {
t.Parallel()
if actual := tc.input.IsPossible(bag); actual != tc.expect {
t.Errorf("want %t, got %t", tc.expect, actual)
}
})
}
}
func Example() {
// You play several games and record the information from each game
// (your puzzle input). Each game is listed with its ID number (like the
// 11 in Game 11: ...) followed by a semicolon-separated list of subsets
// of cubes that were revealed from the bag (like 3 red, 5 green, 4
// blue).
//
// In game 1, three sets of cubes are revealed from the bag (and then
// put back again). The first set is 3 blue cubes and 4 red cubes; the
// second set is 1 red cube, 2 green cubes, and 6 blue cubes; the third
// set is only 2 green cubes.
input := "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green\n" +
"Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue\n" +
"Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red\n" +
"Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red\n" +
"Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green"
// The Elf would first like to know which games would have been possible
// if the bag contained only 12 red cubes, 13 green cubes, and 14 blue
// cubes?
result, err := part1.GetSecretNumber(strings.NewReader(input), part1.Bag{
Red: 12,
Green: 13,
Blue: 14,
})
if err != nil {
log.Fatalln(err)
}
// In the example above, games 1, 2, and 5 would have been possible if
// the bag had been loaded with that configuration. However, game 3
// would have been impossible because at one point the Elf showed you 20
// red cubes at once; similarly, game 4 would also have been impossible
// because the Elf showed you 15 blue cubes at once. If you add up the
// IDs of the games that would have been possible, you get 8.
fmt.Println(result)
// Output: 8
}