Day 13 solution

This was incredibly cool and I had a really fun time with it. Uncomment the draw and sleep in Part2 to see the game play itself! Note that I'm not seeking around in the terminal window to make the drawing smooth, I'm just outputting each new frame as it happens, so there's some jitter, but it still looks great!

I messed around a bit with control codes to move the cursor around instead of the "draw the buffer over and over again" approach, and they work, mostly, but I'm sticking with this for now.
This commit is contained in:
2022-06-27 00:01:19 -05:00
parent 2178a2618d
commit dd5ea5ea86
4 changed files with 155 additions and 0 deletions

148
days/13.go Normal file
View File

@ -0,0 +1,148 @@
package days
import (
"fmt"
"strings"
u "parnic.com/aoc2019/utilities"
)
const (
tileEmpty = 0
tileWall = 1
tileBlock = 2
tileHPaddle = 3
tileBall = 4
)
type tile struct {
pos u.Vec2[int]
id int
}
type Day13 struct {
program u.IntcodeProgram
tiles []tile
gameBoard [24][45]int
}
func (d *Day13) Parse() {
d.program = u.LoadIntcodeProgram("13p")
d.tiles = make([]tile, 0, 1080)
}
func (d Day13) Num() int {
return 13
}
func (d Day13) getNumBlocks() int {
blockTiles := 0
for _, tile := range d.tiles {
if tile.id == tileBlock {
blockTiles++
}
}
return blockTiles
}
func (d Day13) Draw() {
s := strings.Builder{}
for x := range d.gameBoard {
for y := range d.gameBoard[x] {
block := d.gameBoard[x][y]
if block == tileBlock {
s.WriteString(u.ColorBlue)
s.WriteRune('█')
s.WriteString(u.TextReset)
} else if block == tileBall {
s.WriteString(u.ColorGreen)
s.WriteRune('█')
s.WriteString(u.TextReset)
} else if block == tileWall {
s.WriteString(u.ColorWhite)
s.WriteRune('█')
s.WriteString(u.TextReset)
} else if block == tileHPaddle {
s.WriteString(u.ColorRed)
s.WriteRune('█')
s.WriteString(u.TextReset)
} else if block == tileEmpty {
s.WriteRune(' ')
}
}
s.WriteRune('\n')
}
fmt.Print(s.String())
}
func (d *Day13) Part1() string {
outputStep := 0
var newTilePos u.Vec2[int]
d.program.RunIn(func(inputStep int) int64 {
return 0
}, func(val int64, state u.IntcodeProgramState) {
if outputStep == 0 {
newTilePos.X = int(val)
outputStep++
} else if outputStep == 1 {
newTilePos.Y = int(val)
outputStep++
} else {
d.tiles = append(d.tiles, tile{
pos: newTilePos,
id: int(val),
})
outputStep = 0
}
})
return fmt.Sprintf("%d total tiles, # block tiles: %s%d%s", len(d.tiles), u.TextBold, d.getNumBlocks(), u.TextReset)
}
func (d *Day13) Part2() string {
d.program.Reset()
d.program.SetMemory(0, 2)
outputStep := 0
newTilePos := u.Vec2[int]{}
var ball u.Vec2[int]
var paddle u.Vec2[int]
var score int64
d.program.RunIn(func(inputStep int) int64 {
if ball.X < paddle.X {
return -1
} else if ball.X > paddle.X {
return 1
}
return 0
}, func(val int64, state u.IntcodeProgramState) {
if outputStep == 0 {
newTilePos.X = int(val)
outputStep++
} else if outputStep == 1 {
newTilePos.Y = int(val)
outputStep++
} else {
if newTilePos.Equals(u.Vec2[int]{X: -1, Y: 0}) {
score = val
} else {
d.gameBoard[newTilePos.Y][newTilePos.X] = int(val)
if val == tileBall {
ball = newTilePos
} else if val == tileHPaddle {
paddle = newTilePos
// d.Draw()
// time.Sleep(time.Millisecond * 33)
}
}
outputStep = 0
}
})
return fmt.Sprintf("Game over! Score: %s%d%s", u.TextBold, score, u.TextReset)
}

1
inputs/13p.txt Normal file

File diff suppressed because one or more lines are too long

View File

@ -43,6 +43,7 @@ var dayMap = []day{
&days.Day10{}, &days.Day10{},
&days.Day11{}, &days.Day11{},
&days.Day12{}, &days.Day12{},
&days.Day13{},
} }
func main() { func main() {

View File

@ -37,6 +37,11 @@ func (v Vec2[T]) AngleBetween(other Vec2[T]) float64 {
return rad * 180 / math.Pi return rad * 180 / math.Pi
} }
func (v Vec2[T]) Equals(other Vec2[T]) bool {
return v.X == other.X &&
v.Y == other.Y
}
func VecBetween[T Number](a, b Vec2[T]) Vec2[T] { func VecBetween[T Number](a, b Vec2[T]) Vec2[T] {
return Vec2[T]{ return Vec2[T]{
X: a.X - b.X, X: a.X - b.X,