Compare commits

...

4 Commits

Author SHA1 Message Date
efim e771ac9d9b day19, part2, finally 2023-12-19 11:46:16 +00:00
efim 52beb4196f day19, struggling part2, not quite yet 2023-12-19 11:00:59 +00:00
efim 5f62ea45f7 day19, example 2023-12-19 09:03:03 +00:00
efim 6efd55ae6a day18, example second parallel 2023-12-19 06:53:49 +00:00
8 changed files with 593 additions and 82 deletions

2
.gitignore vendored
View File

@ -1,2 +1,4 @@
/.direnv/ /.direnv/
/.go /.go
example
/day19/input

View File

@ -7,12 +7,13 @@ import (
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
"sync"
) )
func Run() int { func Run() int {
log.Println("hello day 18") log.Println("hello day 18")
log.Println("problem of lagoon bgins") log.Println("problem of lagoon bgins")
filename := "day18/example" filename := "day18/input"
instructions := ReadInstructionas2(filename) instructions := ReadInstructionas2(filename)
h, w := calcHeightWidth(instructions) h, w := calcHeightWidth(instructions)
log.Printf("read %+v instructions", instructions) log.Printf("read %+v instructions", instructions)
@ -210,8 +211,10 @@ type Cell struct {
} }
type BorderSymbol rune type BorderSymbol rune
// '' always left to right
const (Vertical BorderSymbol = '|' // ” always left to right
const (
Vertical BorderSymbol = '|'
ToDown BorderSymbol = '7' ToDown BorderSymbol = '7'
ToUp BorderSymbol = 'J' ToUp BorderSymbol = 'J'
FromUp BorderSymbol = 'F' FromUp BorderSymbol = 'F'
@ -225,19 +228,20 @@ type Field struct {
MinRow, MaxRow, MinCol, MaxCol int MinRow, MaxRow, MinCol, MaxCol int
BordersFromLeft map[int]map[int]BorderSymbol BordersFromLeft map[int]map[int]BorderSymbol
} }
func (f *Field)confirmCoord(c Coord) {
func (f *Field) confirmCoord(c Coord) {
// log.Printf("configming coord %+v", c) // log.Printf("configming coord %+v", c)
if c.Row - 3 < f.MinRow { if c.Row-3 < f.MinRow {
f.MinRow = c.Row - 3 f.MinRow = c.Row - 3
} }
if c.Row + 3 > f.MaxRow { if c.Row+3 > f.MaxRow {
f.MaxRow = c.Row + 3 f.MaxRow = c.Row + 3
} }
if c.Col - 3 < f.MinCol { if c.Col-3 < f.MinCol {
f.MinCol = c.Col - 3 f.MinCol = c.Col - 3
} }
if c.Col + 3 > f.MaxCol { if c.Col+3 > f.MaxCol {
f.MaxCol = c.Col + 3 f.MaxCol = c.Col + 3
} }
} }
@ -337,7 +341,6 @@ func getTurnAsIfGoingFromLeft(directionFrom, directionTo Direction) []BorderSym
symbol = FromUp symbol = FromUp
} }
// panic(fmt.Sprint("got strange from %s to %s", directionFrom.String(), directionTo.String())) // panic(fmt.Sprint("got strange from %s to %s", directionFrom.String(), directionTo.String()))
return []BorderSymbol{symbol} return []BorderSymbol{symbol}
} }
@ -346,27 +349,27 @@ func (f *Field) String() string {
s := "text 15,15 \"" s := "text 15,15 \""
for row := f.MinRow; row <= f.MaxRow; row++ { for row := f.MinRow; row <= f.MaxRow; row++ {
rowChars := make([]rune, f.MaxCol - f.MinCol + 1) rowChars := make([]rune, f.MaxCol-f.MinCol+1)
for col := f.MinCol; col <= f.MaxCol; col++ { for col := f.MinCol; col <= f.MaxCol; col++ {
rowBords := f.BordersFromLeft[row] rowBords := f.BordersFromLeft[row]
if rowBords != nil { if rowBords != nil {
bord, exists := rowBords[col] bord, exists := rowBords[col]
if exists { if exists {
rowChars[col - f.MinCol] = rune(bord) rowChars[col-f.MinCol] = rune(bord)
continue continue
} }
} }
cell := f.Cells[Coord{col, row}] cell := f.Cells[Coord{col, row}]
if cell != nil && cell.ToBeDug { if cell != nil && cell.ToBeDug {
rowChars[col - f.MinCol] = '@' rowChars[col-f.MinCol] = '@'
continue continue
} }
if f.isCellDug(row, col) { if f.isCellDug(row, col) {
rowChars[col - f.MinCol] = '#' rowChars[col-f.MinCol] = '#'
} else { } else {
rowChars[col - f.MinCol] = '.' rowChars[col-f.MinCol] = '.'
} }
} }
@ -376,14 +379,36 @@ func (f *Field) String() string {
s += "\"" s += "\""
return s return s
} }
func (f *Field) digInsides() (countInside int) { func (f *Field) digInsides() (result int) {
lineSum := make(chan int)
var wg sync.WaitGroup
rowsCount := f.MaxRow - f.MinRow
wg.Add(rowsCount)
done := make(chan bool)
go func() {
wg.Wait()
close(lineSum)
}()
go func() {
for rowInternalCount := range lineSum {
result += rowInternalCount
}
close(done)
}()
for row := f.MinRow; row < f.MaxRow; row++ { for row := f.MinRow; row < f.MaxRow; row++ {
if row % 10000 == 0 { go func(row int){
if row%10000 == 0 {
log.Printf("processed rows %d out of %d", row, f.MaxRow) log.Printf("processed rows %d out of %d", row, f.MaxRow)
} }
specialBorders := f.BordersFromLeft[row] specialBorders := f.BordersFromLeft[row]
if len(specialBorders) == 0 { if len(specialBorders) == 0 {
continue wg.Done()
return
} }
type BorderItem struct { type BorderItem struct {
border BorderSymbol border BorderSymbol
@ -425,13 +450,14 @@ func (f *Field) digInsides() (countInside int) {
continue continue
} }
if bordersCrossed % 2 == 1 { // is in if bordersCrossed%2 == 1 { // is in
for col := prevBorder.col+1; col < specialBorder.col; col++ { for col := prevBorder.col + 1; col < specialBorder.col; col++ {
// f.Cells[Coord{Col: col, Row: row}] = &Cell{ // f.Cells[Coord{Col: col, Row: row}] = &Cell{
// ToBeDug: true, // ToBeDug: true,
// } // }
} }
countInside += diff lineSum <- diff
// countInside += diff
} }
if specialBorder.border == Vertical { if specialBorder.border == Vertical {
@ -440,8 +466,14 @@ func (f *Field) digInsides() (countInside int) {
prevBorder = specialBorder prevBorder = specialBorder
} }
wg.Done()
}(row)
} }
return
<-done
return result
} }
// func (f *Field) digInsides() (countInside int) { // func (f *Field) digInsides() (countInside int) {

View File

@ -34,3 +34,18 @@ day18 result: 952408144115
952408144115 952408144115
*** YES. *** YES.
*** about 1M for 4 minutes
** so, my input is ~16M rows
3.5 seconds per 10k
** well, maybe i can parallel.
*** parallel example
day18 result: 952407566854
*** and with separate done channel from the summing goroutine
952408144115
**** YES
** and
2023/12/18 23:35:31 border is 195341588; inside is 148441957805559
2023/12/18 23:35:31
day18 result: 148442153147147
* i should have used a formula. maybe then it would taken less than 4 hours

5
day19/example1 Normal file
View File

@ -0,0 +1,5 @@
in{x<4000:R,m<4000:R,A}
px{a<4000:R,A}
qqz{s>2770:R,m<1801:A,R}
{x=787,m=2655,a=1222,s=2876}

57
day19/intervals.go Normal file
View File

@ -0,0 +1,57 @@
package day19
import (
"sort"
)
func merge(intervals [][]int) [][]int {
const start, end = 0, 1
var merged [][]int
if len(intervals) > 1 {
sort.Slice(intervals, func(i, j int) bool {
return intervals[i][start] < intervals[j][start]
})
}
for _, interval := range intervals {
last := len(merged) - 1
if last < 0 || interval[start] > merged[last][end] {
merged = append(merged,
[]int{start: interval[start], end: interval[end]},
)
} else if interval[end] > merged[last][end] {
merged[last][end] = interval[end]
}
}
return merged[:len(merged):len(merged)]
}
func applyLessThan(intervals [][]int, n int) [][]int {
var lessers [][]int
for _, interval := range intervals {
from := interval[0]
if from >= n {
continue
}
lessers = append(lessers, []int{from, n-1})
}
return lessers
}
func applyMoreThan(intervals [][]int, n int) [][]int {
var greaters [][]int
for _, interval := range intervals {
to := interval[1]
if to <= n {
continue
}
greaters = append(greaters, []int{n+1, to})
}
// log.Printf(">>>> in applyMoreThan %d to %+v ; result %+v", n, intervals, greaters)
return greaters
}

47
day19/notes.org Normal file
View File

@ -0,0 +1,47 @@
#+title: Notes
* testing things
testSorter := day19.ReadSorterLine("qqz{s>2770:qs,m<1801:hdj,R}")
log.Printf("my test sorter is %+v", testSorter)
testOperation := day19.ReadOperationLine("s>2770:qs")
log.Println(testOperation)
** testing simplification
lnx{m>1548:A,A}
qqz{s>2770:qs,m<1801:hdj,R}
kt{m>2215:R,x>3386:A,x<3107:R,R}
testSorter := day19.ReadSorterLine("kt{m>2215:R,x>3386:A,x<3107:R,R}")
log.Printf("my test sorter is %+v", testSorter)
simplified := day19.SimplifyOperation(testSorter)
log.Printf("> simplivied %+v", simplified)
* i probably don't need 'actual actors'
just a generic function that takes 'detail' and 'sorterData'
then applies sorterData to the detail,
and calls itself with new sorter
with special cases for "R" and "A"
so. have funciton from OpeartionData & Detail -> true/false
if true take the destination, if false, check next
* well. only way to do this is with intervals
so, sorter check takes in interval.
then for each of the rule,
call first rule with full interval,
deduct first rule (for those that don't match) and pass to second.
deduct second and pass to next
A will return full
R will return empty
and results from each rule application should be joined
so. i need interval deduction
and i need interval join
* found a bug in always using initial intervals to calculate 'failing' after each step
2023/12/19 11:45:14 got and checked 167409079868000
In the above example, there are 167409079868000 distinct combinations of ratings that will be accepted.

341
day19/sortingParts.go Normal file
View File

@ -0,0 +1,341 @@
package day19
import (
"fmt"
"log"
"os"
"regexp"
"strconv"
"strings"
"sync"
)
func Run() int {
fmt.Println("hello day 19. sorting parts")
filename := "day19/input"
bytes, err := os.ReadFile(filename)
if err != nil {
panic(fmt.Sprint("cannot read file ", filename))
}
text := string(bytes)
split := strings.Split(text, "\n\n")
sorters := ReadSorters(split[0])
details := ReadDetailsPart(split[1])
log.Printf("yay, got sorters\n%+v\nand details\n%+v", sorters, details)
// countApproved := CountApprovedDetails(details, sorters)
result := 0
fullIntervals := AttrIntervals{
"x": [][]int{[]int{1, 4000}},
"m": [][]int{[]int{1, 4000}},
"a": [][]int{[]int{1, 4000}},
"s": [][]int{[]int{1, 4000}},
}
andChecked := processInterval(fullIntervals, "in", sorters)
log.Print("got and checked ", andChecked)
result = andChecked
return result
}
func CountApprovedDetails(details []DetailData, sorters map[string]SorterData) int {
var wg sync.WaitGroup
wg.Add(len(details))
approvedDetails := make(chan DetailData)
go func() {
wg.Wait()
close(approvedDetails)
}()
count := 0
acceptedScore := 0
done := make(chan any)
go func() {
for detail := range approvedDetails {
log.Println("got approved ", detail)
count += 1
for _, attrValue := range detail.Attrs {
acceptedScore += attrValue
}
}
close(done)
}()
for _, d := range details {
go func(d DetailData) {
log.Print("> starting for ", d)
isAccepted := ProcessDetail(d, sorters)
if isAccepted {
log.Println("> accepting ", d)
approvedDetails <- d
} else {
log.Println("> rejecting ", d)
}
wg.Done()
}(d)
}
<-done
return acceptedScore
}
type Operation rune
const (
LessThan Operation = '<'
MoreThan Operation = '>'
)
func (o Operation)String() string {
return string(o)
}
type OperationData struct {
AttrName string
Operation Operation
Num int
SentToName string
InitialString string
}
func (od OperationData)String() string {
return od.InitialString
}
type SorterData struct {
Name string
DefaultState string
Operations []OperationData
}
func ReadSorters(sortersText string) map[string]SorterData {
result := make(map[string]SorterData)
sortersText = strings.TrimSpace(sortersText)
lines := strings.Split(sortersText, "\n")
for _, line := range lines {
sorter := SimplifyOperation( ReadSorterLine(line) )
result[sorter.Name] = sorter
}
return result
}
// qqz{s>2770:qs,m<1801:hdj,R}
func ReadSorterLine(line string) (result SorterData) {
re1 := regexp.MustCompile(`(?P<NAME>\D+){(?P<OPERATIONS>.+)}`)
firstSplit := re1.FindStringSubmatch(line)
result.Name = firstSplit[1]
operationLines := strings.Split(firstSplit[2], ",")
operations := make([]OperationData, len(operationLines)-1)
result.Operations = operations
result.DefaultState = operationLines[len(operationLines)-1]
for i, line := range operationLines[:len(operationLines)-1] {
operations[i] = ReadOperationLine(line)
}
log.Printf("mathed %s got %+v; operations : %+v\n", line, firstSplit, operations)
return
}
// s>2770:qs
func ReadOperationLine(line string) (result OperationData) {
result.InitialString = line
re := regexp.MustCompile(`(?P<ATTRNAME>\D)(?P<OPERATION>[\>\<])(?P<NUMBER>\d+):(?P<TARGET>\D+)`)
split := re.FindStringSubmatch(line)
log.Printf("matching operation %s into %+v\n", line, split)
result.AttrName = split[1]
result.Operation = Operation([]rune(split[2])[0])
result.SentToName = split[4]
num, err := strconv.Atoi(split[3])
if err != nil {
panic(fmt.Sprintf("error getting number %s in line %s. %s", split[3], line, err))
}
result.Num = num
return
}
// drop last operations which target same 'next' as default. these check are not necessary
func SimplifyOperation(sorter SorterData) SorterData {
actualLast := len(sorter.Operations) - 1
for i := actualLast; i >= 0; i-- {
if sorter.Operations[i].SentToName != sorter.DefaultState {
break
}
actualLast -= 1
}
sorter.Operations = sorter.Operations[:actualLast+1]
return sorter
}
type DetailData struct {
Attrs map[string]int
}
func ReadDetailsPart(text string) (result []DetailData) {
text = strings.TrimSpace(text)
for _, line := range strings.Split(text, "\n") {
result = append(result, ReadDetailLine(line))
}
return
}
// {x=787,m=2655,a=1222,s=2876}
func ReadDetailLine(line string) (result DetailData) {
attrs := make(map[string]int)
result.Attrs = attrs
line = line[1 : len(line)-1]
attrsLine := strings.Split(line, ",")
re := regexp.MustCompile(`(?P<ATTR>\D)=(?P<NUM>\d+)`)
for _, attrLine := range attrsLine {
split := re.FindStringSubmatch(attrLine)
attrName := split[1]
num, err := strconv.Atoi(split[2])
if err != nil {
panic(fmt.Sprint("error parsing detail ", line))
}
attrs[attrName] = num
}
return
}
func ProcessDetail(d DetailData, sorters map[string]SorterData) (isAccepted bool) {
curSorterName := "in"
for curSorterName != "A" && curSorterName != "R" {
sorter, found := sorters[curSorterName]
if !found {
panic(fmt.Sprint("error finding soter ", curSorterName))
}
curSorterName = sorter.NextSorterNameFor(d)
}
return curSorterName == "A"
}
func (s SorterData) NextSorterNameFor(d DetailData) string {
for _, operation := range s.Operations {
if operation.IsDetailPassing(d) {
return operation.SentToName
}
}
return s.DefaultState
}
func (o OperationData) IsDetailPassing(d DetailData) bool {
detailValue := d.Attrs[o.AttrName]
switch o.Operation {
case LessThan:
return detailValue < o.Num
case MoreThan:
return detailValue > o.Num
}
panic(fmt.Sprint("unknown operation. ", o, d))
}
type AttrIntervals map[string][][]int
func (o OperationData) getPassingIntervals(i AttrIntervals) AttrIntervals {
result := make(AttrIntervals, 0)
for key, value := range i {
result[key] = value
}
operationKey := o.AttrName
operatedIntervals := result[operationKey]
switch o.Operation {
case LessThan:
result[operationKey] = applyLessThan(operatedIntervals, o.Num)
case MoreThan:
result[operationKey] = applyMoreThan(operatedIntervals, o.Num)
}
return result
}
func (o OperationData) getFailingIntervals(i AttrIntervals) AttrIntervals {
result := make(AttrIntervals, 0)
for key, value := range i {
result[key] = value
}
operationKey := o.AttrName
operatedIntervals := result[operationKey]
switch o.Operation {
case LessThan:
result[operationKey] = applyMoreThan(operatedIntervals, o.Num-1)
case MoreThan:
result[operationKey] = applyLessThan(operatedIntervals, o.Num+1)
}
return result
}
func processInterval(i AttrIntervals, sorterName string, sorters map[string]SorterData) (combinationsAccepted int) {
if sorterName == "A" {
mul := 1
for key, attrIntervals := range i {
allowedValuesOfAttr := 0
for _, interval := range attrIntervals {
from := interval[0]
to := interval[1]
len := to - from + 1
allowedValuesOfAttr += len
log.Printf("for %s allowed attrs are %d", key, allowedValuesOfAttr)
}
mul *= allowedValuesOfAttr
}
log.Printf("exit recursion for %s. Accept interval %+v . result %d. max is %d", sorterName, i, mul, 40000 * 4000 * 4000 * 4000)
return mul
}
if sorterName == "R" {
return 0
}
s := sorters[sorterName]
log.Printf("> starting interval check for %s (%+v) on %+v", sorterName, s, i)
intervalsPassingOnThisStep := i
for _, operation := range s.Operations {
intervalsPassing := operation.getPassingIntervals(intervalsPassingOnThisStep)
log.Printf(">> %s; in operation %+v. passing are %+v", sorterName, operation, intervalsPassing)
ofThoseAreAccepted := processInterval(intervalsPassing, operation.SentToName, sorters)
combinationsAccepted += ofThoseAreAccepted
log.Printf(">> %s; results so far are %d", sorterName, combinationsAccepted)
intervalsFailingAndPassedToNextCheck := operation.getFailingIntervals(intervalsPassingOnThisStep)
log.Printf(">> %s; failing for the next step %+v", sorterName, intervalsFailingAndPassedToNextCheck)
intervalsPassingOnThisStep = intervalsFailingAndPassedToNextCheck
}
log.Printf(">> %s; about to go into DEFAULT", sorterName)
intervalsAfterDefault := processInterval(intervalsPassingOnThisStep, s.DefaultState, sorters)
log.Printf(">> %s; after defaul. passing are %+v", sorterName, intervalsAfterDefault)
combinationsAccepted += intervalsAfterDefault
log.Printf(">> %s; results after default %d", sorterName, combinationsAccepted)
return
}

18
main.go
View File

@ -3,12 +3,24 @@ package main
import ( import (
"log" "log"
"sunshine.industries/aoc2023/day18" "sunshine.industries/aoc2023/day19"
) )
func main() { func main() {
log.Print("> starting run:") log.Print("> starting run:")
result := day18.Run() // lnx{m>1548:A,A}
log.Printf("\n\nday18 result: %d\n****\n", result) // qqz{s>2770:qs,m<1801:hdj,R}
// kt{m>2215:R,x>3386:A,x<3107:R,R}
testSorter := day19.ReadSorterLine("kt{m>2215:R,x>3386:A,x<3107:R,R}")
log.Printf("my test sorter is %+v", testSorter)
simplified := day19.SimplifyOperation(testSorter)
log.Printf("> simplivied %+v", simplified)
detail := day19.ReadDetailLine("{x=787,m=2655,a=1222,s=2876}")
log.Printf("> detail %+v", detail)
result := day19.Run()
log.Printf("\n\nday19 result: %d\n****\n", result)
} }