From 41352399f92fb7f5bcbc3dfe93b630f7fd1e7f3c Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Mon, 16 Dec 2019 14:56:41 +0100 Subject: [PATCH] Add solution for Day 16 Signed-off-by: Knut Ahlers --- day16.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++++ day16_input.txt | 1 + day16_test.go | 97 +++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+) create mode 100644 day16.go create mode 100644 day16_input.txt create mode 100644 day16_test.go diff --git a/day16.go b/day16.go new file mode 100644 index 0000000..4faf5a8 --- /dev/null +++ b/day16.go @@ -0,0 +1,141 @@ +package aoc2019 + +import ( + "bytes" + "io/ioutil" + "math" + "strconv" + "strings" + + "github.com/pkg/errors" +) + +var day16BasePattern = []int64{0, 1, 0, -1} + +type day16Pattern struct { + pattern []int64 + ptr int +} + +func (d *day16Pattern) get() int64 { + d.ptr += 1 + if d.ptr == len(d.pattern) { + d.ptr = 0 + } + + return d.pattern[d.ptr] +} + +func day16PatternForElement(idx int) *day16Pattern { + var pattern []int64 + + for _, p := range day16BasePattern { + for i := 0; i <= idx; i++ { + pattern = append(pattern, p) + } + } + + return &day16Pattern{pattern: pattern, ptr: 0} +} + +func day16ReadInputSignal(in string) ([]int64, error) { + var out []int64 + + for _, c := range in { + v, err := strconv.ParseInt(string(c), 10, 64) + if err != nil { + return nil, errors.Wrap(err, "Unable to parse number") + } + out = append(out, v) + } + + return out, nil +} + +func day16ProcessSignal(s []int64, phases int) []int64 { + var processedSignal = make([]int64, len(s)) + copy(processedSignal, s) + + for i := 0; i < phases; i++ { + var tmpSignal = make([]int64, len(processedSignal)) + for i := range tmpSignal { + p := day16PatternForElement(i) + + for _, n := range processedSignal { + tmpSignal[i] += n * p.get() + } + + tmpSignal[i] = int64(math.Abs(float64(tmpSignal[i]))) % 10 + } + + copy(processedSignal, tmpSignal) + } + + return processedSignal +} + +func solveDay16Part1(inFile string) (string, error) { + raw, err := ioutil.ReadFile(inFile) + if err != nil { + return "", errors.Wrap(err, "Unable to read input file") + } + + s, err := day16ReadInputSignal(strings.TrimSpace(string(raw))) + if err != nil { + return "", errors.Wrap(err, "Unable to parse input signal") + } + + ps := day16ProcessSignal(s, 100)[:8] + + var res string + for _, n := range ps { + res += strconv.FormatInt(n, 10) + } + + return res, nil +} + +func solveDay16Part2(inFile string) (string, error) { + raw, err := ioutil.ReadFile(inFile) + if err != nil { + return "", errors.Wrap(err, "Unable to read input file") + } + + raw = bytes.TrimSpace(raw) + + // Assemble "real signal" + rs := make([]byte, 10000*len(raw)) + for i := 0; i < 10000; i++ { + copy(rs[i*len(raw):(i+1)*len(raw)], raw) + } + + offset, err := strconv.ParseInt(string(raw[0:7]), 10, 64) + if err != nil { + return "", errors.Wrap(err, "Unable to parse offset") + } + rs = rs[offset:] + + s, err := day16ReadInputSignal(string(rs)) + if err != nil { + return "", errors.Wrap(err, "Unable to parse input signal") + } + + // Calculate special solution for offset-trimmed signal + for phase := 0; phase < 100; phase++ { + for i := len(s) - 1; i >= 0; i-- { + var ni int64 + if i+1 < len(s) { + ni = s[i+1] + } + s[i] = int64(math.Abs(float64(ni+s[i]))) % 10 + } + } + + // Marshal output + var res string + for _, n := range s[:8] { + res += strconv.FormatInt(n, 10) + } + + return res, nil +} diff --git a/day16_input.txt b/day16_input.txt new file mode 100644 index 0000000..757bbed --- /dev/null +++ b/day16_input.txt @@ -0,0 +1 @@ +59773419794631560412886746550049210714854107066028081032096591759575145680294995770741204955183395640103527371801225795364363411455113236683168088750631442993123053909358252440339859092431844641600092736006758954422097244486920945182483159023820538645717611051770509314159895220529097322723261391627686997403783043710213655074108451646685558064317469095295303320622883691266307865809481566214524686422834824930414730886697237161697731339757655485312568793531202988525963494119232351266908405705634244498096660057021101738706453735025060225814133166491989584616948876879383198021336484629381888934600383957019607807995278899293254143523702000576897358 diff --git a/day16_test.go b/day16_test.go new file mode 100644 index 0000000..dbaadd3 --- /dev/null +++ b/day16_test.go @@ -0,0 +1,97 @@ +package aoc2019 + +import ( + "reflect" + "testing" +) + +func TestDay16TestPatternForElement(t *testing.T) { + for idx, expPattern := range [][]int64{ + {0, 1, 0, -1}, + {0, 0, 1, 1, 0, 0, -1, -1}, + {0, 0, 0, 1, 1, 1, 0, 0, 0, -1, -1, -1}, + } { + if p := day16PatternForElement(idx); !reflect.DeepEqual(expPattern, p.pattern) { + t.Errorf("Unexpected pattern for idx=%d: exp=%+v got=%+v", idx, expPattern, p.pattern) + } + } +} + +func TestDay16PatternLoop(t *testing.T) { + p1 := day16PatternForElement(0) + + for i, expN := range []int64{ + // MUST skip first element at first execution + // 0, + 1, 0, -1, 0, 1, 0, -1, 0, 1, 0, -1, + } { + if n := p1.get(); n != expN { + t.Errorf("Unexpected number for pattern p1 at index %d: exp=%d got=%d", i, expN, n) + } + } +} + +func TestDay16ParseInputSignal(t *testing.T) { + s, err := day16ReadInputSignal("12345678") + if err != nil { + t.Fatalf("Unable to parse input signal: %s", err) + } + + if !reflect.DeepEqual(s, []int64{1, 2, 3, 4, 5, 6, 7, 8}) { + t.Errorf("Unexpected input signal: %+v", s) + } +} + +func TestDay16ProcessSignal(t *testing.T) { + s, err := day16ReadInputSignal("12345678") + if err != nil { + t.Fatalf("Unable to parse input signal: %s", err) + } + + for i, expS := range [][]int64{ + {1, 2, 3, 4, 5, 6, 7, 8}, + {4, 8, 2, 2, 6, 1, 5, 8}, + {3, 4, 0, 4, 0, 4, 3, 8}, + {0, 3, 4, 1, 5, 5, 1, 8}, + {0, 1, 0, 2, 9, 4, 9, 8}, + } { + if rs := day16ProcessSignal(s, i); !reflect.DeepEqual(rs, expS) { + t.Errorf("Unexpected processed signal after %d phases: exp=%+v got=%+v", i, expS, rs) + } + } +} + +func TestDay16ProcessSignal8OfLonger(t *testing.T) { + for signal, expPSig8 := range map[string][]int64{ + "80871224585914546619083218645595": {2, 4, 1, 7, 6, 1, 7, 6}, + "19617804207202209144916044189917": {7, 3, 7, 4, 5, 4, 1, 8}, + "69317163492948606335995924319873": {5, 2, 4, 3, 2, 1, 3, 3}, + } { + s, err := day16ReadInputSignal(signal) + if err != nil { + t.Fatalf("Unable to parse input signal %q: %s", signal, err) + } + + if rs := day16ProcessSignal(s, 100); !reflect.DeepEqual(rs[:8], expPSig8) { + t.Errorf("Unexpected processed signal for signal %q: exp=%+v got=%+v", signal, expPSig8, rs[:8]) + } + } +} + +func TestCalculateDay16_Part1(t *testing.T) { + res, err := solveDay16Part1("day16_input.txt") + if err != nil { + t.Fatalf("Day 16 solver failed: %s", err) + } + + t.Logf("Solution Day 16 Part 1: %s", res) +} + +func TestCalculateDay16_Part2(t *testing.T) { + res, err := solveDay16Part2("day16_input.txt") + if err != nil { + t.Fatalf("Day 16 solver failed: %s", err) + } + + t.Logf("Solution Day 16 Part 2: %s", res) +}