Skip to content

Commit fb50261

Browse files
committed
day 14 2020
1 parent b07e0b5 commit fb50261

File tree

3 files changed

+855
-2
lines changed

3 files changed

+855
-2
lines changed

2020/day14/day14.go

+178-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,183 @@
11
package day14
22

3-
import "fmt"
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
"strings"
8+
)
49

510
func Run(lines []string) error {
6-
return fmt.Errorf("not yet implemented")
11+
cpu := NewCPU(1)
12+
13+
err := cpu.Run(lines)
14+
if err != nil {
15+
return err
16+
}
17+
sum := cpu.SumMemory()
18+
fmt.Println("Part 1:", sum)
19+
20+
cpu = NewCPU(2)
21+
22+
err = cpu.Run(lines)
23+
if err != nil {
24+
return err
25+
}
26+
sum = cpu.SumMemory()
27+
fmt.Println("Part 2:", sum)
28+
29+
return nil
30+
}
31+
32+
func applyMaskV2(value int64, mask string) (string, error) {
33+
var result strings.Builder
34+
for i := range mask {
35+
if mask[i] == '0' {
36+
valueBit := (value >> (35 - i)) & 0b1
37+
result.WriteString(fmt.Sprint(valueBit))
38+
} else {
39+
err := result.WriteByte(mask[i])
40+
if err != nil {
41+
return "", fmt.Errorf("error writing to builder: %v", err)
42+
}
43+
}
44+
}
45+
return result.String(), nil
46+
}
47+
48+
func applyMask(value int64, mask string) int64 {
49+
var output int64
50+
output = 0
51+
for i := 0; i < 36; i++ {
52+
switch mask[35-i] {
53+
case 'X':
54+
// Copy the current flag value
55+
output |= (value & (int64(1) << i))
56+
case '1':
57+
output |= 1 << i
58+
}
59+
// For a 0, just leave the output at zero
60+
}
61+
return output
62+
}
63+
64+
func expandFloating(input string) []string {
65+
results := []string{}
66+
for _, chr := range input {
67+
if chr == 'X' {
68+
results = expandOne(results, []string{"0", "1"})
69+
} else {
70+
results = expandOne(results, []string{string(chr)})
71+
}
72+
}
73+
74+
return results
75+
}
76+
77+
func expandOne(inputs []string, expansions []string) []string {
78+
if len(inputs) == 0 {
79+
return expansions
80+
} else {
81+
results := make([]string, 0, len(expansions)*len(inputs))
82+
for _, input := range inputs {
83+
for _, exp := range expansions {
84+
results = append(results, input+exp)
85+
}
86+
}
87+
return results
88+
}
89+
}
90+
91+
type CPU struct {
92+
Mask string
93+
Memory map[int64]int64
94+
Version int
95+
}
96+
97+
func NewCPU(version int) CPU {
98+
return CPU{Mask: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", Memory: make(map[int64]int64), Version: version}
99+
}
100+
101+
var memMatcher = regexp.MustCompile(`^mem\[(\d+)\] = (\d+)$`)
102+
103+
func (c *CPU) SumMemory() int64 {
104+
sum := int64(0)
105+
for _, val := range c.Memory {
106+
sum += val
107+
}
108+
return sum
109+
}
110+
111+
func (c *CPU) Run(program []string) error {
112+
for _, line := range program {
113+
err := c.Step(line)
114+
if err != nil {
115+
return fmt.Errorf("error executing '%s': %v", line, err)
116+
}
117+
}
118+
return nil
119+
}
120+
121+
func (c *CPU) Step(line string) error {
122+
if strings.HasPrefix(line, "mask = ") {
123+
newMask := line[7:]
124+
if len(newMask) != 36 {
125+
return fmt.Errorf("invalid instruction: %s", line)
126+
}
127+
c.Mask = newMask
128+
} else {
129+
matches := memMatcher.FindStringSubmatch(line)
130+
if len(matches) == 3 {
131+
addr, err := strconv.ParseInt(matches[1], 10, 64)
132+
if err != nil {
133+
return fmt.Errorf("invalid address: %s", matches[1])
134+
}
135+
value, err := strconv.ParseInt(matches[2], 10, 64)
136+
if err != nil {
137+
return fmt.Errorf("invalid value: %s", matches[2])
138+
}
139+
140+
addrs, err := c.getAddrs(addr)
141+
if err != nil {
142+
return err
143+
}
144+
value = c.getVal(value)
145+
146+
for _, addr := range addrs {
147+
c.Memory[addr] = value
148+
}
149+
} else {
150+
return fmt.Errorf("invalid instruction: %s", line)
151+
}
152+
}
153+
return nil
154+
}
155+
156+
func (c *CPU) getAddrs(addr int64) ([]int64, error) {
157+
if c.Version == 1 {
158+
return []int64{addr}, nil
159+
} else {
160+
mask, err := applyMaskV2(addr, c.Mask)
161+
if err != nil {
162+
return nil, err
163+
}
164+
strs := expandFloating(mask)
165+
results := make([]int64, 0, len(strs))
166+
for _, str := range strs {
167+
i, err := strconv.ParseInt(str, 2, 64)
168+
if err != nil {
169+
return nil, fmt.Errorf("failed to parse input '%s'", str)
170+
}
171+
results = append(results, i)
172+
}
173+
return results, nil
174+
}
175+
}
176+
177+
func (c *CPU) getVal(val int64) int64 {
178+
if c.Version == 1 {
179+
return applyMask(val, c.Mask)
180+
} else {
181+
return val
182+
}
7183
}

2020/day14/day14_test.go

+123
Original file line numberDiff line numberDiff line change
@@ -1 +1,124 @@
11
package day14
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestApplyMask(t *testing.T) {
10+
mask := "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X"
11+
require.Equal(t, int64(73), applyMask(11, mask))
12+
require.Equal(t, int64(101), applyMask(101, mask))
13+
require.Equal(t, int64(64), applyMask(0, mask))
14+
}
15+
16+
func TestVersion1(t *testing.T) {
17+
program := []string{
18+
"mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X",
19+
"mem[8] = 11",
20+
"mem[7] = 101",
21+
"mem[8] = 0",
22+
}
23+
24+
cpu := NewCPU(1)
25+
26+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", cpu.Mask)
27+
require.Equal(t, 0, len(cpu.Memory))
28+
29+
require.NoError(t, cpu.Step(program[0]))
30+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X", cpu.Mask)
31+
require.Equal(t, 0, len(cpu.Memory))
32+
33+
require.NoError(t, cpu.Step(program[1]))
34+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X", cpu.Mask)
35+
require.Equal(t, 1, len(cpu.Memory))
36+
require.Equal(t, int64(73), cpu.Memory[8])
37+
38+
require.NoError(t, cpu.Step(program[2]))
39+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X", cpu.Mask)
40+
require.Equal(t, 2, len(cpu.Memory))
41+
require.Equal(t, int64(73), cpu.Memory[8])
42+
require.Equal(t, int64(101), cpu.Memory[7])
43+
44+
require.NoError(t, cpu.Step(program[3]))
45+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X", cpu.Mask)
46+
require.Equal(t, 2, len(cpu.Memory))
47+
require.Equal(t, int64(64), cpu.Memory[8])
48+
require.Equal(t, int64(101), cpu.Memory[7])
49+
}
50+
51+
func TestVersion2(t *testing.T) {
52+
program := []string{
53+
"mask = 000000000000000000000000000000X1001X",
54+
"mem[42] = 100",
55+
"mask = 00000000000000000000000000000000X0XX",
56+
"mem[26] = 1",
57+
}
58+
59+
cpu := NewCPU(2)
60+
61+
require.Equal(t, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", cpu.Mask)
62+
require.Equal(t, 0, len(cpu.Memory))
63+
64+
require.NoError(t, cpu.Step(program[0]))
65+
require.Equal(t, "000000000000000000000000000000X1001X", cpu.Mask)
66+
require.Equal(t, 0, len(cpu.Memory))
67+
68+
require.NoError(t, cpu.Step(program[1]))
69+
require.Equal(t, "000000000000000000000000000000X1001X", cpu.Mask)
70+
require.Equal(t, 4, len(cpu.Memory))
71+
require.Equal(t, int64(100), cpu.Memory[26])
72+
require.Equal(t, int64(100), cpu.Memory[27])
73+
require.Equal(t, int64(100), cpu.Memory[58])
74+
require.Equal(t, int64(100), cpu.Memory[59])
75+
76+
require.NoError(t, cpu.Step(program[2]))
77+
require.Equal(t, "00000000000000000000000000000000X0XX", cpu.Mask)
78+
require.Equal(t, 4, len(cpu.Memory))
79+
80+
require.NoError(t, cpu.Step(program[3]))
81+
require.Equal(t, "00000000000000000000000000000000X0XX", cpu.Mask)
82+
require.Equal(t, 10, len(cpu.Memory))
83+
require.Equal(t, int64(1), cpu.Memory[16])
84+
require.Equal(t, int64(1), cpu.Memory[17])
85+
require.Equal(t, int64(1), cpu.Memory[18])
86+
require.Equal(t, int64(1), cpu.Memory[19])
87+
require.Equal(t, int64(1), cpu.Memory[24])
88+
require.Equal(t, int64(1), cpu.Memory[25])
89+
require.Equal(t, int64(1), cpu.Memory[26])
90+
require.Equal(t, int64(1), cpu.Memory[27])
91+
92+
require.Equal(t, int64(100), cpu.Memory[58])
93+
require.Equal(t, int64(100), cpu.Memory[59])
94+
}
95+
96+
func TestExpandFloating(t *testing.T) {
97+
require.Equal(t, []string{
98+
"000000000000000000000000000000011010",
99+
"000000000000000000000000000000011011",
100+
"000000000000000000000000000000111010",
101+
"000000000000000000000000000000111011",
102+
}, expandFloating("000000000000000000000000000000X1101X"))
103+
104+
require.Equal(t, []string{
105+
"000000000000000000000000000000010000",
106+
"000000000000000000000000000000010001",
107+
"000000000000000000000000000000010010",
108+
"000000000000000000000000000000010011",
109+
"000000000000000000000000000000011000",
110+
"000000000000000000000000000000011001",
111+
"000000000000000000000000000000011010",
112+
"000000000000000000000000000000011011",
113+
}, expandFloating("00000000000000000000000000000001X0XX"))
114+
}
115+
116+
func TestApplyMaskV2(t *testing.T) {
117+
result, err := applyMaskV2(42, "000000000000000000000000000000X1001X")
118+
require.NoError(t, err)
119+
require.Equal(t, "000000000000000000000000000000X1101X", result)
120+
121+
result, err = applyMaskV2(26, "00000000000000000000000000000000X0XX")
122+
require.NoError(t, err)
123+
require.Equal(t, "00000000000000000000000000000001X0XX", result)
124+
}

0 commit comments

Comments
 (0)