mirror of
https://github.com/Luzifer/aoc2019.git
synced 2024-12-22 05:51:16 +00:00
Add solution for Day 8
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
90216aba66
commit
b516f08460
4 changed files with 206 additions and 0 deletions
146
day08.go
Normal file
146
day08.go
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
day08ColorBlack int = iota
|
||||||
|
day08ColorWhite
|
||||||
|
day08ColorTransparent
|
||||||
|
)
|
||||||
|
|
||||||
|
type day08Layer struct {
|
||||||
|
data []int
|
||||||
|
w, h int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d day08Layer) countNumber(n int) int {
|
||||||
|
var count int
|
||||||
|
|
||||||
|
for _, v := range d.data {
|
||||||
|
if v == n {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func day08ComposeLayers(layers []day08Layer) day08Layer {
|
||||||
|
var finalLayerData = make([]int, layers[0].w*layers[0].h)
|
||||||
|
|
||||||
|
for i := len(layers) - 1; i >= 0; i-- {
|
||||||
|
for idx, col := range layers[i].data {
|
||||||
|
if col == day08ColorTransparent {
|
||||||
|
// Transparent, ignore
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
finalLayerData[idx] = col
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return day08Layer{data: finalLayerData, w: layers[0].w, h: layers[0].h}
|
||||||
|
}
|
||||||
|
|
||||||
|
func day08ParseToLayers(in string, w, h int) ([]day08Layer, error) {
|
||||||
|
var layers []day08Layer
|
||||||
|
|
||||||
|
if len(in)%(w*h) != 0 {
|
||||||
|
return nil, errors.Errorf("Unexpected input length for format %dx%d: %d", w, h, len(in))
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos int
|
||||||
|
for pos < len(in) {
|
||||||
|
var lData []int
|
||||||
|
for _, c := range in[pos : pos+w*h] {
|
||||||
|
v, err := strconv.Atoi(string(c))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Unable to parse digit")
|
||||||
|
}
|
||||||
|
lData = append(lData, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
layers = append(layers, day08Layer{data: lData, w: w, h: h})
|
||||||
|
pos += w * h
|
||||||
|
}
|
||||||
|
|
||||||
|
return layers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func day08RenderLayer(layer day08Layer) image.Image {
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, layer.w, layer.h))
|
||||||
|
|
||||||
|
for idx, px := range layer.data {
|
||||||
|
var (
|
||||||
|
x = idx % layer.w
|
||||||
|
y = idx / layer.w
|
||||||
|
)
|
||||||
|
|
||||||
|
switch px {
|
||||||
|
case day08ColorBlack:
|
||||||
|
img.SetRGBA(x, y, color.RGBA{0x0, 0x0, 0x0, 0xff})
|
||||||
|
case day08ColorWhite:
|
||||||
|
img.SetRGBA(x, y, color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return img
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay8Part1(inFile string) (int, error) {
|
||||||
|
raw, err := ioutil.ReadFile(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to read input file")
|
||||||
|
}
|
||||||
|
|
||||||
|
layers, err := day08ParseToLayers(strings.TrimSpace(string(raw)), 25, 6)
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Wrap(err, "Unable to parse layers")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
fewest = math.MaxInt64
|
||||||
|
layer day08Layer
|
||||||
|
)
|
||||||
|
for _, l := range layers {
|
||||||
|
if n := l.countNumber(0); n < fewest {
|
||||||
|
fewest = n
|
||||||
|
layer = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer.countNumber(1) * layer.countNumber(2), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveDay8Part2(inFile string) error {
|
||||||
|
raw, err := ioutil.ReadFile(inFile)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to read input file")
|
||||||
|
}
|
||||||
|
|
||||||
|
layers, err := day08ParseToLayers(strings.TrimSpace(string(raw)), 25, 6)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to parse layers")
|
||||||
|
}
|
||||||
|
|
||||||
|
img := day08RenderLayer(day08ComposeLayers(layers))
|
||||||
|
|
||||||
|
f, err := os.Create("day08_image.png")
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Unable to open result file")
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return errors.Wrap(png.Encode(f, img), "Unable to render image")
|
||||||
|
}
|
BIN
day08_image.png
Normal file
BIN
day08_image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 144 B |
1
day08_input.txt
Normal file
1
day08_input.txt
Normal file
File diff suppressed because one or more lines are too long
59
day08_test.go
Normal file
59
day08_test.go
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package aoc2019
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDay08ComposeLayers(t *testing.T) {
|
||||||
|
var (
|
||||||
|
exp = []int{0, 1, 1, 0}
|
||||||
|
in = "0222112222120000"
|
||||||
|
w = 2
|
||||||
|
h = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
layers, err := day08ParseToLayers(in, w, h)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Layer parser failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
layer := day08ComposeLayers(layers)
|
||||||
|
if !reflect.DeepEqual(layer.data, exp) {
|
||||||
|
t.Errorf("Did not yield expected composed layer: exp=%+v got=%+v", exp, layer.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDay08ParseToLayers(t *testing.T) {
|
||||||
|
var (
|
||||||
|
in = "123456789012"
|
||||||
|
w = 3
|
||||||
|
h = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
layers, err := day08ParseToLayers(in, w, h)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Layer parser failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(layers) != 2 || !reflect.DeepEqual(layers[0].data, []int{1, 2, 3, 4, 5, 6}) || !reflect.DeepEqual(layers[1].data, []int{7, 8, 9, 0, 1, 2}) {
|
||||||
|
t.Errorf("Did not yield expected layers (123 456) (789 012): got=%#v", layers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay8_Part1(t *testing.T) {
|
||||||
|
count, err := solveDay8Part1("day08_input.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Day 8 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("Solution Day 8 Part 1: %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculateDay8_Part2(t *testing.T) {
|
||||||
|
if err := solveDay8Part2("day08_input.txt"); err != nil {
|
||||||
|
t.Fatalf("Day 8 solver failed: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Solution Day 8 Part 2: See day08_image.png")
|
||||||
|
}
|
Loading…
Reference in a new issue