aoc-2020/d14/main.go

201 lines
3.7 KiB
Go

package main
import (
"bufio"
"fmt"
"log"
"math"
"os"
"regexp"
"strconv"
"strings"
)
func processLines(path string, f func(string) (stop bool, err error)) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
stop, err := f(scanner.Text())
if stop || err != nil {
return err
}
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
var memExtractor = regexp.MustCompile(`^mem\[([0-9]+)] = ([0-9]+)`)
func powInt(x, y int) int {
return int(math.Pow(float64(x), float64(y)))
}
func applyMask(mask string, v int) (int, error) {
for i := 0; i < len(mask); i++ {
m := mask[len(mask)-i-1]
switch m {
case 'X':
case '1':
v |= powInt(2, i)
case '0':
v &^= powInt(2, i)
default:
return 0, fmt.Errorf("could not apply mask %v in position %d", m, i)
}
}
return v, nil
}
func sprintBinArray(a []int, z int) string {
r := " "
for _, v := range a {
f := fmt.Sprintf("%%%db(%%d)", z)
r += fmt.Sprintf(f, v, v)
}
return fmt.Sprintf("[%s]", r)
}
func applyFloatingMask(mask string, v int) ([]int, error) {
base2 := 2
results := []int{v}
// fmt.Printf("Applying %s to %8b\n", mask, v)
for i := 0; i < len(mask); i++ {
m := mask[len(mask)-i-1]
// fmt.Printf("Apply %s at %d ", string(m), i)
resultLen := len(results)
for j := 0; j < resultLen; j++ {
switch m {
case 'X':
// Add a 0 mask to end of results
results = append(results, results[j]|powInt(base2, i))
// Apply a 1 mask in place
results[j] &^= powInt(base2, i)
case '1':
results[j] |= powInt(base2, i)
case '0':
// results[j] &^= powInt(base2, i)
default:
return results, fmt.Errorf("could not apply mask %v in position %d", m, i)
}
}
// fmt.Printf("Result %s\n", sprintBinArray(results, 8))
}
return results, nil
}
func parseMemValues(line string) (loc int, val int, err error) {
match := memExtractor.FindStringSubmatch(line)
if match == nil {
err = fmt.Errorf("could not extract mem val in %s", line)
return
}
loc, err = strconv.Atoi(match[1])
if err != nil {
err = fmt.Errorf("error parsing int from %s: %w", line, err)
return
}
val, err = strconv.Atoi(match[2])
if err != nil {
err = fmt.Errorf("error parsing int from %s: %w", line, err)
return
}
return
}
func part1() {
var mask string
mem := map[int]int{}
if err := processLines("input.txt", func(line string) (bool, error) {
if strings.HasPrefix(line, "mask") {
mask = strings.TrimPrefix(line, "mask = ")
} else {
loc, val, err := parseMemValues(line)
if err != nil {
return false, err
}
val, err = applyMask(mask, val)
if err != nil {
return false, fmt.Errorf("error applying mask to value on line %s: %w", line, err)
}
// fmt.Printf("mem[%d] = %d\n", loc, val)
mem[loc] = val
}
return false, nil
}); err != nil {
log.Fatal(err)
}
sum := 0
for _, v := range mem {
sum += v
}
fmt.Printf("Total value in memory %d\n", sum)
}
func part2() {
var mask string
mem := map[int]int{}
if err := processLines("input.txt", func(line string) (bool, error) {
if strings.HasPrefix(line, "mask") {
mask = strings.TrimPrefix(line, "mask = ")
} else {
loc, val, err := parseMemValues(line)
if err != nil {
return false, err
}
locs, err := applyFloatingMask(mask, loc)
if err != nil {
return false, fmt.Errorf("error applying mask to value on line %s: %w", line, err)
}
for _, l := range locs {
mem[l] = val
}
}
return false, nil
}); err != nil {
log.Fatal(err)
}
sum := 0
for _, v := range mem {
sum += v
}
fmt.Printf("Total value in memory %d\n", sum)
}
func main() {
part1()
part2()
}