Skip to content

Commit

Permalink
Day 12: part 1 - bruteforce
Browse files Browse the repository at this point in the history
  • Loading branch information
dmytro-spivakov committed Oct 20, 2024
1 parent 5dadb6a commit 9076b42
Show file tree
Hide file tree
Showing 6 changed files with 374 additions and 0 deletions.
68 changes: 68 additions & 0 deletions :w
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package day12

import (
"bufio"
"fmt"
"log"
"os"
"strconv"
"strings"
)

type Row struct {
rawSeq []string
damagedGroups []int
}

func Solution1(filepath string) int {
m := parseInput(filepath)
printMatrix(m)
return -1
}

func Solution2(filepath string) int {
return -1
}

func parseInput(filepath string) []Row {
file, err := os.Open(filepath)
if err != nil {
log.Fatalf("Failed to open the input file with: %v\n", err.Error())
}

var m []Row
scanner := bufio.NewScanner(file)
for scanner.Scan() {
divInput := strings.Split(scanner.Text(), " ")
seq := strings.Split(divInput[0], "")
dmgGroups := strings.Split(divInput[1], ",")
m = append(m, Row{rawSeq: seq, damagedGroups: parseInts(dmgGroups)})
}
if err = scanner.Err(); err != nil {
log.Fatalf("Error during input file read: %v\n", err.Error())
}

return m
}

func printRows(rows []Row) {
fmt.Println("-----MATRIX START-----")
for _, row := range rows {
fmt.Println(row)
}
fmt.Println("------MATRIX END-----")
}

func parseInts(sNums []string) []int {
var nums []int
for _, sNum := range sNums {
num64, err := strconv.ParseInt(sNum, 10, 64)
if err != nil {
log.Fatalf("Failed to parse number %v with: %v\n", sNum, err.Error())
}

nums = append(nums, int(num64))
}

return nums
}
196 changes: 196 additions & 0 deletions day12/day12.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package day12

import (
"bufio"
"fmt"
"log"
"os"
"regexp"
"slices"
"strconv"
"strings"
)

type Row struct {
rawSeq []string
damagedGroups []int
}

func Solution1(filepath string) int {
rows := parseInput(filepath)
printRows(rows)

result := 0
for _, r := range rows {
result += countCombinations(r)
}
return result
}

func Solution2(filepath string) int {
return -1
}

func countCombinations(r Row) int {
unsolvedChunks := getUnsolvedChunkRanges(r.rawSeq)

// { startIdx: []{all possible substrings of len()=length}, ... }
chunkVars := make(map[int][]string)
for idx, length := range unsolvedChunks {
chunkVars[idx] = getPossibleCombinations(length, "")
}

// chunkVars to generate full row strings of all potential combinations of . and # in place of ?
var chunksOrder []int
for chunkIdx, _ := range chunkVars {
chunksOrder = append(chunksOrder, chunkIdx)
}
slices.Sort(chunksOrder)

varStrings := getAllPossibleStrings(strings.Join(r.rawSeq, ""), chunkVars, chunksOrder)
// fmt.Println("ALL STRINGS:")
// for _, vs := range varStrings {
// fmt.Println(vs)
// }
// fmt.Println("--------------")

// count valid strings
result := 0
expectedGroups := r.damagedGroups

for _, v := range varStrings {
dmgRegex := regexp.MustCompile(`#+`)
matches := dmgRegex.FindAllString(v, -1)
var groups []int
for _, m := range matches {
groups = append(groups, len(m))
}

if slices.Equal(expectedGroups, groups) {
result += 1
}
}

return result
}

func getUnsolvedChunkRanges(s []string) map[int]int {
// { startIdx: length }
unsolvedChunks := make(map[int]int)

start := -1
currentSlice := false
for i, char := range s {
if char == "?" {
if !currentSlice {
currentSlice = true
start = i
}
} else {
if currentSlice {
unsolvedChunks[start] = i - start
currentSlice = false
start = -1
}
}
}
if currentSlice {
unsolvedChunks[start] = len(s) - start
}

return unsolvedChunks
}

func getAllPossibleStrings(baseString string, chunkVariations map[int][]string, chunksOrder []int) []string {
var result []string

if len(chunksOrder) == 0 {
return []string{baseString}
}

idx := chunksOrder[0]
for _, v := range chunkVariations[idx] {
newBaseString := ""
if idx > 0 {
newBaseString = baseString[:idx]
}
newBaseString += v
if insertEndIdx := idx + len([]rune(v)); insertEndIdx < len([]rune(baseString)) {
newBaseString += baseString[insertEndIdx:]
}

var newOrderChunk []int
if len(chunksOrder) > 0 {
newOrderChunk = chunksOrder[1:]
} else {
newOrderChunk = make([]int, 0)
}
result = append(
result,
getAllPossibleStrings(newBaseString, chunkVariations, newOrderChunk)...,
)
}
return result
}

