diff --git a/.github/README.md b/.github/README.md index f499e4e..0b4b50c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -29,7 +29,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have | [11](/11-seatingSystem) \* | ![Completed][check] | [Link](/11-seatingSystem/python) | [Link](/11-seatingSystem/python) | | [12](/12-rainRisk) \* | ![Completed][check] | [Link](/12-rainRisk/python) | [Link](/12-rainRisk/go) | | [13](/13-shuttleSearch) | ![Partially complete][partial] | [Link](/13-shuttleSearch/python) | | -| [14](/14-dockingData) | ![Partially complete][partial] | [Link](/14-dockingData/python) | | +| [14](/14-dockingData) | ![Completed][check] | [Link](/14-dockingData/python) | [Link](/14-dockingData/go) | | 15 | | | | | 16 | | | | | 17 | | | | diff --git a/14-dockingData/README.md b/14-dockingData/README.md index 3856a1c..c706808 100644 --- a/14-dockingData/README.md +++ b/14-dockingData/README.md @@ -11,6 +11,18 @@ Test cases 1.1 pass 2.1 pass +Answers +Part 1: 8570568288597 +Part 2: 3289441921203 + +❯ go run .\go\ +AoC 2020: day 14 - Docking Data +Go go1.15.2 + +Test cases +1.1 pass +2.1 pass + Answers Part 1: 8570568288597 Part 2: 3289441921203 diff --git a/14-dockingData/go/challenge/common.go b/14-dockingData/go/challenge/common.go new file mode 100644 index 0000000..7d420f6 --- /dev/null +++ b/14-dockingData/go/challenge/common.go @@ -0,0 +1,77 @@ +package challenge + +import ( + "errors" + "fmt" + "regexp" + "strconv" + "strings" +) + +type Instruction struct { + Address int + Value int + Mask string +} + +var instructionRegex = regexp.MustCompile(`mem\[(\d+)\] ?= ?(\d+)`) + +func NewInstruction(instruction, mask string) Instruction { + if mask == "" { + panic(errors.New("bad mask")) + } + if !instructionRegex.MatchString(instruction) { + panic(fmt.Errorf("unable to parse input instruction '%s'", instruction)) + } + matches := instructionRegex.FindAllStringSubmatch(instruction, -1) + address, err := strconv.Atoi(matches[0][1]) + if err != nil { + panic(err) + } + value, err := strconv.Atoi(matches[0][2]) + if err != nil { + panic(err) + } + return Instruction{ + Address: address, + Value: value, + Mask: mask, + } +} + +func parse(instr string) (retval []Instruction) { + + var currentMask string + for _, line := range strings.Split(strings.TrimSpace(instr), "\n") { + if strings.Contains(line, "mask") { + currentMask = strings.TrimSpace(strings.Split(line, "=")[1]) + } else { + retval = append(retval, NewInstruction(line, currentMask)) + } + } + + return +} + +func numberToBase(n, b int) string { + if n == 0 { + return "0" + } + var digits []int + for n != 0 { + digits = append(digits, n%b) + n /= b + } + var sarr []string + for i := len(digits) - 1; i >= 0; i -= 1 { + sarr = append(sarr, strconv.Itoa(digits[i])) + } + return strings.Join(sarr, "") +} + +func zfill(instr string, n int) string { + for i := 0; i < n; i += 1 { + instr = "0" + instr + } + return instr +} \ No newline at end of file diff --git a/14-dockingData/go/challenge/partOne.go b/14-dockingData/go/challenge/partOne.go new file mode 100644 index 0000000..aefef87 --- /dev/null +++ b/14-dockingData/go/challenge/partOne.go @@ -0,0 +1,43 @@ +package challenge + +import ( + "strconv" + "strings" +) + +func applyMask(number int, mask string) int { + mask = strings.ToLower(mask) + value := numberToBase(number, 2) + value = zfill(value, len(mask)-len(value)) + var combi string + for i := 0; i < len(value); i += 1 { + v := string(value[i]) + m := string(mask[i]) + + if m == "x" { + combi += v + } else { + combi += m + } + } + ix, err := strconv.ParseInt(combi, 2, 64) + if err != nil { + panic(err) + } + return int(ix) +} + +func PartOne(instr string) int { + inputInstructions := parse(instr) + memory := make(map[int]int) + + for _, instruction := range inputInstructions { + memory[instruction.Address] = applyMask(instruction.Value, instruction.Mask) + } + + var sigma int + for _, v := range memory { + sigma += v + } + return sigma +} diff --git a/14-dockingData/go/challenge/partTwo.go b/14-dockingData/go/challenge/partTwo.go new file mode 100644 index 0000000..5ce4f9a --- /dev/null +++ b/14-dockingData/go/challenge/partTwo.go @@ -0,0 +1,99 @@ +package challenge + +import ( + "strconv" + "strings" +) + +// The following two functions were taken from https://stackoverflow.com/a/19249957 +func generateCombinations(alphabet string, length int) <-chan string { + c := make(chan string) + go func(c chan string) { + defer close(c) + addLetter(c, "", alphabet, length) + }(c) + return c +} +func addLetter(c chan string, combo string, alphabet string, length int) { + if length <= 0 { + return + } + + var newCombo string + for _, ch := range alphabet { + newCombo = combo + string(ch) + c <- newCombo + addLetter(c, newCombo, alphabet, length-1) + } +} + +// end taken functions + +func getMemoryAddresses(number int, mask string) []int { + mask = strings.ToLower(mask) + value := numberToBase(number, 2) + value = zfill(value, len(mask)-len(value)) + + var combi []string + + for i := 0; i < len(value); i += 1 { + v := string(value[i]) + m := string(mask[i]) + + var av string + + if m == "0" { + av = v + } else if m == "1" { + av = "1" + } else { + av = m + } + + combi = append(combi, av) + } + + var xns []int + + n := strings.Count(mask, "x") + for valstring := range generateCombinations("01", n) { + if len(valstring) != n { + continue + } + val_combo := strings.Split(valstring, "") + msk := make([]string, len(combi)) + copy(msk, combi) + var val_counter int + for i, char := range msk { + if char == "x" { + msk[i] = val_combo[val_counter] + val_counter += 1 + } + } + ix, err := strconv.ParseInt(strings.Join(msk, ""), 2, 64) + if err != nil { + panic(err) + } + xns = append(xns, int(ix)) + } + + return xns +} + +func PartTwo(instr string) int { + inputInstructions := parse(instr) + memory := make(map[int]int) + + for _, instruction := range inputInstructions { + addresses := getMemoryAddresses(instruction.Address, instruction.Mask) + for _, address := range addresses { + memory[address] = instruction.Value + } + } + + var sigma int + for _, v := range memory { + sigma += v + } + return sigma +} diff --git a/14-dockingData/go/main.go b/14-dockingData/go/main.go new file mode 100644 index 0000000..5a0c97a --- /dev/null +++ b/14-dockingData/go/main.go @@ -0,0 +1,100 @@ +package main + +import ( + "adventOfCode/14-dockingData/go/challenge" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + + "github.com/fatih/color" +) + +func main() { + var infoStruct info + { + inb, err := ioutil.ReadFile("info.json") + if err != nil { + fmt.Println("Error: could not open info.json") + os.Exit(-1) + } + err = json.Unmarshal(inb, &infoStruct) + if err != nil { + fmt.Println("Error: could not parse info.json") + os.Exit(-1) + } + } + + var ( + year = infoStruct.Year + day = infoStruct.Day + title = infoStruct.Title + ) + + fmt.Fprintf(color.Output, "%s: day %s - %s\n", color.YellowString("AoC "+year), color.BlueString(day), title) + fmt.Fprintf(color.Output, "Go %s\n\n", color.BlueString(runtime.Version())) + + var challengeInput string + { + inb, err := ioutil.ReadFile("input.txt") + if err != nil { + fmt.Println("Error: could not open input.txt") + os.Exit(-1) + } + challengeInput = string(inb) + } + + runTests(infoStruct) + + fmt.Println("Answers") + fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("1"), color.BlueString(strconv.Itoa(challenge.PartOne(challengeInput)))) + fmt.Fprintf(color.Output, "Part %s: %s\n", color.BlueString("2"), color.BlueString(strconv.Itoa(challenge.PartTwo(challengeInput)))) + +} + +type tc struct { + Input string `json:"input"` + Expected int `json:"expected"` +} + +type info struct { + Year string `json:"year"` + Day string `json:"day"` + Title string `json:"title"` + TestCases struct { + One []tc `json:"one"` + Two []tc `json:"two"` + } `json:"testCases"` +} + +func runTests(infoStruct info) { + + if len(infoStruct.TestCases.One) == 0 && len(infoStruct.TestCases.Two) == 0 { + fmt.Println("Info: no test cases specified. Skipping tests") + fmt.Println() + return + } + + fmt.Println("Test cases") + + rt := func(tcs []tc, f func(string) int, n string) { + for i, tc := range tcs { + fmt.Fprintf(color.Output, "%s ", color.BlueString(n+"."+strconv.Itoa(i+1))) + result := f(tc.Input) + if result == tc.Expected { + fmt.Fprintf(color.Output, "%s", color.GreenString("pass")) + } else { + fmt.Fprintf(color.Output, "%s (got %s, expected %s)", color.RedString("fail"), color.BlueString(strconv.Itoa(result)), color.BlueString(strconv.Itoa(tc.Expected))) + } + fmt.Println() + } + } + + rt(infoStruct.TestCases.One, challenge.PartOne, "1") + rt(infoStruct.TestCases.Two, challenge.PartTwo, "2") + + fmt.Println() + +}