⭐ Created 2 day puzzle solution
This commit is contained in:
parent
58a0fbf077
commit
17d20ae88d
|
@ -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
2
go.mod
|
@ -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
2
go.sum
|
@ -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=
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue