Part 1 was very straightforward. I believe there are ways to calculate this answer without the complicated maths from part 2 and without actually applying each of the instructions, but I'm not concerned with finding it. Part 2 was a big "nope" from me. I went out and found this answer and I don't understand it. I'm okay with that.
156 lines
3.4 KiB
Go
156 lines
3.4 KiB
Go
package days
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"strconv"
|
|
"strings"
|
|
|
|
u "parnic.com/aoc2019/utilities"
|
|
)
|
|
|
|
type day22Instruction int
|
|
|
|
const (
|
|
day22InstructionNewStack day22Instruction = iota
|
|
day22InstructionCut
|
|
day22InstructionDealIncrement
|
|
)
|
|
|
|
type day22Shuffle struct {
|
|
instruction day22Instruction
|
|
arg int
|
|
}
|
|
|
|
type Day22 struct {
|
|
shuffles []day22Shuffle
|
|
}
|
|
|
|
func (d *Day22) Parse() {
|
|
lines := u.GetStringLines("22p")
|
|
d.shuffles = make([]day22Shuffle, len(lines))
|
|
|
|
for idx, line := range lines {
|
|
split := strings.Split(line, " ")
|
|
if split[0] == "deal" {
|
|
if split[1] == "into" {
|
|
d.shuffles[idx] = day22Shuffle{instruction: day22InstructionNewStack}
|
|
} else if split[1] == "with" {
|
|
arg, err := strconv.Atoi(split[3])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
d.shuffles[idx] = day22Shuffle{
|
|
instruction: day22InstructionDealIncrement,
|
|
arg: arg,
|
|
}
|
|
}
|
|
} else if split[0] == "cut" {
|
|
arg, err := strconv.Atoi(split[1])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
d.shuffles[idx] = day22Shuffle{
|
|
instruction: day22InstructionCut,
|
|
arg: arg,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d Day22) Num() int {
|
|
return 22
|
|
}
|
|
|
|
func (d Day22) applyShuffle(s day22Shuffle, stack, scratch []int) {
|
|
switch s.instruction {
|
|
case day22InstructionNewStack:
|
|
for i := 0; i < len(stack)/2; i++ {
|
|
stack[i], stack[len(stack)-1-i] = stack[len(stack)-1-i], stack[i]
|
|
}
|
|
|
|
// there's probably a way to do these two in place...
|
|
case day22InstructionCut:
|
|
absArg := int(math.Abs(float64(s.arg)))
|
|
for i, v := range stack {
|
|
if s.arg > 0 {
|
|
if i < absArg {
|
|
scratch[len(scratch)-absArg+i] = v
|
|
} else {
|
|
scratch[i-absArg] = v
|
|
}
|
|
} else {
|
|
if i < absArg {
|
|
scratch[i] = stack[len(stack)-absArg+i]
|
|
} else {
|
|
scratch[i] = stack[i-absArg]
|
|
}
|
|
}
|
|
}
|
|
copy(stack, scratch)
|
|
|
|
case day22InstructionDealIncrement:
|
|
for i, v := range stack {
|
|
scratch[(i*s.arg)%len(stack)] = v
|
|
}
|
|
copy(stack, scratch)
|
|
}
|
|
}
|
|
|
|
func (d *Day22) Part1() string {
|
|
deckSize := 10007
|
|
// deckSize := 10
|
|
|
|
stack := make([]int, deckSize)
|
|
for i := range stack {
|
|
stack[i] = i
|
|
}
|
|
|
|
scratch := make([]int, len(stack))
|
|
|
|
for _, s := range d.shuffles {
|
|
d.applyShuffle(s, stack, scratch)
|
|
}
|
|
|
|
pos := -1
|
|
for i, v := range stack {
|
|
if v == 2019 {
|
|
pos = i
|
|
break
|
|
}
|
|
}
|
|
|
|
return fmt.Sprintf("Card 2019 is at position %s%d%s", u.TextBold, pos, u.TextReset)
|
|
}
|
|
|
|
func (d *Day22) Part2() string {
|
|
n, iter := big.NewInt(119315717514047), big.NewInt(101741582076661)
|
|
offset, increment := big.NewInt(0), big.NewInt(1)
|
|
for _, s := range d.shuffles {
|
|
switch s.instruction {
|
|
case day22InstructionNewStack:
|
|
increment.Mul(increment, big.NewInt(-1))
|
|
offset.Add(offset, increment)
|
|
case day22InstructionCut:
|
|
offset.Add(offset, big.NewInt(0).Mul(big.NewInt(int64(s.arg)), increment))
|
|
case day22InstructionDealIncrement:
|
|
increment.Mul(increment, big.NewInt(0).Exp(big.NewInt(int64(s.arg)), big.NewInt(0).Sub(n, big.NewInt(2)), n))
|
|
}
|
|
}
|
|
|
|
finalIncr := big.NewInt(0).Exp(increment, iter, n)
|
|
|
|
finalOffs := big.NewInt(0).Exp(increment, iter, n)
|
|
finalOffs.Sub(big.NewInt(1), finalOffs)
|
|
invmod := big.NewInt(0).Exp(big.NewInt(0).Sub(big.NewInt(1), increment), big.NewInt(0).Sub(n, big.NewInt(2)), n)
|
|
finalOffs.Mul(finalOffs, invmod)
|
|
finalOffs.Mul(finalOffs, offset)
|
|
|
|
answer := big.NewInt(0).Mul(big.NewInt(2020), finalIncr)
|
|
answer.Add(answer, finalOffs)
|
|
answer.Mod(answer, n)
|
|
|
|
return fmt.Sprintf("Card at position 2020: %s%d%s", u.TextBold, answer, u.TextReset)
|
|
}
|