diff --git a/.github/README.md b/.github/README.md index c1c3b93..38552a5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -40,7 +40,7 @@ Puzzle inputs and descriptions are not included in this repository. You'll have | [22](/22-crabCombat) | ![Partially complete][partial] | [Link](/22-crabCombat/python) | | | | [23](/23-crabCups) | ![Incomplete][cross] | | | | | [24](/24-lobbyLayout) | ![Partially complete][partial] | [Link](/24-lobbyLayout/python) | | | -| 25 | ![Not yet attempted][pending] | | | | +| [25](/25-comboBreaker) | ![Completed][check] | [Link](/25-comboBreaker/python) | [Link](/25-comboBreaker/go) | | diff --git a/25-comboBreaker/README.md b/25-comboBreaker/README.md index a856da3..753cedb 100644 --- a/25-comboBreaker/README.md +++ b/25-comboBreaker/README.md @@ -9,7 +9,7 @@ I might go back and try some of the other challenges from this year that I didn'
Script output ``` -❯ python .\python\ ft +❯ python python ft AoC 2020: day 25 - Combo Breaker Python 3.8.5 @@ -17,8 +17,19 @@ Test cases 1.1 pass in 0.0 seconds Answers -Part 1: 297257 in 7.745995283126831 seconds +Part 1: 297257 in 7.089499235153198 seconds Part 2: 0 in 0.0 seconds + +❯ go run .\go\ ft +AoC 2020: day 25 - Combo Breaker +Go go1.15.2 + +Test cases +1.1 pass in 0.00000000 seconds + +Answers +Part 1: 297257 in 0.13749980 seconds +Part 2: 0 in 0.00000000 seconds ```
diff --git a/25-comboBreaker/go/challenge/common.go b/25-comboBreaker/go/challenge/common.go new file mode 100644 index 0000000..3ec9b21 --- /dev/null +++ b/25-comboBreaker/go/challenge/common.go @@ -0,0 +1,18 @@ +package challenge + +import ( + "strconv" + "strings" +) + +func parse(instr string) []int { + var o []int + for _, x := range strings.Split(strings.TrimSpace(instr), "\n") { + i, err := strconv.Atoi(x) + if err != nil { + panic(err) + } + o = append(o, i) + } + return o +} diff --git a/25-comboBreaker/go/challenge/partOne.go b/25-comboBreaker/go/challenge/partOne.go new file mode 100644 index 0000000..feafa35 --- /dev/null +++ b/25-comboBreaker/go/challenge/partOne.go @@ -0,0 +1,44 @@ +package challenge + +import "errors" + +func singleTransformation(v, subjectNumber int) int { + return (v * subjectNumber) % 20201227 +} + +func transform(loopSize, subjectNumber int) int { + v := 1 + for i := 0; i < loopSize; i += 1 { + v = singleTransformation(v, subjectNumber) + } + return v +} + +func findLoopSize(targetPubkey int) int { + var c int + v := 1 + for { + c += 1 + v = singleTransformation(v, 7) + if v == targetPubkey { + return c + } + } +} + +func PartOne(instr string) int { + inputSlice := parse(instr) + + pubkeyOne := inputSlice[0] + pubkeyTwo := inputSlice[1] + + loopSizeOne := findLoopSize(pubkeyOne) + loopSizeTwo := findLoopSize(pubkeyTwo) + + encryptionKey := transform(loopSizeTwo, pubkeyOne) + if encryptionKey != transform(loopSizeOne, pubkeyTwo) { + panic(errors.New("encryption keys do not match")) + } + + return encryptionKey +} diff --git a/25-comboBreaker/go/challenge/partTwo.go b/25-comboBreaker/go/challenge/partTwo.go new file mode 100644 index 0000000..ae6b24d --- /dev/null +++ b/25-comboBreaker/go/challenge/partTwo.go @@ -0,0 +1,5 @@ +package challenge + +func PartTwo(instr string) int { + return 0 +} diff --git a/25-comboBreaker/go/main.go b/25-comboBreaker/go/main.go new file mode 100644 index 0000000..554e154 --- /dev/null +++ b/25-comboBreaker/go/main.go @@ -0,0 +1,155 @@ +package main + +import ( + "adventOfCode/25-comboBreaker/go/challenge" + "encoding/json" + "fmt" + "io/ioutil" + "os" + "runtime" + "strconv" + "time" + + "github.com/fatih/color" +) + +var forceTime bool + +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) + } + + for _, v := range os.Args[1:] { + if v == "ft" { + forceTime = true + } + } + + runTests(infoStruct) + + fmt.Println("Answers") + + fmt.Printf("Running part %s...\r", color.BlueString("1")) + outputString := fmt.Sprintf("Part %s: ", color.BlueString("1")) + x, t := runAndTime(func() int { return challenge.PartOne(challengeInput) }) + outputString += color.BlueString(strconv.Itoa(x)) + if t > 15 || forceTime { + outputString += fmt.Sprintf(" in %s seconds", color.BlueString(fmt.Sprintf("%.8f", t))) + } + for i := 0; i < 12; i += 1 { + outputString += " " + } + outputString += "\n" + fmt.Fprint(color.Output, outputString) + + fmt.Printf("Running part %s...\r", color.BlueString("2")) + outputString = fmt.Sprintf("Part %s: ", color.BlueString("2")) + x, t = runAndTime(func() int { return challenge.PartTwo(challengeInput) }) + outputString += color.BlueString(strconv.Itoa(x)) + if t > 15 || forceTime { + outputString += fmt.Sprintf(" in %s seconds", color.BlueString(fmt.Sprintf("%.8f", t))) + } + for i := 0; i < 12; i += 1 { + outputString += " " + } + outputString += "\n" + fmt.Fprint(color.Output, outputString) + +} + +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 { + readable_test_num := color.BlueString(n + "." + strconv.Itoa(i+1)) + fmt.Fprintf(color.Output, "Running %s...\r", readable_test_num) + + outputString := readable_test_num + " " + + result, t := runAndTime(func() int { return f(tc.Input) }) + + if result == tc.Expected { + outputString += color.GreenString("pass") + } else { + outputString += fmt.Sprintf("%s (got %s, expected %s)", color.RedString("fail"), color.BlueString(strconv.Itoa(result)), color.BlueString(strconv.Itoa(tc.Expected))) + } + + if t > 15 || forceTime { + outputString += fmt.Sprintf(" in %s seconds", color.BlueString(fmt.Sprintf("%.8f", t))) + } + + for i := 0; i < 12; i += 1 { + outputString += " " + } + outputString += "\n" + + fmt.Fprint(color.Output, outputString) + } + } + + rt(infoStruct.TestCases.One, challenge.PartOne, "1") + rt(infoStruct.TestCases.Two, challenge.PartTwo, "2") + + fmt.Println() + +} + +func runAndTime(f func() int) (int, float64) { + st := time.Now() + x := f() + et := time.Now() + return x, et.Sub(st).Seconds() +}