package days import ( "fmt" "strings" u "parnic.com/aoc2019/utilities" ) type camViewCellType int type botFacing int const ( cellTypeScaffold camViewCellType = iota cellTypeOpen cellTypeInvalid ) const ( botFacingUp botFacing = iota botFacingLeft botFacingDown botFacingRight botFacingFirst = botFacingUp botFacingLast = botFacingRight ) const ( dirLeft dirType = 1 dirRight dirType = -1 ) var ( day17AdjacentOffsets = []u.Vec2i{ {X: -1, Y: 0}, {X: 1, Y: 0}, {X: 0, Y: -1}, {X: 0, Y: 1}, } ) type Day17 struct { program u.IntcodeProgram grid [][]camViewCellType botLocation u.Vec2i botFacingDir botFacing endLocation u.Vec2i } func (d *Day17) Parse() { d.program = u.LoadIntcodeProgram("17p") d.grid = [][]camViewCellType{{}} } func (d Day17) Num() int { return 17 } func (d Day17) Draw() { for y := range d.grid { for x := range d.grid[y] { switch d.grid[y][x] { case cellTypeOpen: fmt.Print(" ") case cellTypeScaffold: char := "█" color := u.ColorBlack if d.botLocation.X == x && d.botLocation.Y == y { switch d.botFacingDir { case botFacingUp: char = "^" case botFacingLeft: char = "<" case botFacingDown: char = "v" case botFacingRight: char = ">" } } else if d.endLocation.X == x && d.endLocation.Y == y { char = "@" } else { color = u.ColorWhite } fmt.Printf("%s%s%s%s", u.BackgroundWhite, color, char, u.TextReset) } } fmt.Println() } } func (d Day17) getAdjacentScaffolds(y, x int) []u.Vec2i { retval := make([]u.Vec2i, 0) for _, offset := range day17AdjacentOffsets { offY := y + offset.Y offX := x + offset.X if offY < 0 || offY >= len(d.grid) || offX < 0 || offX >= len(d.grid[0]) { continue } if d.grid[offY][offX] == cellTypeScaffold { retval = append(retval, u.Vec2i{X: offX, Y: offY}) } } return retval } func (d Day17) getNumSurroundingScaffolds(y, x int) int { return len(d.getAdjacentScaffolds(y, x)) } func (d Day17) forEachCellOfType(t camViewCellType, f func(y, x int)) { for y := range d.grid { for x := range d.grid[y] { if d.grid[y][x] == t { f(y, x) } } } } func (d Day17) getNewFacingDir(currentDir botFacing, turnDir dirType) botFacing { currentDir += botFacing(turnDir) if currentDir < botFacingFirst { currentDir = botFacingLast } else if currentDir > botFacingLast { currentDir = botFacingFirst } return currentDir } func (d Day17) getCellTypeInDirection(y, x int, facingDir botFacing) (camViewCellType, int, int) { newX := x newY := y switch facingDir { case botFacingUp: newY-- case botFacingLeft: newX-- case botFacingDown: newY++ case botFacingRight: newX++ } if newY < 0 || newY >= len(d.grid) || newX < 0 || newX >= len(d.grid[0]) { return cellTypeInvalid, newY, newX } return d.grid[newY][newX], newY, newX } func (d *Day17) Part1() string { y := 0 d.program.RunIn(func(inputStep int) int64 { return 0 }, func(val int64, state u.IntcodeProgramState) { rVal := rune(val) switch rVal { case '\n': y++ d.grid = append(d.grid, make([]camViewCellType, 0)) case '#': d.grid[y] = append(d.grid[y], cellTypeScaffold) case '.': d.grid[y] = append(d.grid[y], cellTypeOpen) case '^', '<', 'v', '>': d.botLocation = u.Vec2i{X: len(d.grid[y]), Y: y} d.grid[y] = append(d.grid[y], cellTypeScaffold) switch rVal { case '^': d.botFacingDir = botFacingUp case '<': d.botFacingDir = botFacingLeft case 'v': d.botFacingDir = botFacingDown case '>': d.botFacingDir = botFacingRight } } }) for y := len(d.grid) - 1; y >= 0; y-- { if len(d.grid[y]) == 0 { d.grid = d.grid[0 : len(d.grid)-1] } } alignmentParameterTotal := 0 d.forEachCellOfType(cellTypeScaffold, func(y, x int) { if numSurrounding := d.getNumSurroundingScaffolds(y, x); numSurrounding == 4 { alignmentParameterTotal += y * x } else if numSurrounding == 1 { if d.botLocation.X != x || d.botLocation.Y != y { d.endLocation = u.Vec2i{X: x, Y: y} } } }) // d.Draw() return fmt.Sprintf("Alignment parameter sum: %s%d%s", u.TextBold, alignmentParameterTotal, u.TextReset) } func (d *Day17) Part2() string { instructions := make([]string, 0) pos := d.botLocation botFacingDir := d.botFacingDir for { if pos == d.endLocation { fmt.Println() break } adj := d.getAdjacentScaffolds(pos.Y, pos.X) turnDirection := dirType(0) if botFacingDir == botFacingUp || botFacingDir == botFacingDown { if u.ArrayContains(adj, u.Vec2i{X: pos.X - 1, Y: pos.Y}) { if botFacingDir == botFacingUp { turnDirection = dirLeft } else if botFacingDir == botFacingDown { turnDirection = dirRight } } else if u.ArrayContains(adj, u.Vec2i{X: pos.X + 1, Y: pos.Y}) { if botFacingDir == botFacingUp { turnDirection = dirRight } else if botFacingDir == botFacingDown { turnDirection = dirLeft } } } else { if u.ArrayContains(adj, u.Vec2i{X: pos.X, Y: pos.Y - 1}) { if botFacingDir == botFacingLeft { turnDirection = dirRight } else if botFacingDir == botFacingRight { turnDirection = dirLeft } } else if u.ArrayContains(adj, u.Vec2i{X: pos.X, Y: pos.Y + 1}) { if botFacingDir == botFacingLeft { turnDirection = dirLeft } else if botFacingDir == botFacingRight { turnDirection = dirRight } } } if turnDirection == 0 { panic("at an invalid location somehow") } dirAscii := "L" if turnDirection == dirRight { dirAscii = "R" } instructions = append(instructions, dirAscii) botFacingDir = d.getNewFacingDir(botFacingDir, turnDirection) numMoved := 0 for { cell, newY, newX := d.getCellTypeInDirection(pos.Y, pos.X, botFacingDir) if cell != cellTypeScaffold { break } pos.X = newX pos.Y = newY numMoved++ } instructions = append(instructions, fmt.Sprintf("%d", numMoved)) } return fmt.Sprintf("%s%s%s", u.TextBold, strings.Join(instructions, ","), u.TextReset) }