func getPossibleCombinations(length int, currentComb string) []string {
var combs []string

if length < 0 {
log.Fatalln("bruh")
}

if length == 1 {
combA := currentComb + "."
combB := currentComb + "#"
return []string{combA, combB}
}

combs = append(combs, getPossibleCombinations(length-1, currentComb+".")...)
combs = append(combs, getPossibleCombinations(length-1, currentComb+"#")...)

return combs
}

func parseInput(filepath string) []Row {
file, err := os.Open(filepath)
if err != nil {
log.Fatalf("Failed to open the input file with: %v\n", err.Error())
}

var m []Row
scanner := bufio.NewScanner(file)
for scanner.Scan() {
divInput := strings.Split(scanner.Text(), " ")
seq := strings.Split(divInput[0], "")
dmgGroups := strings.Split(divInput[1], ",")
m = append(m, Row{rawSeq: seq, damagedGroups: parseInts(dmgGroups)})
}
if err = scanner.Err(); err != nil {
log.Fatalf("Error during input file read: %v\n", err.Error())
}

return m
}

func printRows(rows []Row) {
fmt.Println("-----MATRIX START-----")
for i, row := range rows {
fmt.Printf("Row %d: %v | %v\n", i, strings.Join(row.rawSeq, ""), row.damagedGroups)
}
fmt.Println("------MATRIX END-----")
}

func parseInts(sNums []string) []int {
var nums []int
for _, sNum := range sNums {
num64, err := strconv.ParseInt(sNum, 10, 64)
if err != nil {
log.Fatalf("Failed to parse number %v with: %v\n", sNum, err.Error())
}

nums = append(nums, int(num64))
}

return nums
}
97 changes: 97 additions & 0 deletions day12/day12_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package day12

import (
"fmt"
"maps"
"slices"
"strings"
"testing"
)

func TestSolutio1(t *testing.T) {
cases := map[string]int{
"test_input12": 6,
"test_input11": 21,
}

for input, expectedResult := range cases {
result := Solution1(input)

if result == expectedResult {
fmt.Printf("Solution1()=%d, OK\n", result)
} else {
t.Fatalf("Solution1()=%d, expecting %d, FAIL\n", result, expectedResult)
}
}
}

func TestGetAllPossibleStrings(t *testing.T) {
input := "?..?##.?"
expectedResult := []string{
"#..###.#",
"#..###..",
"#...##.#",
"#...##..",
"...###.#",
"...###..",
"....##.#",
"....##..",
}
chunkVars := map[int][]string{
0: {".", "#"},
3: {".", "#"},
7: {".", "#"},
}

result := getAllPossibleStrings(input, chunkVars, []int{0, 3, 7})
slices.Sort(expectedResult)
slices.Sort(result)
if slices.Equal(result, expectedResult) {
fmt.Printf("getAllPossibleStrings()=%v; OK\n", result)
} else {
t.Fatalf("getAllPossibleStrings()=%v, expecting %v; FAIL\n", result, expectedResult)
}
}

func TestGetAllPossibleCombinations(t *testing.T) {
cases := map[int][]string{
1: {".", "#"},
2: {
"..",
"#.",
".#",
"##",
},
}

for input, expectedResult := range cases {
result := getPossibleCombinations(input, "")
slices.Sort(result)
slices.Sort(expectedResult)

if slices.Equal(result, expectedResult) {
fmt.Printf("getPossibleCombinations()=%v, OK\n", result)
} else {
t.Fatalf("getPossibleCombinations()=%v, expecting %v, FAIL\n", result, expectedResult)
}
}
}

func TestGetAllUnsolvedChunkRanges(t *testing.T) {
cases := map[string]map[int]int{
"???.###": {0: 3},
".??..??...?##.": {1: 2, 5: 2, 10: 1},
"?#?#?": {0: 1, 2: 1, 4: 1},
"?###???????": {0: 1, 4: 7},
}

for input, expectedResult := range cases {
result := getUnsolvedChunkRanges(strings.Split(input, ""))

if maps.Equal(result, expectedResult) {
fmt.Printf("getUnsolvedChunkRanges()=%v; OK\n", result)
} else {
t.Fatalf("getUnsolvedChunkRanges()=%v, expecting %v; FAIL\n", result, expectedResult)
}
}
}
6 changes: 6 additions & 0 deletions day12/test_input11
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
????.#...#... 4,1,1
????.######..#####. 1,6,5
?###???????? 3,2,1
3 changes: 3 additions & 0 deletions day12/test_input12
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
???.### 1,1,3
.??..??...?##. 1,1,3
?#?#?#?#?#?#?#? 1,3,1,6
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"advent-of-code/day09"
"advent-of-code/day10"
"advent-of-code/day11"
"advent-of-code/day12"
"fmt"
)

Expand Down Expand Up @@ -48,4 +49,7 @@ func main() {

fmt.Printf("Day 11, solution 1: %v\n", day11.Solution1("inputs/day11"))
fmt.Printf("Day 11, solution 2: %v\n", day11.Solution2("inputs/day11"))

fmt.Printf("Day 12, solution 1: %v\n", day12.Solution1("inputs/day12"))
fmt.Printf("Day 12, solution 2: %v\n", day12.Solution2("inputs/day12"))
}

0 comments on commit 9076b42

Please sign in to comment.