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/
/.go
example
/day19/input

View File

@ -7,12 +7,13 @@ import (
"slices"
"strconv"
"strings"
"sync"
)
func Run() int {
log.Println("hello day 18")
log.Println("problem of lagoon bgins")
filename := "day18/example"
filename := "day18/input"
instructions := ReadInstructionas2(filename)
h, w := calcHeightWidth(instructions)
log.Printf("read %+v instructions", instructions)
@ -210,34 +211,37 @@ type Cell struct {
}
type BorderSymbol rune
// '' always left to right
const (Vertical BorderSymbol = '|'
ToDown BorderSymbol = '7'
ToUp BorderSymbol = 'J'
FromUp BorderSymbol = 'F'
// ” always left to right
const (
Vertical BorderSymbol = '|'
ToDown BorderSymbol = '7'
ToUp BorderSymbol = 'J'
FromUp BorderSymbol = 'F'
FromDown BorderSymbol = 'L'
)
type Field struct {
Height, Width int
// Cells [][]*Cell
Cells map[Coord]*Cell
Cells map[Coord]*Cell
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)
if c.Row - 3 < f.MinRow {
if c.Row-3 < f.MinRow {
f.MinRow = c.Row - 3
}
if c.Row + 3 > f.MaxRow {
if c.Row+3 > f.MaxRow {
f.MaxRow = c.Row + 3
}
if c.Col - 3 < f.MinCol {
if c.Col-3 < f.MinCol {
f.MinCol = c.Col - 3
}
if c.Col + 3 > f.MaxCol {
if c.Col+3 > f.MaxCol {
f.MaxCol = c.Col + 3
}
}
@ -245,7 +249,7 @@ func (f *Field)confirmCoord(c Coord) {
func CreateField(height, width int) Field {
return Field{
Height: height, Width: width,
Cells: make(map[Coord]*Cell),
Cells: make(map[Coord]*Cell),
BordersFromLeft: make(map[int]map[int]BorderSymbol),
}
}
@ -307,7 +311,7 @@ func (f *Field) digByInstructions(instructions []Instruction) (borderAmount int)
return
}
func getTurnAsIfGoingFromLeft(directionFrom, directionTo Direction) []BorderSymbol {
func getTurnAsIfGoingFromLeft(directionFrom, directionTo Direction) []BorderSymbol {
// log.Printf("getTurnAsIfGoingFromLeft from %s to %s", directionFrom.String(), directionTo.String())
var symbol BorderSymbol
@ -337,7 +341,6 @@ func getTurnAsIfGoingFromLeft(directionFrom, directionTo Direction) []BorderSym
symbol = FromUp
}
// panic(fmt.Sprint("got strange from %s to %s", directionFrom.String(), directionTo.String()))
return []BorderSymbol{symbol}
}
@ -346,27 +349,27 @@ func (f *Field) String() string {
s := "text 15,15 \""
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++ {
rowBords := f.BordersFromLeft[row]
if rowBords != nil {
bord, exists := rowBords[col]
if exists {
rowChars[col - f.MinCol] = rune(bord)
rowChars[col-f.MinCol] = rune(bord)
continue
}
}
cell := f.Cells[Coord{col, row}]
if cell != nil && cell.ToBeDug {
rowChars[col - f.MinCol] = '@'
rowChars[col-f.MinCol] = '@'
continue
}
if f.isCellDug(row, col) {
rowChars[col - f.MinCol] = '#'
rowChars[col-f.MinCol] = '#'
} else {
rowChars[col - f.MinCol] = '.'
rowChars[col-f.MinCol] = '.'
}
}
@ -376,72 +379,101 @@ func (f *Field) String() string {
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++ {
if row % 10000 == 0 {
log.Printf("processed rows %d out of %d", row, f.MaxRow)
}
specialBorders := f.BordersFromLeft[row]
if len(specialBorders) == 0 {
continue
}
type BorderItem struct {
border BorderSymbol
col int
}
rowBorders := make([]BorderItem, 0)
for col, borderSymbol := range specialBorders {
rowBorders = append(rowBorders, BorderItem{borderSymbol, col})
}
slices.SortFunc(rowBorders, func(a BorderItem, b BorderItem) int {
return a.col - b.col
})
go func(row int){
if row%10000 == 0 {
log.Printf("processed rows %d out of %d", row, f.MaxRow)
}
specialBorders := f.BordersFromLeft[row]
if len(specialBorders) == 0 {
wg.Done()
return
}
type BorderItem struct {
border BorderSymbol
col int
}
rowBorders := make([]BorderItem, 0)
for col, borderSymbol := range specialBorders {
rowBorders = append(rowBorders, BorderItem{borderSymbol, col})
}
slices.SortFunc(rowBorders, func(a BorderItem, b BorderItem) int {
return a.col - b.col
})
// log.Printf(">>>>>>> for row %d sorted %+v", row, rowBorders)
prevBorder := rowBorders[0]
bordersCrossed := 0
if prevBorder.border == Vertical {
bordersCrossed += 1
}
for _, specialBorder := range rowBorders[1:] {
diff := specialBorder.col - prevBorder.col - 1
if specialBorder.border == ToUp && prevBorder.border == FromUp {
// log.Printf(">>>>>>> for row %d sorted %+v", row, rowBorders)
prevBorder := rowBorders[0]
bordersCrossed := 0
if prevBorder.border == Vertical {
bordersCrossed += 1
prevBorder = specialBorder
continue
}
if specialBorder.border == ToDown && prevBorder.border == FromDown {
bordersCrossed += 1
prevBorder = specialBorder
continue
}
if specialBorder.border == ToUp && prevBorder.border == FromDown {
prevBorder = specialBorder
continue
}
if specialBorder.border == ToDown && prevBorder.border == FromUp {
prevBorder = specialBorder
continue
}
for _, specialBorder := range rowBorders[1:] {
diff := specialBorder.col - prevBorder.col - 1
if bordersCrossed % 2 == 1 { // is in
for col := prevBorder.col+1; col < specialBorder.col; col++ {
// f.Cells[Coord{Col: col, Row: row}] = &Cell{
// ToBeDug: true,
// }
if specialBorder.border == ToUp && prevBorder.border == FromUp {
bordersCrossed += 1
prevBorder = specialBorder
continue
}
countInside += diff
if specialBorder.border == ToDown && prevBorder.border == FromDown {
bordersCrossed += 1
prevBorder = specialBorder
continue
}
if specialBorder.border == ToUp && prevBorder.border == FromDown {
prevBorder = specialBorder
continue
}
if specialBorder.border == ToDown && prevBorder.border == FromUp {
prevBorder = specialBorder
continue
}
if bordersCrossed%2 == 1 { // is in
for col := prevBorder.col + 1; col < specialBorder.col; col++ {
// f.Cells[Coord{Col: col, Row: row}] = &Cell{
// ToBeDug: true,
// }
}
lineSum <- diff
// countInside += diff
}
if specialBorder.border == Vertical {
bordersCrossed += 1
}
prevBorder = specialBorder
}
if specialBorder.border == Vertical {
bordersCrossed += 1
}
prevBorder = specialBorder
}
wg.Done()
}(row)
}
return
<-done
return result
}
// func (f *Field) digInsides() (countInside int) {

View File

@ -34,3 +34,18 @@ day18 result: 952408144115
952408144115
*** 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 (
"log"
"sunshine.industries/aoc2023/day18"
"sunshine.industries/aoc2023/day19"
)
func main() {
log.Print("> starting run:")
result := day18.Run()
log.Printf("\n\nday18 result: %d\n****\n", result)
// 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)
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)
}