Initial commit
This commit is contained in:
143
2022/02/code.go
Executable file
143
2022/02/code.go
Executable file
@@ -0,0 +1,143 @@
|
||||
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
|
||||
}
|
||||
77
2022/04/code.go
Executable file
77
2022/04/code.go
Executable file
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jpillora/puzzler/harness/aoc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
aoc.Harness(run)
|
||||
}
|
||||
|
||||
func run(part2 bool, input string) any {
|
||||
if input == "" {
|
||||
return "not implemented"
|
||||
}
|
||||
count := 0
|
||||
lines := strings.Split(strings.TrimSpace(input), "\n")
|
||||
for _, line := range lines {
|
||||
pair := strings.SplitN(line, ",", 2)
|
||||
if len(pair) != 2 {
|
||||
log.Panicf("invalid input %q", line)
|
||||
}
|
||||
a := parse(pair[0])
|
||||
b := parse(pair[1])
|
||||
if !part2 && a.subsetEither(b) {
|
||||
count++
|
||||
} else if part2 && a.intersetsEither(b) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
type hilo struct {
|
||||
lo, hi int
|
||||
}
|
||||
|
||||
func (h hilo) String() string {
|
||||
return fmt.Sprintf("%d-%d", h.lo, h.hi)
|
||||
}
|
||||
|
||||
func parse(s string) hilo {
|
||||
pair := strings.SplitN(s, "-", 2)
|
||||
if len(pair) != 2 {
|
||||
panic("bad pair")
|
||||
}
|
||||
lo, err := strconv.Atoi(pair[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hi, err := strconv.Atoi(pair[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hilo{lo, hi}
|
||||
}
|
||||
|
||||
func (a hilo) subsetEither(b hilo) bool {
|
||||
return a.subsetOf(b) || b.subsetOf(a)
|
||||
}
|
||||
|
||||
func (a hilo) subsetOf(b hilo) bool {
|
||||
return a.lo >= b.lo && a.hi <= b.hi
|
||||
}
|
||||
|
||||
func (a hilo) intersetsEither(b hilo) bool {
|
||||
return a.intersets(b) || b.intersets(a)
|
||||
}
|
||||
|
||||
func (a hilo) intersets(b hilo) bool {
|
||||
return a.lo <= b.lo && b.lo <= a.hi || // b.lo in a OR
|
||||
a.lo <= b.hi && b.hi <= a.hi // b.hi in a
|
||||
}
|
||||
260
2022/07/code.go
Executable file
260
2022/07/code.go
Executable file
@@ -0,0 +1,260 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jpillora/puzzler/harness/aoc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
aoc.Harness(run)
|
||||
}
|
||||
|
||||
func run(part2 bool, input string) any {
|
||||
if input == "" {
|
||||
return "skip"
|
||||
}
|
||||
var e *exec
|
||||
execs := []*exec{}
|
||||
for _, line := range strings.Split(strings.TrimSpace(input), "\n") {
|
||||
if strings.HasPrefix(line, "$ ") {
|
||||
command := strings.Split(line[2:], " ")
|
||||
e = &exec{command: command}
|
||||
execs = append(execs, e)
|
||||
} else {
|
||||
e.output = append(e.output, line)
|
||||
}
|
||||
}
|
||||
fs := newFileSystem()
|
||||
for _, e := range execs {
|
||||
fs.simulate(e)
|
||||
}
|
||||
const draw = false
|
||||
if draw {
|
||||
fmt.Println(fs.root.tree())
|
||||
}
|
||||
if part2 {
|
||||
return fs.MinDeleteFor(updateSize)
|
||||
}
|
||||
return fs.root.Size100KB()
|
||||
}
|
||||
|
||||
type exec struct {
|
||||
command []string
|
||||
output []string
|
||||
}
|
||||
|
||||
func (e exec) prog() string {
|
||||
return e.command[0]
|
||||
}
|
||||
|
||||
func (e exec) arg(i int) string {
|
||||
return e.command[1+i]
|
||||
}
|
||||
|
||||
type fileSystem struct {
|
||||
root *dir
|
||||
wd *dir
|
||||
total int64
|
||||
}
|
||||
|
||||
const updateSize = 30000000
|
||||
|
||||
func newFileSystem() *fileSystem {
|
||||
root := newDir("", nil)
|
||||
return &fileSystem{
|
||||
root: root,
|
||||
wd: root,
|
||||
total: 70000000,
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *fileSystem) simulate(e *exec) {
|
||||
switch e.prog() {
|
||||
case "cd":
|
||||
fs.changeDir(e.arg(0))
|
||||
case "ls":
|
||||
fs.listDir(e.output)
|
||||
default:
|
||||
panic("unknown command")
|
||||
}
|
||||
}
|
||||
|
||||
func (fs *fileSystem) unused() int64 {
|
||||
return fs.total - fs.root.Size()
|
||||
}
|
||||
|
||||
func (fs *fileSystem) changeDir(target string) {
|
||||
switch target {
|
||||
case "/":
|
||||
fs.wd = fs.root
|
||||
case "..":
|
||||
if fs.wd.parent == nil {
|
||||
panic("nil parent")
|
||||
}
|
||||
fs.wd = fs.wd.parent
|
||||
default:
|
||||
fs.wd = fs.wd.subDir(target)
|
||||
}
|
||||
}
|
||||
|
||||
func (fs fileSystem) listDir(output []string) {
|
||||
for _, line := range output {
|
||||
pair := strings.SplitN(line, " ", 2)
|
||||
name := pair[1]
|
||||
if pair[0] == "dir" {
|
||||
fs.wd.subDir(name)
|
||||
continue
|
||||
}
|
||||
size, err := strconv.ParseInt(pair[0], 10, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fs.wd.file(name, size)
|
||||
}
|
||||
}
|
||||
|
||||
func (fs fileSystem) MinDeleteFor(target int64) int64 {
|
||||
required := target - fs.unused()
|
||||
if required < 0 {
|
||||
panic("no min required")
|
||||
}
|
||||
min := int64(math.MaxInt64)
|
||||
fs.root.forEach(func(d *dir) {
|
||||
s := d.Size()
|
||||
if s < required {
|
||||
return
|
||||
}
|
||||
// d is a candidate
|
||||
if s < min {
|
||||
min = s
|
||||
}
|
||||
})
|
||||
if min == math.MaxInt64 {
|
||||
panic("no min found")
|
||||
}
|
||||
return min
|
||||
}
|
||||
|
||||
type dir struct {
|
||||
name string
|
||||
children map[string]any
|
||||
parent *dir
|
||||
}
|
||||
|
||||
func newDir(name string, parent *dir) *dir {
|
||||
return &dir{
|
||||
name: name,
|
||||
children: map[string]any{},
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dir) subDir(name string) *dir {
|
||||
x, ok := d.children[name]
|
||||
if !ok {
|
||||
dir := newDir(name, d)
|
||||
d.children[name] = dir
|
||||
return dir
|
||||
}
|
||||
dir, ok := x.(*dir)
|
||||
if !ok {
|
||||
panic("file/dir mismatch")
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func (d dir) tree() string {
|
||||
sb := strings.Builder{}
|
||||
fmt.Fprintf(&sb, "%s (%d) {\n", d.path(), d.Size())
|
||||
for _, c := range d.children {
|
||||
var out string
|
||||
switch v := c.(type) {
|
||||
case *dir:
|
||||
out = v.tree()
|
||||
case *file:
|
||||
out = v.String()
|
||||
default:
|
||||
panic("invalid child")
|
||||
}
|
||||
ls := strings.Split(out, "\n")
|
||||
for i := range ls {
|
||||
ls[i] = " " + ls[i]
|
||||
}
|
||||
indented := strings.Join(ls, "\n")
|
||||
sb.WriteString(indented)
|
||||
sb.WriteRune('\n')
|
||||
}
|
||||
sb.WriteString("}")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (d *dir) file(name string, size int64) {
|
||||
d.children[name] = &file{
|
||||
name: name,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *dir) path() string {
|
||||
if d.name == "" {
|
||||
return "/"
|
||||
}
|
||||
p := []string{d.name}
|
||||
w := d.parent
|
||||
for w != nil {
|
||||
p = append([]string{w.name}, p...)
|
||||
w = w.parent
|
||||
}
|
||||
return strings.Join(p, "/")
|
||||
}
|
||||
|
||||
func (d dir) Size() int64 {
|
||||
sum := int64(0)
|
||||
for _, c := range d.children {
|
||||
switch v := c.(type) {
|
||||
case *dir:
|
||||
sum += v.Size()
|
||||
case *file:
|
||||
sum += v.size
|
||||
default:
|
||||
panic("invalid child")
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (d *dir) forEach(fn func(*dir)) {
|
||||
fn(d)
|
||||
for _, c := range d.children {
|
||||
sub, ok := c.(*dir)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sub.forEach(fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (d dir) Size100KB() int64 {
|
||||
const limit = 100000
|
||||
sum := int64(0)
|
||||
d.forEach(func(sub *dir) {
|
||||
size := sub.Size()
|
||||
if size < limit {
|
||||
sum += size
|
||||
}
|
||||
})
|
||||
return sum
|
||||
}
|
||||
|
||||
type file struct {
|
||||
name string
|
||||
size int64
|
||||
}
|
||||
|
||||
func (f file) String() string {
|
||||
return fmt.Sprintf("%s (%d)", f.name, f.size)
|
||||
}
|
||||
Reference in New Issue
Block a user