297 lines
7.7 KiB
Go
297 lines
7.7 KiB
Go
package common
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
)
|
|
|
|
|
|
type Rock byte
|
|
|
|
const (
|
|
Minus Rock = iota
|
|
Plus
|
|
Angle
|
|
Line
|
|
Square
|
|
)
|
|
|
|
func GetRock(rockType int) Rock {
|
|
switch rockType {
|
|
case 0:
|
|
return Minus
|
|
case 1:
|
|
return Plus
|
|
case 2:
|
|
return Angle
|
|
case 3:
|
|
return Line
|
|
case 4:
|
|
return Square
|
|
}
|
|
return Minus
|
|
}
|
|
|
|
|
|
type Tile byte
|
|
|
|
const (
|
|
Empty Tile = iota
|
|
MovingRock
|
|
StoppedRock
|
|
)
|
|
|
|
|
|
type PreviousAction struct {
|
|
endJet int
|
|
heightDifference int
|
|
}
|
|
|
|
|
|
type Chamber struct {
|
|
tiles [][]Tile
|
|
highestPoint int
|
|
offset int
|
|
nRocks int
|
|
jets []Jet
|
|
currentJet int
|
|
cache map[Rock]map[int]PreviousAction
|
|
}
|
|
|
|
func NewChamber(jets []Jet) *Chamber {
|
|
return &Chamber{[][]Tile{}, -1, 0, 0, jets, 0, make(map[Rock]map[int]PreviousAction)}
|
|
}
|
|
|
|
func (chamber Chamber) Clone() *Chamber {
|
|
return &Chamber{[][]Tile{}, -1, 0, 0, chamber.jets, 0, make(map[Rock]map[int]PreviousAction)}
|
|
}
|
|
|
|
func (chamber *Chamber) DropRock() {
|
|
for i := len(chamber.tiles); i <= chamber.highestPoint + 7 - chamber.offset; i++ {
|
|
chamber.tiles = append(chamber.tiles, []Tile{Empty, Empty, Empty, Empty, Empty, Empty, Empty})
|
|
}
|
|
|
|
newRock := GetRock(chamber.nRocks % 5)
|
|
chamber.nRocks++
|
|
startJet := chamber.currentJet
|
|
currHighestPoint := chamber.highestPoint - chamber.offset
|
|
|
|
switch newRock {
|
|
case Minus:
|
|
chamber.tiles[len(chamber.tiles) - 4][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][3] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][4] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][5] = MovingRock
|
|
case Plus:
|
|
chamber.tiles[len(chamber.tiles) - 4][3] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][3] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][4] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 2][3] = MovingRock
|
|
case Angle:
|
|
chamber.tiles[len(chamber.tiles) - 4][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][3] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][4] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][4] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 2][4] = MovingRock
|
|
case Line:
|
|
chamber.tiles[len(chamber.tiles) - 4][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 2][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 1][2] = MovingRock
|
|
case Square:
|
|
chamber.tiles[len(chamber.tiles) - 4][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 4][3] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][2] = MovingRock
|
|
chamber.tiles[len(chamber.tiles) - 3][3] = MovingRock
|
|
}
|
|
|
|
moving := true
|
|
for moving {
|
|
canShift := true
|
|
jet := chamber.jets[chamber.currentJet]
|
|
chamber.currentJet = (chamber.currentJet + 1) % len(chamber.jets)
|
|
|
|
rowsChecked := 0
|
|
var lowestPoint int
|
|
for i := len(chamber.tiles) - 1; i >= 0 && rowsChecked != 0b01111111; i-- {
|
|
for j := 0; j < len(chamber.tiles[i]); j++ {
|
|
if chamber.tiles[i][j] == MovingRock {
|
|
if jet == LeftToRight {
|
|
if j == 6 || chamber.tiles[i][j+1] == StoppedRock {
|
|
canShift = false
|
|
}
|
|
} else if jet == RightToLeft {
|
|
if j == 0 || chamber.tiles[i][j-1] == StoppedRock {
|
|
canShift = false
|
|
}
|
|
}
|
|
} else if chamber.tiles[i][j] == StoppedRock {
|
|
rowsChecked |= 0b01 << j
|
|
}
|
|
}
|
|
if rowsChecked == 0b01111111 {
|
|
lowestPoint = i
|
|
}
|
|
}
|
|
|
|
for i := lowestPoint; i < len(chamber.tiles); i++ {
|
|
if jet == LeftToRight {
|
|
for j := len(chamber.tiles[i]) - 1; j >= 0; j-- {
|
|
if chamber.tiles[i][j] == MovingRock {
|
|
if canShift {
|
|
chamber.tiles[i][j] = Empty
|
|
chamber.tiles[i][j+1] = MovingRock
|
|
if i == 0 || chamber.tiles[i-1][j+1] == StoppedRock {
|
|
moving = false
|
|
}
|
|
} else {
|
|
if i == 0 || chamber.tiles[i-1][j] == StoppedRock {
|
|
moving = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if jet == RightToLeft {
|
|
for j := 0; j < len(chamber.tiles[i]); j++ {
|
|
if chamber.tiles[i][j] == MovingRock {
|
|
if canShift {
|
|
chamber.tiles[i][j] = Empty
|
|
chamber.tiles[i][j-1] = MovingRock
|
|
if i == 0 || chamber.tiles[i-1][j-1] == StoppedRock {
|
|
moving = false
|
|
}
|
|
} else {
|
|
if i == 0 || chamber.tiles[i-1][j] == StoppedRock {
|
|
moving = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := lowestPoint; i < len(chamber.tiles); i++ {
|
|
for j := 0; j < len(chamber.tiles[i]); j++ {
|
|
if chamber.tiles[i][j] == MovingRock {
|
|
if moving {
|
|
chamber.tiles[i][j] = Empty
|
|
chamber.tiles[i-1][j] = MovingRock
|
|
} else {
|
|
chamber.tiles[i][j] = StoppedRock
|
|
if i > currHighestPoint {
|
|
currHighestPoint = i
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if chamber.nRocks > len(chamber.jets) {
|
|
if _, ok := chamber.cache[newRock]; !ok {
|
|
chamber.cache[newRock] = make(map[int]PreviousAction)
|
|
}
|
|
chamber.cache[newRock][startJet] = PreviousAction{chamber.currentJet, currHighestPoint - chamber.highestPoint}
|
|
}
|
|
chamber.highestPoint = currHighestPoint + chamber.offset
|
|
}
|
|
|
|
func (chamber *Chamber) DropNRocks(nRocks int) {
|
|
var loopNewRock Rock
|
|
loopCurrentJet := -1
|
|
loopEndJet := -1
|
|
loopNRocks := 0
|
|
loopHeightDifference := 0
|
|
loopFound := false
|
|
|
|
var i int
|
|
for i = 0; i < nRocks && !loopFound; i++ {
|
|
newRock := GetRock(chamber.nRocks % 5)
|
|
found := false
|
|
if jets, ok := chamber.cache[newRock]; ok {
|
|
if result, ok := jets[chamber.currentJet]; ok {
|
|
if loopCurrentJet < 0 {
|
|
loopCurrentJet = chamber.currentJet
|
|
loopNewRock = newRock
|
|
}
|
|
if loopNRocks > 0 && loopNewRock == newRock && loopCurrentJet == chamber.currentJet {
|
|
loopFound = true
|
|
} else {
|
|
loopNRocks++
|
|
loopHeightDifference += result.heightDifference
|
|
loopEndJet = result.endJet
|
|
chamber.nRocks++
|
|
chamber.offset += result.heightDifference
|
|
chamber.highestPoint += result.heightDifference
|
|
chamber.currentJet = result.endJet
|
|
}
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
chamber.DropRock()
|
|
}
|
|
}
|
|
|
|
if loopFound {
|
|
quotientRocks := int((nRocks - i) / loopNRocks)
|
|
chamber.nRocks += quotientRocks * loopNRocks
|
|
chamber.highestPoint += quotientRocks * loopHeightDifference
|
|
chamber.offset += quotientRocks * loopHeightDifference
|
|
chamber.currentJet = loopEndJet
|
|
i += quotientRocks * loopNRocks
|
|
|
|
for ; i <= nRocks; i++ {
|
|
chamber.DropRock()
|
|
}
|
|
}
|
|
}
|
|
|
|
func (chamber *Chamber) GetHighestPoint() int {
|
|
return chamber.highestPoint + 1
|
|
}
|
|
|
|
func (chamber *Chamber) Print() {
|
|
for i := len(chamber.tiles) - 1; i >= 0; i-- {
|
|
fmt.Print("|")
|
|
for j := 0; j < len(chamber.tiles[i]); j++ {
|
|
switch chamber.tiles[i][j] {
|
|
case Empty:
|
|
fmt.Print(".")
|
|
case MovingRock:
|
|
fmt.Print("@")
|
|
case StoppedRock:
|
|
fmt.Print("#")
|
|
}
|
|
}
|
|
fmt.Println("|")
|
|
}
|
|
fmt.Println("+-------+")
|
|
}
|
|
|
|
|
|
type Jet byte
|
|
|
|
const (
|
|
LeftToRight Jet = iota
|
|
RightToLeft
|
|
)
|
|
|
|
|
|
func Parse(scanner bufio.Scanner) Chamber {
|
|
jets := []Jet{}
|
|
|
|
scanner.Scan()
|
|
line := scanner.Text()
|
|
for _, c := range(line) {
|
|
if c == '>' {
|
|
jets = append(jets, LeftToRight)
|
|
} else if c == '<' {
|
|
jets = append(jets, RightToLeft)
|
|
}
|
|
}
|
|
|
|
return *NewChamber(jets)
|
|
}
|