144 lines
2.1 KiB
Go
Executable File
144 lines
2.1 KiB
Go
Executable File
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/jpillora/puzzler/harness/aoc"
|
|
)
|
|
|
|
func main() {
|
|
aoc.Harness(run)
|
|
}
|
|
|
|
func run(part2 bool, input string) any {
|
|
sum := 0
|
|
for _, line := range strings.Split(strings.TrimSpace(input), "\n") {
|
|
pair := strings.SplitN(line, " ", 2)
|
|
var op move
|
|
op.unmarshal('A', pair[0])
|
|
if op.String() == "unknown" {
|
|
panic("invalid opponent move")
|
|
}
|
|
if part2 {
|
|
var o outcome
|
|
o.unmarshal(pair[1])
|
|
me := op.want(o)
|
|
sum += o.score() + me.score()
|
|
} else {
|
|
var me move
|
|
me.unmarshal('X', pair[1])
|
|
s := op.play(me).score()
|
|
sum += me.score() + s
|
|
}
|
|
}
|
|
return sum
|
|
}
|
|
|
|
type outcome byte
|
|
|
|
const (
|
|
lose outcome = iota
|
|
draw
|
|
win
|
|
)
|
|
|
|
func (o *outcome) unmarshal(s string) {
|
|
*o = outcome(delta('X', s))
|
|
}
|
|
|
|
func (o outcome) String() string {
|
|
switch o {
|
|
case lose:
|
|
return "lose"
|
|
case draw:
|
|
return "draw"
|
|
case win:
|
|
return "win"
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
func (o outcome) score() int {
|
|
switch o {
|
|
case lose:
|
|
return 0
|
|
case draw:
|
|
return 3
|
|
case win:
|
|
return 6
|
|
}
|
|
panic("invalid outcome")
|
|
}
|
|
|
|
type move byte
|
|
|
|
const (
|
|
rock move = iota
|
|
paper
|
|
scissors
|
|
)
|
|
|
|
func (m *move) unmarshal(base byte, s string) {
|
|
*m = move(delta(base, s))
|
|
}
|
|
|
|
func (m move) String() string {
|
|
switch m {
|
|
case rock:
|
|
return "rock"
|
|
case paper:
|
|
return "paper"
|
|
case scissors:
|
|
return "scissors"
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
func (m move) score() int {
|
|
return int(m) + 1
|
|
}
|
|
|
|
func (m move) play(against move) outcome {
|
|
if m == against {
|
|
return draw
|
|
}
|
|
if byte(m) == up(byte(against), 3) {
|
|
return lose
|
|
}
|
|
if byte(m) == down(byte(against), 3) {
|
|
return win
|
|
}
|
|
panic(fmt.Sprintf("invalid moves: %d plays %d", m, against))
|
|
}
|
|
|
|
func (m move) want(outcome outcome) move {
|
|
switch outcome {
|
|
case lose:
|
|
return move(down(byte(m), 3))
|
|
case draw:
|
|
return m
|
|
case win:
|
|
return move(up(byte(m), 3))
|
|
}
|
|
panic("invalid outcome")
|
|
}
|
|
|
|
func delta(base byte, m string) byte {
|
|
if len(m) != 1 {
|
|
panic("invalid delta")
|
|
}
|
|
return m[0] - base
|
|
}
|
|
|
|
func up(n, mod byte) byte {
|
|
return (n + 1) % mod
|
|
}
|
|
|
|
func down(n, mod byte) byte {
|
|
if n == 0 {
|
|
return mod - 1
|
|
}
|
|
return (n - 1) % mod
|
|
}
|