day 12,13
This commit is contained in:
136
2023/go/day12/day12.go
Normal file
136
2023/go/day12/day12.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package day12
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
"adventofcode2023/utils/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cache = make(map[string]int)
|
||||||
|
|
||||||
|
type memoized struct {
|
||||||
|
f func(int) int
|
||||||
|
cache map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Part1(input string) int {
|
||||||
|
ans := 0
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
slices := strings.Split(line, " ")
|
||||||
|
record := slices[0]
|
||||||
|
groups := inputs.ToInts(slices[1], ",")
|
||||||
|
ans += calc(record, groups)
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func Part2(input string) int {
|
||||||
|
ans := 0
|
||||||
|
lines := strings.Split(input, "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
slices := strings.Split(line, " ")
|
||||||
|
record := slices[0] + "?" + slices[0] + "?" + slices[0] + "?" + slices[0] + "?" + slices[0]
|
||||||
|
groups := inputs.ToInts(slices[1] + "," + slices[1] + "," + slices[1] + "," + slices[1] + "," + slices[1] , ",")
|
||||||
|
ans += calc(record, groups)
|
||||||
|
fmt.Printf("running total after lines %v = %v\n",i, ans)
|
||||||
|
}
|
||||||
|
return ans
|
||||||
|
}
|
||||||
|
|
||||||
|
func calc(record string, groups []int) int {
|
||||||
|
key := record + fmt.Sprint(groups)
|
||||||
|
if v, ok := cache[key]; ok {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
// ADD LOGIC HERE ... Base-case logic will go here
|
||||||
|
// Did we run out of groups? We might still be valid
|
||||||
|
if len(groups) == 0 {
|
||||||
|
// Make sure there aren't any more damaged springs, if so, we're valid
|
||||||
|
if ! strings.Contains(record, "#") {
|
||||||
|
cache[key] = 1
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
// Not valid to runout of groups and still have damaged springs "#"
|
||||||
|
cache[key] = 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(record) == 0 {
|
||||||
|
// Already know there are more groups but no records
|
||||||
|
cache[key] = 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at the next element in each record and group
|
||||||
|
next_character := record[0]
|
||||||
|
next_group := groups[0]
|
||||||
|
out := 0
|
||||||
|
|
||||||
|
if next_character == '#' {
|
||||||
|
// Test pound logic
|
||||||
|
out = pound(record, groups, next_group)
|
||||||
|
} else if next_character == '.' {
|
||||||
|
// Test dot logic
|
||||||
|
out = dot(record, groups)
|
||||||
|
} else if next_character == '?' {
|
||||||
|
// This character could be either character, so we'll explore both
|
||||||
|
//possibilities
|
||||||
|
out = dot(record, groups) + pound(record, groups, next_group)
|
||||||
|
} else {
|
||||||
|
utils.PanicOnErr(fmt.Errorf("RuntimeError"))
|
||||||
|
|
||||||
|
}
|
||||||
|
// Help with debugging
|
||||||
|
// fmt.Println(record, groups, out)
|
||||||
|
cache[key] = out
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic that treats the first character as pound-sign "#"
|
||||||
|
func pound(record string, groups []int, next_group int) int {
|
||||||
|
|
||||||
|
// If the first is a pound, then the first n characters must be
|
||||||
|
// able to be treated as a pound, where n is the first group number
|
||||||
|
if next_group > len(record) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
this_group := record[:next_group]
|
||||||
|
this_group = strings.Replace(this_group, "?", "#", -1)
|
||||||
|
|
||||||
|
// If the next group can't fit all the damaged springs, then abort
|
||||||
|
if this_group != strings.Repeat("#", next_group) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the rest of the record is just the last group, then we're
|
||||||
|
// done and there's only one possibility
|
||||||
|
if len(record) == next_group {
|
||||||
|
// Make sure this is the last group
|
||||||
|
if len(groups) == 1 {
|
||||||
|
//We are valid
|
||||||
|
return 1
|
||||||
|
} else {
|
||||||
|
// There's more groups, we can't make it work
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the character that follows this group can be a seperator
|
||||||
|
if record[next_group] == '?' || record[next_group] == '.' {
|
||||||
|
// It can be seperator, so skip it and reduce to the next group
|
||||||
|
return calc(record[next_group+1:], groups[1:])
|
||||||
|
}
|
||||||
|
// Can't be handled, there are no possibilites
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic that treats the first character as dot "."
|
||||||
|
func dot(record string, groups []int) int {
|
||||||
|
// ADD LOGIC HERE ... need to process this character and call
|
||||||
|
// We just skip over the dot looking for the next pound
|
||||||
|
return calc(record[1:], groups)
|
||||||
|
}
|
||||||
29
2023/go/day12/day12_test.go
Normal file
29
2023/go/day12/day12_test.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
package day12
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPart1(t *testing.T) {
|
||||||
|
r := Part1(
|
||||||
|
`???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1`)
|
||||||
|
require.Equal(t, 21, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart2(t *testing.T) {
|
||||||
|
r := Part2(
|
||||||
|
`???.### 1,1,3
|
||||||
|
.??..??...?##. 1,1,3
|
||||||
|
?#?#?#?#?#?#?#? 1,3,1,6
|
||||||
|
????.#...#... 4,1,1
|
||||||
|
????.######..#####. 1,6,5
|
||||||
|
?###???????? 3,2,1`)
|
||||||
|
require.Equal(t, 525152, r)
|
||||||
|
}
|
||||||
1000
2023/go/day12/input.txt
Normal file
1000
2023/go/day12/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
144
2023/go/day13/day13.go
Normal file
144
2023/go/day13/day13.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package day13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"adventofcode2023/utils"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mirrorLoc struct {
|
||||||
|
orient string
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
func Part1(input string) int {
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
patterns := strings.Split(input, "\n\n")
|
||||||
|
for p, pattern := range patterns {
|
||||||
|
rows := strings.Split(pattern, "\n")
|
||||||
|
if v, ok := findMirror(rows); ok {
|
||||||
|
count = count + v * 100
|
||||||
|
} else {
|
||||||
|
cols := swap(rows)
|
||||||
|
if v, ok := findMirror(cols); ok {
|
||||||
|
count = count + v
|
||||||
|
} else {
|
||||||
|
utils.PanicOnErr(fmt.Errorf("p: %v\n %v\n", p, pattern))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func Part2(input string) int {
|
||||||
|
count := 0
|
||||||
|
patterns := strings.Split(input, "\n\n")
|
||||||
|
|
||||||
|
for p, pattern := range patterns {
|
||||||
|
rows := strings.Split(pattern, "\n")
|
||||||
|
loc, _ := findMirrorLoc(rows)
|
||||||
|
found := false
|
||||||
|
for i:=0;i<len(rows[0])*len(rows);i++{
|
||||||
|
newRows := smudge(i, rows)
|
||||||
|
if v, ok := findNewMirrorLoc(loc, newRows); ok {
|
||||||
|
if v != loc {
|
||||||
|
found = true
|
||||||
|
if v.orient == "row" {
|
||||||
|
count = count + v.index * 100
|
||||||
|
} else {
|
||||||
|
count = count + v.index
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ! found {
|
||||||
|
utils.PanicOnErr(fmt.Errorf("p: %v\n %v\n", p, pattern))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
func smudge(i int, rows []string) []string {
|
||||||
|
out := make([]string, len(rows))
|
||||||
|
copy(out, rows)
|
||||||
|
col := i % len(rows[0])
|
||||||
|
row := i / len(rows[0])
|
||||||
|
if col >= 0 && col < len(rows[0]) {
|
||||||
|
v := rows[row][col]
|
||||||
|
if v == '#' {
|
||||||
|
out[row] = out[row][:col] + "." + out[row][col+1:]
|
||||||
|
} else {
|
||||||
|
out[row] = out[row][:col] + "#" + out[row][col+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNewMirrorLoc(loc mirrorLoc, rows []string) (mirrorLoc, bool) {
|
||||||
|
if v, ok := findNewMirror(loc, "row", rows); ok {
|
||||||
|
return mirrorLoc{"row", v}, true
|
||||||
|
}
|
||||||
|
cols := swap(rows)
|
||||||
|
if v, ok := findNewMirror(loc, "col", cols); ok {
|
||||||
|
return mirrorLoc{"col", v}, true
|
||||||
|
}
|
||||||
|
return mirrorLoc{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMirrorLoc(rows []string) (mirrorLoc, bool) {
|
||||||
|
if v, ok := findMirror(rows); ok {
|
||||||
|
return mirrorLoc{"row", v}, true
|
||||||
|
}
|
||||||
|
cols := swap(rows)
|
||||||
|
if v, ok := findMirror(cols); ok {
|
||||||
|
return mirrorLoc{"col", v}, true
|
||||||
|
}
|
||||||
|
return mirrorLoc{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func swap(in []string) []string {
|
||||||
|
out := []string{}
|
||||||
|
for j:=0;j<len(in[0]);j++ {
|
||||||
|
o := ""
|
||||||
|
for i:=0;i<len(in);i++ {
|
||||||
|
o = o + string(in[i][j])
|
||||||
|
}
|
||||||
|
out = append(out, o)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func findMirror(rows []string) (int, bool) {
|
||||||
|
for i:=0;i<len(rows)-1;i++ {
|
||||||
|
if rows[i] == rows[i+1] {
|
||||||
|
if isMirror(i, 0, rows) {
|
||||||
|
return i+1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNewMirror(loc mirrorLoc, orient string, rows []string) (int, bool) {
|
||||||
|
for i:=0;i<len(rows)-1;i++ {
|
||||||
|
if orient == loc.orient && i == loc.index - 1{ continue }
|
||||||
|
if rows[i] == rows[i+1] {
|
||||||
|
if isMirror(i, 0, rows) {
|
||||||
|
return i+1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMirror(i int, offset int, rows []string) bool {
|
||||||
|
if i == 0 || i == len(rows) - 1 { return true }
|
||||||
|
|
||||||
|
if i - offset < 0 || i+1+offset > len(rows) - 1 { return true }
|
||||||
|
|
||||||
|
if rows[i+1+offset] == rows[i-offset] {
|
||||||
|
return isMirror(i, offset+1, rows)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
47
2023/go/day13/day13_test.go
Normal file
47
2023/go/day13/day13_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package day13
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPart1(t *testing.T) {
|
||||||
|
r := Part1(
|
||||||
|
`#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#`)
|
||||||
|
require.Equal(t, 405, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPart2(t *testing.T) {
|
||||||
|
r := Part2(
|
||||||
|
`#.##..##.
|
||||||
|
..#.##.#.
|
||||||
|
##......#
|
||||||
|
##......#
|
||||||
|
..#.##.#.
|
||||||
|
..##..##.
|
||||||
|
#.#.##.#.
|
||||||
|
|
||||||
|
#...##..#
|
||||||
|
#....#..#
|
||||||
|
..##..###
|
||||||
|
#####.##.
|
||||||
|
#####.##.
|
||||||
|
..##..###
|
||||||
|
#....#..#`)
|
||||||
|
require.Equal(t, 400, r)
|
||||||
|
}
|
||||||
1377
2023/go/day13/input.txt
Normal file
1377
2023/go/day13/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@ import (
|
|||||||
"adventofcode2023/day09"
|
"adventofcode2023/day09"
|
||||||
"adventofcode2023/day10"
|
"adventofcode2023/day10"
|
||||||
"adventofcode2023/day11"
|
"adventofcode2023/day11"
|
||||||
|
"adventofcode2023/day12"
|
||||||
|
"adventofcode2023/day13"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -62,6 +64,12 @@ func main() {
|
|||||||
case 11:
|
case 11:
|
||||||
fmt.Printf("part 1: %d\n", day11.Part1(utils.Readfile(d)))
|
fmt.Printf("part 1: %d\n", day11.Part1(utils.Readfile(d)))
|
||||||
fmt.Printf("part 2: %d\n", day11.Part2(utils.Readfile(d)))
|
fmt.Printf("part 2: %d\n", day11.Part2(utils.Readfile(d)))
|
||||||
|
case 12:
|
||||||
|
fmt.Printf("part 1: %d\n", day12.Part1(utils.Readfile(d)))
|
||||||
|
fmt.Printf("part 2: %d\n", day12.Part2(utils.Readfile(d)))
|
||||||
|
case 13:
|
||||||
|
fmt.Printf("part 1: %d\n", day13.Part1(utils.Readfile(d)))
|
||||||
|
fmt.Printf("part 2: %d\n", day13.Part2(utils.Readfile(d)))
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("no such day: %d", d))
|
panic(fmt.Errorf("no such day: %d", d))
|
||||||
}
|
}
|
||||||
@@ -69,7 +77,7 @@ func main() {
|
|||||||
|
|
||||||
// Reads day from os.Args.
|
// Reads day from os.Args.
|
||||||
func day() int {
|
func day() int {
|
||||||
latest := 10
|
latest := 12
|
||||||
if len(os.Args) == 1 {
|
if len(os.Args) == 1 {
|
||||||
return latest
|
return latest
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user