220 lines
4.7 KiB
Go
220 lines
4.7 KiB
Go
package main
|
|
|
|
func NewAi() *Ai {
|
|
ai := Ai{}
|
|
queue := make([]rune, 1)
|
|
queue[0] = 'x'
|
|
ai.queue = &queue
|
|
return &ai
|
|
}
|
|
|
|
func (ai *Ai) ProcessQueue() {
|
|
if ai.newQueue != nil {
|
|
ai.queue = ai.newQueue
|
|
ai.newQueue = nil
|
|
ai.index = 0
|
|
}
|
|
queue := *ai.queue
|
|
// wasd + qe keyboard keys
|
|
switch queue[ai.index] {
|
|
case 'w':
|
|
board.MinoDrop()
|
|
case 'a':
|
|
board.MinoMoveLeft()
|
|
case 'd':
|
|
board.MinoMoveRight()
|
|
case 'q':
|
|
board.MinoRotateLeft()
|
|
case 'e':
|
|
board.MinoRotateRight()
|
|
case 'x':
|
|
return
|
|
}
|
|
ai.index++
|
|
view.RefreshScreen()
|
|
}
|
|
|
|
func (ai *Ai) GetBestQueue() {
|
|
bestScore := -9999999
|
|
bestQueue := make([]rune, 0, 0)
|
|
currentMino := *board.currentMino
|
|
|
|
for slide1 := 0; slide1 < 5; slide1++ {
|
|
for move1 := board.width; move1 >= 0; move1-- {
|
|
for rotate1 := 0; rotate1 < 5; rotate1++ {
|
|
|
|
queue, mino1 := board.getMovesforMino(rotate1, move1, slide1, ¤tMino, nil)
|
|
if mino1 == nil {
|
|
continue
|
|
}
|
|
|
|
for slide2 := 0; slide2 < 5; slide2++ {
|
|
for move2 := board.width; move2 >= 0; move2-- {
|
|
for rotate2 := 0; rotate2 < 5; rotate2++ {
|
|
|
|
_, mino2 := board.getMovesforMino(rotate2, move2, slide2, board.previewMino, mino1)
|
|
if mino2 == nil {
|
|
continue
|
|
}
|
|
|
|
fullLines, holes, bumpy := board.boardStatsWithMinos(mino1, mino2)
|
|
score := ai.getScoreFromBoardStats(fullLines, holes, bumpy)
|
|
|
|
if score > bestScore {
|
|
bestScore = score
|
|
bestQueue = queue
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(bestQueue) < 1 {
|
|
bestQueue = append(bestQueue, 'x')
|
|
}
|
|
|
|
ai.newQueue = &bestQueue
|
|
}
|
|
|
|
func (board *Board) getMovesforMino(rotate int, move int, slide int, mino1 *Mino, mino2 *Mino) ([]rune, *Mino) {
|
|
queue := make([]rune, 0, 4)
|
|
mino := *mino1
|
|
|
|
if rotate%2 == 0 {
|
|
rotate /= 2
|
|
for i := 0; i < rotate; i++ {
|
|
mino.RotateRight()
|
|
queue = append(queue, 'e')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
} else {
|
|
rotate = rotate/2 + 1
|
|
for i := 0; i < rotate; i++ {
|
|
mino.RotateLeft()
|
|
queue = append(queue, 'q')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
if move%2 == 0 {
|
|
move /= 2
|
|
for i := 0; i < move; i++ {
|
|
mino.MoveLeft()
|
|
queue = append(queue, 'a')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
} else {
|
|
move = move/2 + 1
|
|
for i := 0; i < move; i++ {
|
|
mino.MoveRight()
|
|
queue = append(queue, 'd')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
}
|
|
for mino.ValidLocation(false) && (mino2 == nil || !mino2.minoOverlap(&mino)) {
|
|
mino.MoveDown()
|
|
}
|
|
mino.MoveUp()
|
|
queue = append(queue, 'w')
|
|
|
|
if slide%2 == 0 {
|
|
slide /= 2
|
|
for i := 0; i < slide; i++ {
|
|
mino.MoveLeft()
|
|
queue = append(queue, 'a')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
} else {
|
|
slide = slide/2 + 1
|
|
for i := 0; i < slide; i++ {
|
|
mino.MoveRight()
|
|
queue = append(queue, 'd')
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
}
|
|
}
|
|
|
|
if !mino.ValidLocation(true) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'x')
|
|
return queue, &mino
|
|
}
|
|
|
|
func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int, holes int, bumpy int) {
|
|
// fullLines
|
|
fullLinesY := make(map[int]bool, 2)
|
|
for j := 0; j < board.height; j++ {
|
|
isFullLine := true
|
|
for i := 0; i < board.width; i++ {
|
|
if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) {
|
|
isFullLine = false
|
|
break
|
|
}
|
|
}
|
|
if isFullLine {
|
|
fullLinesY[j] = true
|
|
fullLines++
|
|
}
|
|
}
|
|
|
|
// holes and bumpy
|
|
indexLast := 0
|
|
for i := 0; i < board.width; i++ {
|
|
index := board.height
|
|
indexOffset := 0
|
|
for j := 0; j < board.height; j++ {
|
|
if _, found := fullLinesY[j]; found {
|
|
indexOffset++
|
|
} else {
|
|
if board.colors[i][j] != blankColor || mino1.isMinoAtLocation(i, j) || mino2.isMinoAtLocation(i, j) {
|
|
index = j
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if i != 0 {
|
|
diffrence := (index + fullLines - indexOffset) - indexLast
|
|
if diffrence < 0 {
|
|
diffrence = -diffrence
|
|
}
|
|
bumpy += diffrence
|
|
|
|
}
|
|
indexLast = index + fullLines - indexOffset
|
|
|
|
index++
|
|
for j := index; j < board.height; j++ {
|
|
if board.colors[i][j] == blankColor && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) {
|
|
holes++
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (ai *Ai) getScoreFromBoardStats(fullLines int, holes int, bumpy int) (score int) {
|
|
if fullLines == 4 {
|
|
score += 512
|
|
}
|
|
score -= 75 * holes
|
|
score -= 25 * bumpy
|
|
return score
|
|
}
|