Compare commits
12 Commits
6ffa3b6dd4
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| bec29a111c | |||
| b2ff2ac24c | |||
| 4a9afc8de6 | |||
| 711badad96 | |||
| 7c8ee5edcd | |||
| e13255ab2f | |||
| cc7256461e | |||
| 917be0d8f1 | |||
| 3340be88a1 | |||
| d1c2f5eb90 | |||
| 374dd4a9b7 | |||
| 41c58fa100 |
118
16/main.go
118
16/main.go
@@ -46,30 +46,30 @@ func makeScanner(test bool) *bufio.Scanner {
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
func parseNumber(stream []uint8, length int) int64 {
|
||||
result := int64(0)
|
||||
func parseNumber(stream []uint8, length int) int {
|
||||
result := int(0)
|
||||
for i := 0; i < length; i++ {
|
||||
result += int64(stream[i]) << (length - 1 - i)
|
||||
result += int(stream[i]) << (length - 1 - i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
version int64
|
||||
typeID int64
|
||||
version int
|
||||
typeID int
|
||||
subPackets []packet
|
||||
encodedNumber int64
|
||||
encodedNumber int
|
||||
}
|
||||
|
||||
func getPacketVersion(stream []uint8) int64 {
|
||||
func getPacketVersion(stream []uint8) int {
|
||||
return parseNumber(stream, 3)
|
||||
}
|
||||
|
||||
func getPacketType(stream []uint8) int64 {
|
||||
func getPacketType(stream []uint8) int {
|
||||
return parseNumber(stream, 3)
|
||||
}
|
||||
|
||||
func getSubpacketLength(t uint8, stream []uint8) (int, int64) {
|
||||
func getSubpacketLength(t uint8, stream []uint8) (int, int) {
|
||||
if t == 0 {
|
||||
return 15, parseNumber(stream, 15)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func getSubpacketLength(t uint8, stream []uint8) (int, int64) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func parsePayload(stream []uint8) (int64, int) {
|
||||
func parsePayload(stream []uint8) (int, int) {
|
||||
num := []uint8{}
|
||||
pos := 0
|
||||
chars := 0
|
||||
@@ -111,12 +111,15 @@ func parsePackets(stream []uint8) (int, packet) {
|
||||
size, subpacketlength := getSubpacketLength(stream[6], stream[7:])
|
||||
for {
|
||||
consumed, subs := parsePackets(stream[7+size+parsed:])
|
||||
if consumed == 0 {
|
||||
return parsed + 7 + size, parsedPacket
|
||||
}
|
||||
parsedPacket.subPackets = append(parsedPacket.subPackets, subs)
|
||||
parsed += consumed
|
||||
if size == 11 && int64(len(parsedPacket.subPackets)) == subpacketlength {
|
||||
if size == 11 && len(parsedPacket.subPackets) == subpacketlength {
|
||||
return parsed + 7 + size, parsedPacket
|
||||
|
||||
} else if size == 15 && int64(parsed) > subpacketlength || consumed == 0 {
|
||||
} else if size == 15 && parsed > subpacketlength {
|
||||
return parsed + 7 + size, parsedPacket
|
||||
}
|
||||
}
|
||||
@@ -144,10 +147,97 @@ func partOne() {
|
||||
fmt.Println(countVersions(parsedPacket))
|
||||
}
|
||||
|
||||
func solve(p packet) int {
|
||||
switch p.typeID {
|
||||
case 0: // sum
|
||||
if len(p.subPackets) == 1 {
|
||||
return solve(p.subPackets[0])
|
||||
} else {
|
||||
acc := 0
|
||||
for _, v := range p.subPackets {
|
||||
acc += solve(v)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
case 1: // product
|
||||
if len(p.subPackets) == 1 {
|
||||
return solve(p.subPackets[0])
|
||||
} else {
|
||||
acc := solve(p.subPackets[0])
|
||||
for _, v := range p.subPackets[1:] {
|
||||
acc *= solve(v)
|
||||
}
|
||||
return acc
|
||||
}
|
||||
case 2: // min
|
||||
if len(p.subPackets) == 1 {
|
||||
return solve(p.subPackets[0])
|
||||
} else {
|
||||
acc := solve(p.subPackets[0])
|
||||
for _, v := range p.subPackets[1:] {
|
||||
t := solve(v)
|
||||
if t < acc {
|
||||
acc = t
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}
|
||||
case 3: // max
|
||||
if len(p.subPackets) == 1 {
|
||||
return solve(p.subPackets[0])
|
||||
} else {
|
||||
acc := solve(p.subPackets[0])
|
||||
for _, v := range p.subPackets[1:] {
|
||||
t := solve(v)
|
||||
if t > acc {
|
||||
acc = t
|
||||
}
|
||||
}
|
||||
return acc
|
||||
}
|
||||
case 4: // number
|
||||
return p.encodedNumber
|
||||
case 5: // >
|
||||
if len(p.subPackets) != 2 {
|
||||
panic(fmt.Sprintf("recevied type 5 packet with %d subpackets", len(p.subPackets)))
|
||||
}
|
||||
if solve(p.subPackets[0]) > solve(p.subPackets[1]) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
case 6: // <
|
||||
if len(p.subPackets) != 2 {
|
||||
panic(fmt.Sprintf("recevied type 6 packet with %d subpackets", len(p.subPackets)))
|
||||
}
|
||||
if solve(p.subPackets[0]) < solve(p.subPackets[1]) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
case 7: // ==
|
||||
if len(p.subPackets) != 2 {
|
||||
panic(fmt.Sprintf("recevied type 7 packet with %d subpackets", len(p.subPackets)))
|
||||
}
|
||||
if solve(p.subPackets[0]) == solve(p.subPackets[1]) {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
default:
|
||||
panic("unknown packet type")
|
||||
}
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
scanner.Scan()
|
||||
line := scanner.Text()
|
||||
stream := []uint8{}
|
||||
for _, hex := range line {
|
||||
stream = append(stream, hexMap[hex]...)
|
||||
}
|
||||
_, parsedPacket := parsePackets(stream)
|
||||
fmt.Println(solve(parsedPacket))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func TestVersionParse(t *testing.T) {
|
||||
tests := map[int64][]uint8{
|
||||
tests := map[int][]uint8{
|
||||
0: {0, 0, 0},
|
||||
1: {0, 0, 1},
|
||||
2: {0, 1, 0},
|
||||
@@ -59,3 +59,28 @@ func TestCases(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSolving(t *testing.T) {
|
||||
inputs := map[string]int{
|
||||
"C200B40A82": 3,
|
||||
"04005AC33890": 54,
|
||||
"880086C3E88112": 7,
|
||||
"CE00C43D881120": 9,
|
||||
"D8005AC2A8F0": 1,
|
||||
"F600BC2D8F": 0,
|
||||
"9C005AC2F8F0": 0,
|
||||
"9C0141080250320F1802104A08": 1,
|
||||
}
|
||||
|
||||
for c, r := range inputs {
|
||||
stream := []uint8{}
|
||||
for _, hex := range c {
|
||||
stream = append(stream, hexMap[hex]...)
|
||||
}
|
||||
_, parsedPacket := parsePackets(stream)
|
||||
if r != solve(parsedPacket) {
|
||||
t.Log(c, solve(parsedPacket))
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
159
17/main.go
Normal file
159
17/main.go
Normal file
@@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mustAtoi(line string) int {
|
||||
i, _ := strconv.Atoi(line)
|
||||
return i
|
||||
}
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
type targetArea struct {
|
||||
startx int
|
||||
endx int
|
||||
starty int
|
||||
endy int
|
||||
}
|
||||
|
||||
func ParseInput(line string) targetArea {
|
||||
target := targetArea{}
|
||||
acc := ""
|
||||
for i := 0; i < len(line); i++ {
|
||||
switch line[i] {
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-':
|
||||
acc += string(line[i])
|
||||
case '.':
|
||||
i++ // skip the second .
|
||||
if target.startx == 0 {
|
||||
target.startx = mustAtoi(acc)
|
||||
} else {
|
||||
target.starty = mustAtoi(acc)
|
||||
}
|
||||
acc = ""
|
||||
case ',':
|
||||
target.endx = mustAtoi(acc)
|
||||
acc = ""
|
||||
default: // all other characters do nothing
|
||||
}
|
||||
target.endy = mustAtoi(acc)
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
func InTarget(x, y int, target targetArea) bool {
|
||||
return x >= target.startx && x <= target.endx && y <= target.endy && y >= target.starty
|
||||
}
|
||||
|
||||
func BeyondTarget(x, y int, target targetArea) bool {
|
||||
return x > target.endx || y < target.starty
|
||||
}
|
||||
|
||||
type probe struct {
|
||||
xvelocity int
|
||||
yvelocity int
|
||||
x int
|
||||
y int
|
||||
maxy int
|
||||
}
|
||||
|
||||
func (p *probe) Step() {
|
||||
p.x += p.xvelocity
|
||||
p.y += p.yvelocity
|
||||
if p.xvelocity > 0 {
|
||||
p.xvelocity--
|
||||
} else if p.xvelocity < 0 {
|
||||
p.xvelocity++
|
||||
}
|
||||
p.yvelocity--
|
||||
if p.y > p.maxy {
|
||||
p.maxy = p.y
|
||||
}
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
// read line
|
||||
scanner.Scan()
|
||||
line := scanner.Text()
|
||||
target := ParseInput(line)
|
||||
highestArc := 0
|
||||
for j := 1; j < 50; j++ {
|
||||
for k := 1; k < 100; k++ {
|
||||
initx := j
|
||||
inity := k
|
||||
p := &probe{
|
||||
xvelocity: initx,
|
||||
yvelocity: inity,
|
||||
}
|
||||
for {
|
||||
p.Step()
|
||||
if InTarget(p.x, p.y, target) {
|
||||
if p.maxy > highestArc {
|
||||
highestArc = p.maxy
|
||||
}
|
||||
}
|
||||
if BeyondTarget(p.x, p.y, target) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(highestArc)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
// read line
|
||||
scanner.Scan()
|
||||
line := scanner.Text()
|
||||
target := ParseInput(line)
|
||||
hits := 0
|
||||
for j := 0; j < 1000; j++ {
|
||||
for k := -200; k < 1000; k++ {
|
||||
initx := j
|
||||
inity := k
|
||||
p := &probe{
|
||||
xvelocity: initx,
|
||||
yvelocity: inity,
|
||||
}
|
||||
for {
|
||||
p.Step()
|
||||
if InTarget(p.x, p.y, target) {
|
||||
hits++
|
||||
break
|
||||
}
|
||||
if BeyondTarget(p.x, p.y, target) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(hits)
|
||||
}
|
||||
15
17/main_test.go
Normal file
15
17/main_test.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestInTarget(t *testing.T) {
|
||||
target := targetArea{
|
||||
startx: 20,
|
||||
endx: 30,
|
||||
starty: -10,
|
||||
endy: -5,
|
||||
}
|
||||
if !InTarget(28, -7, target) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
225
18/main.go
Normal file
225
18/main.go
Normal file
@@ -0,0 +1,225 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mustAtoi(line rune) int {
|
||||
i, _ := strconv.Atoi(string(line))
|
||||
return i
|
||||
}
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
type pair struct {
|
||||
parent *pair
|
||||
leftp *pair
|
||||
left int
|
||||
rightp *pair
|
||||
right int
|
||||
}
|
||||
|
||||
func Parse(raw string) *pair {
|
||||
root := &pair{}
|
||||
var current *pair
|
||||
left := true
|
||||
for _, v := range raw {
|
||||
switch v {
|
||||
case '[':
|
||||
if current == nil {
|
||||
current = root
|
||||
} else {
|
||||
p := &pair{parent: current}
|
||||
if left {
|
||||
current.leftp = p
|
||||
} else {
|
||||
current.rightp = p
|
||||
}
|
||||
current = p
|
||||
}
|
||||
left = true
|
||||
case ']':
|
||||
current = current.parent
|
||||
case ',':
|
||||
left = false
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
if left {
|
||||
current.left = mustAtoi(v)
|
||||
} else {
|
||||
current.right = mustAtoi(v)
|
||||
}
|
||||
case ' ':
|
||||
continue
|
||||
default:
|
||||
panic("what the: " + string(v))
|
||||
}
|
||||
}
|
||||
return root
|
||||
}
|
||||
|
||||
func (p *pair) String() string {
|
||||
var left, right string
|
||||
if p.leftp == nil {
|
||||
left = fmt.Sprintf("%d", p.left)
|
||||
} else {
|
||||
left = p.leftp.String()
|
||||
}
|
||||
if p.rightp == nil {
|
||||
right = fmt.Sprintf("%d", p.right)
|
||||
} else {
|
||||
right = p.rightp.String()
|
||||
}
|
||||
return fmt.Sprintf("[%s,%s]", left, right)
|
||||
}
|
||||
|
||||
func (p *pair) Magnitude() int {
|
||||
left := 0
|
||||
right := 0
|
||||
if p.leftp == nil {
|
||||
left = p.left
|
||||
} else {
|
||||
left = p.leftp.Magnitude()
|
||||
}
|
||||
if p.rightp == nil {
|
||||
right = p.right
|
||||
} else {
|
||||
right = p.rightp.Magnitude()
|
||||
}
|
||||
return left*3 + right*2
|
||||
}
|
||||
|
||||
func Add(left, right *pair) *pair {
|
||||
return &pair{
|
||||
leftp: left,
|
||||
rightp: right,
|
||||
}
|
||||
}
|
||||
|
||||
func Reduce(p *pair) {
|
||||
for {
|
||||
if MustExplode(0, p) {
|
||||
Explode(p)
|
||||
} else if MustSplit(p) {
|
||||
Split(p)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Split(p *pair) bool {
|
||||
if p.left > 9 {
|
||||
p.leftp = &pair{
|
||||
left: p.left / 2,
|
||||
right: (p.left / 2) + (p.left % 2),
|
||||
}
|
||||
p.left = 0
|
||||
return true
|
||||
}
|
||||
if p.leftp != nil {
|
||||
m := Split(p.leftp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if p.right > 9 {
|
||||
p.rightp = &pair{
|
||||
left: p.right / 2,
|
||||
right: (p.right / 2) + (p.right % 2),
|
||||
}
|
||||
p.right = 0
|
||||
return true
|
||||
}
|
||||
if p.rightp != nil {
|
||||
m := Split(p.rightp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func MustSplit(p *pair) bool {
|
||||
if p.left > 9 || p.right > 9 {
|
||||
return true
|
||||
}
|
||||
if p.leftp != nil {
|
||||
m := MustSplit(p.leftp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if p.rightp != nil {
|
||||
m := MustSplit(p.rightp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func MustExplode(depth int, p *pair) bool {
|
||||
if depth >= 4 {
|
||||
return true
|
||||
}
|
||||
if p.leftp != nil {
|
||||
m := MustExplode(depth+1, p.leftp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if p.rightp != nil {
|
||||
m := MustExplode(depth+1, p.rightp)
|
||||
if m {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func Explode(p *pair) {
|
||||
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
scanner.Scan()
|
||||
line := scanner.Text()
|
||||
total := Parse(line)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
total = Add(total, Parse(line))
|
||||
}
|
||||
fmt.Println(total)
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
}
|
||||
}
|
||||
160
18/main_test.go
Normal file
160
18/main_test.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
cases := []string{
|
||||
"[1,2]",
|
||||
"[[3,2],1]",
|
||||
"[[3,2],[4,5]]",
|
||||
"[[3,2],[[7,8],5]]",
|
||||
"[[1,2],3]",
|
||||
"[9,[8,7]]",
|
||||
"[[1,9],[8,5]]",
|
||||
"[[[[1,2],[3,4]],[[5,6],[7,8]]],9]",
|
||||
"[[[9,[3,8]],[[0,9],6]],[[[3,7],[4,9]],3]]",
|
||||
"[[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]]",
|
||||
}
|
||||
for _, input := range cases {
|
||||
p := Parse(input)
|
||||
if input != p.String() {
|
||||
t.Log(p.String())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustExplode(t *testing.T) {
|
||||
cases := map[string]bool{
|
||||
"[1,2]": false,
|
||||
"[[[[2,4],4],[2,3]],1]": false,
|
||||
"[[[[[4,3],4],4],1],3]": true,
|
||||
"[2,[1,[4,[3,[4,3]]]]]": true,
|
||||
"[[[[0,7],4],[7,[[8,4],9]]],[1,1]]": true,
|
||||
}
|
||||
for input, output := range cases {
|
||||
if MustExplode(0, Parse(input)) != output {
|
||||
t.Log(input)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
cases := map[*pair]string{
|
||||
{left: 1, right: 1}: "[1,1]",
|
||||
{left: 10, right: 1}: "[[5,5],1]",
|
||||
{left: 11, right: 1}: "[[5,6],1]",
|
||||
{left: 12, right: 1}: "[[6,6],1]",
|
||||
{left: 12, right: 12}: "[[6,6],12]",
|
||||
{left: 2, right: 12}: "[2,[6,6]]",
|
||||
{
|
||||
leftp: &pair{left: 15, right: 9},
|
||||
rightp: &pair{left: 8, right: 1},
|
||||
}: "[[[7,8],9],[8,1]]",
|
||||
{
|
||||
leftp: &pair{left: 1, right: 9},
|
||||
rightp: &pair{left: 8, right: 15},
|
||||
}: "[[1,9],[8,[7,8]]]",
|
||||
}
|
||||
for input, expected := range cases {
|
||||
Split(input)
|
||||
if input.String() != expected {
|
||||
t.Log(input.String(), expected)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMustSplit(t *testing.T) {
|
||||
cases := map[*pair]bool{
|
||||
{left: 1, right: 1}: false,
|
||||
{left: 10, right: 1}: true,
|
||||
{left: 11, right: 1}: true,
|
||||
{left: 12, right: 1}: true,
|
||||
{left: 12, right: 12}: true,
|
||||
{left: 2, right: 12}: true,
|
||||
{
|
||||
leftp: &pair{left: 1, right: 9},
|
||||
rightp: &pair{left: 8, right: 5},
|
||||
}: false,
|
||||
{
|
||||
leftp: &pair{left: 1, right: 9},
|
||||
rightp: &pair{left: 8, right: 15},
|
||||
}: true,
|
||||
}
|
||||
for input, output := range cases {
|
||||
result := MustSplit(input)
|
||||
if result != output {
|
||||
t.Log(input.String())
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
cases := map[string][]*pair{
|
||||
"[[1,2],[[3,4],5]]": {
|
||||
&pair{left: 1, right: 2},
|
||||
&pair{leftp: &pair{left: 3, right: 4}, right: 5},
|
||||
},
|
||||
}
|
||||
for answer, pairs := range cases {
|
||||
r := Add(pairs[0], pairs[1])
|
||||
if r.String() != answer {
|
||||
fmt.Printf("%s %s", r, answer)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReduce(t *testing.T) {
|
||||
cases := map[*pair]string{
|
||||
{left: 1, right: 1}: "[1,1]",
|
||||
{left: 10, right: 1}: "[[5,5],1]",
|
||||
{left: 11, right: 1}: "[[5,6],1]",
|
||||
{left: 12, right: 1}: "[[6,6],1]",
|
||||
{left: 12, right: 13}: "[[6,6],[6,7]]",
|
||||
{left: 2, right: 12}: "[2,[6,6]]",
|
||||
{
|
||||
leftp: &pair{left: 15, right: 9},
|
||||
rightp: &pair{left: 8, right: 1},
|
||||
}: "[[[7,8],9],[8,1]]",
|
||||
{
|
||||
leftp: &pair{left: 1, right: 9},
|
||||
rightp: &pair{left: 8, right: 15},
|
||||
}: "[[1,9],[8,[7,8]]]",
|
||||
}
|
||||
for input, expected := range cases {
|
||||
Reduce(input)
|
||||
if input.String() != expected {
|
||||
t.Log(input.String(), expected)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMagnitude(t *testing.T) {
|
||||
cases := map[int]*pair{
|
||||
29: {left: 9, right: 1},
|
||||
21: {left: 1, right: 9},
|
||||
129: {
|
||||
leftp: &pair{left: 9, right: 1},
|
||||
rightp: &pair{left: 1, right: 9},
|
||||
},
|
||||
143: {
|
||||
leftp: &pair{left: 1, right: 2},
|
||||
rightp: &pair{leftp: &pair{left: 3, right: 4}, right: 5},
|
||||
},
|
||||
}
|
||||
for answer, pair := range cases {
|
||||
r := pair.Magnitude()
|
||||
if r != answer {
|
||||
fmt.Printf("%d %d", r, answer)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
180
20/main.go
Normal file
180
20/main.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
type coord struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
// grid SHOULD be like this:
|
||||
// x ->
|
||||
// y
|
||||
// |
|
||||
// V
|
||||
|
||||
type image struct {
|
||||
background rune // what all uninitialized cells are set to; defaults to '.'
|
||||
grid map[coord]rune
|
||||
topleft coord
|
||||
size int
|
||||
}
|
||||
|
||||
func GetNeighbors(c coord) []coord {
|
||||
return []coord{
|
||||
{x: c.x - 1, y: c.y - 1},
|
||||
{x: c.x, y: c.y - 1},
|
||||
{x: c.x + 1, y: c.y - 1},
|
||||
|
||||
{x: c.x - 1, y: c.y},
|
||||
{x: c.x, y: c.y},
|
||||
{x: c.x + 1, y: c.y},
|
||||
|
||||
{x: c.x - 1, y: c.y + 1},
|
||||
{x: c.x, y: c.y + 1},
|
||||
{x: c.x + 1, y: c.y + 1},
|
||||
}
|
||||
}
|
||||
|
||||
func (img *image) GetNewRune(coord coord, enhancementMap string) rune {
|
||||
index := 0
|
||||
coords := GetNeighbors(coord)
|
||||
for i := 0; i < len(coords); i++ {
|
||||
value := img.grid[coords[i]]
|
||||
if value == 0 {
|
||||
value = img.background
|
||||
}
|
||||
if value == '#' {
|
||||
index += (1 << (8 - i))
|
||||
}
|
||||
}
|
||||
return rune(enhancementMap[index])
|
||||
}
|
||||
|
||||
func (img *image) CountBrightPixels() int {
|
||||
count := 0
|
||||
for _, v := range img.grid {
|
||||
if v == '#' {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (img *image) Print() {
|
||||
fmt.Println(strings.Repeat(string(img.background), img.size+2))
|
||||
for y := img.topleft.y; y < img.size+img.topleft.y; y++ {
|
||||
fmt.Print(string(img.background))
|
||||
for x := img.topleft.x; x < img.size+img.topleft.x; x++ {
|
||||
fmt.Printf("%s", string(img.grid[coord{x, y}]))
|
||||
}
|
||||
fmt.Printf("%s\n", string(img.background))
|
||||
}
|
||||
fmt.Println(strings.Repeat(string(img.background), img.size+2))
|
||||
}
|
||||
|
||||
func Enhance(img *image, m string) *image {
|
||||
next := &image{
|
||||
topleft: coord{
|
||||
x: img.topleft.x - 1,
|
||||
y: img.topleft.y - 1,
|
||||
},
|
||||
size: img.size + 2,
|
||||
grid: map[coord]rune{},
|
||||
}
|
||||
if img.background == '.' { // permute new "background" value
|
||||
next.background = rune(m[0])
|
||||
} else if img.background == '#' {
|
||||
next.background = rune(m[511])
|
||||
}
|
||||
for y := img.topleft.y - 1; y < img.size+1; y++ {
|
||||
for x := img.topleft.x - 1; x < img.size+1; x++ {
|
||||
next.grid[coord{x, y}] = img.GetNewRune(coord{x, y}, m)
|
||||
}
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
scanner.Scan()
|
||||
enhancementString := scanner.Text()
|
||||
scanner.Scan() // eat empty line
|
||||
|
||||
img := &image{
|
||||
background: '.',
|
||||
grid: map[coord]rune{},
|
||||
topleft: coord{0, 0},
|
||||
}
|
||||
|
||||
y := 0
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
img.size = len(line)
|
||||
for x, c := range line {
|
||||
img.grid[coord{x, y}] = c
|
||||
}
|
||||
y++
|
||||
}
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
img = Enhance(img, enhancementString)
|
||||
}
|
||||
fmt.Println(img.CountBrightPixels())
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
scanner.Scan()
|
||||
enhancementString := scanner.Text()
|
||||
scanner.Scan() // eat empty line
|
||||
|
||||
img := &image{
|
||||
background: '.',
|
||||
grid: map[coord]rune{},
|
||||
topleft: coord{0, 0},
|
||||
}
|
||||
|
||||
y := 0
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
img.size = len(line)
|
||||
for x, c := range line {
|
||||
img.grid[coord{x, y}] = c
|
||||
}
|
||||
y++
|
||||
}
|
||||
|
||||
for i := 0; i < 50; i++ {
|
||||
img = Enhance(img, enhancementString)
|
||||
}
|
||||
fmt.Println(img.CountBrightPixels())
|
||||
}
|
||||
66
20/main_test.go
Normal file
66
20/main_test.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetNewRune(t *testing.T) {
|
||||
img := image{
|
||||
grid: map[coord]rune{
|
||||
{0, 0}: '.', {1, 0}: '.', {2, 0}: '.',
|
||||
{0, 1}: '.', {1, 1}: '.', {2, 1}: '.',
|
||||
{0, 2}: '.', {1, 2}: '.', {2, 2}: '.',
|
||||
},
|
||||
}
|
||||
if img.GetNewRune(coord{1, 1}, ".") != '.' {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
img = image{
|
||||
grid: map[coord]rune{
|
||||
{0, 0}: '.', {1, 0}: '.', {2, 0}: '.',
|
||||
{0, 1}: '.', {1, 1}: '.', {2, 1}: '.',
|
||||
{0, 2}: '.', {1, 2}: '.', {2, 2}: '#',
|
||||
},
|
||||
}
|
||||
if img.GetNewRune(coord{1, 1}, ".#") != '#' {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
img = image{
|
||||
grid: map[coord]rune{
|
||||
{0, 0}: '.', {1, 0}: '.', {2, 0}: '.',
|
||||
{0, 1}: '.', {1, 1}: '.', {2, 1}: '.',
|
||||
{0, 2}: '.', {1, 2}: '#', {2, 2}: '.',
|
||||
},
|
||||
}
|
||||
if img.GetNewRune(coord{1, 1}, "..#.") != '#' {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
img = image{
|
||||
grid: map[coord]rune{
|
||||
{0, 0}: '.', {1, 0}: '.', {2, 0}: '.',
|
||||
{0, 1}: '.', {1, 1}: '.', {2, 1}: '.',
|
||||
{0, 2}: '.', {1, 2}: '#', {2, 2}: '#',
|
||||
},
|
||||
}
|
||||
if img.GetNewRune(coord{1, 1}, "...#") != '#' {
|
||||
t.Fail()
|
||||
}
|
||||
|
||||
img = image{
|
||||
background: '#',
|
||||
grid: map[coord]rune{
|
||||
{0, 0}: '.', {1, 0}: '.', {2, 0}: '.',
|
||||
{0, 1}: '.', {1, 1}: '.', {2, 1}: '.',
|
||||
{0, 2}: '.', {1, 2}: '#', {2, 2}: '#',
|
||||
},
|
||||
}
|
||||
m := strings.Repeat(".", 484)
|
||||
m += "#"
|
||||
if img.GetNewRune(coord{0, 0}, m) != '#' { // 111100100, or 484
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
95
21/main.go
Normal file
95
21/main.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mustAtoi(line string) int {
|
||||
i, _ := strconv.Atoi(line)
|
||||
return i
|
||||
}
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
func MakeDeterministicDie(max int) func() int {
|
||||
currentDieValue := 0
|
||||
return func() int {
|
||||
if currentDieValue >= max {
|
||||
currentDieValue = 0
|
||||
}
|
||||
currentDieValue++
|
||||
return currentDieValue
|
||||
}
|
||||
}
|
||||
|
||||
func RollThree(f func() int) int {
|
||||
return f() + f() + f()
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
scanner.Scan()
|
||||
player1 := mustAtoi(strings.Split(scanner.Text(), " ")[4])
|
||||
scanner.Scan()
|
||||
player2 := mustAtoi(strings.Split(scanner.Text(), " ")[4])
|
||||
|
||||
die := MakeDeterministicDie(100)
|
||||
score1 := 0
|
||||
score2 := 0
|
||||
rolls := 0
|
||||
|
||||
for {
|
||||
player1 = player1 + RollThree(die)
|
||||
rolls += 3
|
||||
for player1 > 10 {
|
||||
player1 -= 10
|
||||
}
|
||||
score1 += player1
|
||||
if score1 >= 1000 {
|
||||
fmt.Println(score2 * rolls)
|
||||
break
|
||||
}
|
||||
player2 = player2 + RollThree(die)
|
||||
rolls += 3
|
||||
for player2 > 10 {
|
||||
player2 -= 10
|
||||
}
|
||||
score2 += player2
|
||||
if score2 >= 1000 {
|
||||
fmt.Println(score1 * rolls)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(false)
|
||||
|
||||
for scanner.Scan() {
|
||||
// line := scanner.Text()
|
||||
}
|
||||
}
|
||||
134
22/main.go
Normal file
134
22/main.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func mustAtoi(line string) int {
|
||||
i, _ := strconv.Atoi(line)
|
||||
return i
|
||||
}
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
partTwo()
|
||||
duration2 := time.Since(start)
|
||||
fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
type step struct {
|
||||
on bool
|
||||
x []int
|
||||
y []int
|
||||
z []int
|
||||
}
|
||||
|
||||
func ParseInput(line string) step {
|
||||
s := step{}
|
||||
var state byte
|
||||
acc := ""
|
||||
for i := 0; i < len(line); i++ {
|
||||
switch line[i] {
|
||||
case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-':
|
||||
acc += string(line[i])
|
||||
case 'f':
|
||||
s.on = false
|
||||
case 'n':
|
||||
s.on = true
|
||||
case 'x', 'y', 'z':
|
||||
state = line[i]
|
||||
case '.', ',':
|
||||
if line[i] == '.' {
|
||||
i++ // skip second .
|
||||
}
|
||||
switch state {
|
||||
case 'x':
|
||||
s.x = append(s.x, mustAtoi(acc))
|
||||
case 'y':
|
||||
s.y = append(s.y, mustAtoi(acc))
|
||||
case 'z':
|
||||
s.z = append(s.z, mustAtoi(acc))
|
||||
}
|
||||
acc = ""
|
||||
default: // all other characters do nothing
|
||||
}
|
||||
}
|
||||
s.z = append(s.z, mustAtoi(acc)) // catch last number
|
||||
return s
|
||||
}
|
||||
|
||||
type coord struct {
|
||||
x int
|
||||
y int
|
||||
z int
|
||||
}
|
||||
|
||||
func GenerateCoords(s step) []coord {
|
||||
c := []coord{}
|
||||
for x := s.x[0]; x <= s.x[1]; x++ {
|
||||
for y := s.y[0]; y <= s.y[1]; y++ {
|
||||
for z := s.z[0]; z <= s.z[1]; z++ {
|
||||
c = append(c, coord{x, y, z})
|
||||
}
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(true)
|
||||
|
||||
grid := map[coord]struct{}{}
|
||||
steps := []step{}
|
||||
for scanner.Scan() {
|
||||
steps = append(steps, ParseInput(scanner.Text()))
|
||||
}
|
||||
for _, step := range steps[0:20] {
|
||||
for _, coord := range GenerateCoords(step) {
|
||||
if step.on {
|
||||
grid[coord] = struct{}{}
|
||||
} else {
|
||||
delete(grid, coord)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(len(grid))
|
||||
}
|
||||
|
||||
func partTwo() {
|
||||
scanner := makeScanner(true)
|
||||
|
||||
grid := map[coord]struct{}{}
|
||||
steps := []step{}
|
||||
for scanner.Scan() {
|
||||
steps = append(steps, ParseInput(scanner.Text()))
|
||||
}
|
||||
// this ain't gonna work
|
||||
for _, step := range steps {
|
||||
for _, coord := range GenerateCoords(step) {
|
||||
if step.on {
|
||||
grid[coord] = struct{}{}
|
||||
} else {
|
||||
delete(grid, coord)
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Println(len(grid))
|
||||
}
|
||||
110
25/main.go
Normal file
110
25/main.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
partOne()
|
||||
duration := time.Since(start)
|
||||
fmt.Printf("p1: %s\n", duration)
|
||||
}
|
||||
|
||||
func makeScanner(test bool) *bufio.Scanner {
|
||||
var f *os.File
|
||||
if test {
|
||||
f, _ = os.Open("inputs/testinput")
|
||||
} else {
|
||||
f, _ = os.Open("inputs/input")
|
||||
}
|
||||
reader := bufio.NewReader(f)
|
||||
return bufio.NewScanner(reader)
|
||||
}
|
||||
|
||||
type coord struct {
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
type floor struct {
|
||||
steps int
|
||||
width int
|
||||
height int
|
||||
m map[coord]rune
|
||||
}
|
||||
|
||||
func (f *floor) Print() {
|
||||
for y := 0; y < f.height; y++ {
|
||||
for x := 0; x < f.width; x++ {
|
||||
fmt.Printf("%s", string(f.m[coord{x: x, y: y}]))
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
func (f *floor) Step() bool {
|
||||
f.steps++
|
||||
moved := false
|
||||
toMove := map[coord]coord{}
|
||||
for y := 0; y < f.height; y++ {
|
||||
for x := 0; x < f.width; x++ {
|
||||
current := coord{x: x, y: y}
|
||||
x += 1
|
||||
if x >= f.width {
|
||||
x = 0
|
||||
}
|
||||
if f.m[current] == '>' && f.m[coord{x: x, y: y}] == '.' {
|
||||
toMove[current] = coord{x: x, y: y}
|
||||
moved = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for current, next := range toMove {
|
||||
f.m[next] = f.m[current]
|
||||
f.m[current] = '.'
|
||||
}
|
||||
toMove = map[coord]coord{}
|
||||
for y := 0; y < f.height; y++ {
|
||||
for x := 0; x < f.width; x++ {
|
||||
current := coord{x: x, y: y}
|
||||
y += 1
|
||||
if y >= f.height {
|
||||
y = 0
|
||||
}
|
||||
if f.m[current] == 'v' && f.m[coord{x: x, y: y}] == '.' {
|
||||
toMove[current] = coord{x: x, y: y}
|
||||
moved = true
|
||||
}
|
||||
}
|
||||
}
|
||||
for current, next := range toMove {
|
||||
f.m[next] = f.m[current]
|
||||
f.m[current] = '.'
|
||||
}
|
||||
return moved
|
||||
}
|
||||
|
||||
func partOne() {
|
||||
scanner := makeScanner(false)
|
||||
f := &floor{
|
||||
m: make(map[coord]rune),
|
||||
}
|
||||
y := 0
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
f.width = len(line)
|
||||
for x, v := range line {
|
||||
f.m[coord{x: x, y: y}] = v
|
||||
}
|
||||
y++
|
||||
}
|
||||
f.height = y
|
||||
for f.Step() {
|
||||
}
|
||||
fmt.Println(f.steps)
|
||||
}
|
||||
Reference in New Issue
Block a user