254 lines
5.5 KiB
Go
254 lines
5.5 KiB
Go
package main
|
|
|
|
// NewAi creates a new AI
|
|
func NewAi() *Ai {
|
|
ai := Ai{}
|
|
queue := make([]rune, 1)
|
|
queue[0] = 'x'
|
|
ai.queue = &queue
|
|
return &ai
|
|
}
|
|
|
|
// ProcessQueue checks AI queue and process key moments
|
|
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()
|
|
}
|
|
|
|
// GetBestQueue gets the best queue
|
|
func (ai *Ai) GetBestQueue() {
|
|
bestScore := -9999999
|
|
bestQueue := []rune{'x'}
|
|
currentMino := board.currentMino
|
|
previewMino := board.previewMino
|
|
rotations1 := 5
|
|
rotations2 := 5
|
|
slides := 5
|
|
if board.width > 10 {
|
|
slides = 3
|
|
}
|
|
|
|
switch currentMino.minoRotation[0][1][1] {
|
|
case colorCyan, colorGreen, colorRed:
|
|
rotations1 = 2
|
|
case colorYellow:
|
|
rotations1 = 1
|
|
}
|
|
switch previewMino.minoRotation[0][1][1] {
|
|
case colorCyan, colorGreen, colorRed:
|
|
rotations2 = 2
|
|
case colorYellow:
|
|
rotations2 = 1
|
|
}
|
|
|
|
for slide1 := 0; slide1 < slides; slide1++ {
|
|
for move1 := board.width; move1 >= 0; move1-- {
|
|
for rotate1 := 0; rotate1 < rotations1; rotate1++ {
|
|
|
|
queue, mino1 := board.getMovesforMino(rotate1, move1, slide1, currentMino, nil)
|
|
if mino1 == nil {
|
|
continue
|
|
}
|
|
|
|
for slide2 := 0; slide2 < slides; slide2++ {
|
|
for move2 := board.width; move2 >= 0; move2-- {
|
|
for rotate2 := 0; rotate2 < rotations2; rotate2++ {
|
|
|
|
_, mino2 := board.getMovesforMino(rotate2, move2, slide2, previewMino, mino1)
|
|
if mino2 == nil {
|
|
continue
|
|
}
|
|
|
|
fullLines, holes, bumpy, heightEnds := board.boardStatsWithMinos(mino1, mino2)
|
|
score := ai.getScoreFromBoardStats(fullLines, holes, bumpy, heightEnds, mino1.y, mino2.y)
|
|
|
|
if score > bestScore {
|
|
bestScore = score
|
|
bestQueue = queue
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
ai.newQueue = &bestQueue
|
|
}
|
|
|
|
func (board *Board) getMovesforMino(rotate int, move int, slide int, mino1 *Mino, mino2 *Mino) ([]rune, *Mino) {
|
|
var i int
|
|
queue := make([]rune, 0, (rotate/2+1)+(move/2+1)+(slide/2+1)+1)
|
|
mino := *mino1
|
|
|
|
mino.MoveDown()
|
|
|
|
if rotate%2 == 0 {
|
|
rotate /= 2
|
|
for i = 0; i < rotate; i++ {
|
|
mino.RotateRight()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'e')
|
|
}
|
|
} else {
|
|
rotate = rotate/2 + 1
|
|
for i = 0; i < rotate; i++ {
|
|
mino.RotateLeft()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'q')
|
|
}
|
|
}
|
|
|
|
if move%2 == 0 {
|
|
move /= 2
|
|
for i = 0; i < move; i++ {
|
|
mino.MoveRight()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'd')
|
|
}
|
|
} else {
|
|
move = move/2 + 1
|
|
for i = 0; i < move; i++ {
|
|
mino.MoveLeft()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'a')
|
|
}
|
|
}
|
|
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()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'a')
|
|
}
|
|
} else {
|
|
slide = slide/2 + 1
|
|
for i = 0; i < slide; i++ {
|
|
mino.MoveRight()
|
|
if !mino.ValidLocation(false) || (mino2 != nil && mino2.minoOverlap(&mino)) {
|
|
return queue, nil
|
|
}
|
|
queue = append(queue, 'd')
|
|
}
|
|
}
|
|
|
|
if !mino.ValidLocation(true) {
|
|
return queue, nil
|
|
}
|
|
|
|
return append(queue, 'x'), &mino
|
|
}
|
|
|
|
func (board *Board) boardStatsWithMinos(mino1 *Mino, mino2 *Mino) (fullLines int, holes int, bumpy int, heightEnds int) {
|
|
var i int
|
|
var j int
|
|
|
|
// fullLines
|
|
for j = 0; j < board.height; j++ {
|
|
board.fullLinesY[j] = true
|
|
for i = 0; i < board.width; i++ {
|
|
if board.colors[i][j] == colorBlank && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) {
|
|
board.fullLinesY[j] = false
|
|
break
|
|
}
|
|
}
|
|
if board.fullLinesY[j] {
|
|
fullLines++
|
|
}
|
|
}
|
|
|
|
// holes and bumpy
|
|
var foundLast int
|
|
var fullLinesFound int
|
|
for i = 0; i < board.width; i++ {
|
|
found := board.height
|
|
fullLinesFound = 0
|
|
for j = 0; j < board.height; j++ {
|
|
if board.fullLinesY[j] {
|
|
fullLinesFound++
|
|
} else {
|
|
if board.colors[i][j] != colorBlank || mino1.isMinoAtLocation(i, j) || mino2.isMinoAtLocation(i, j) {
|
|
found = j
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if i == 0 {
|
|
heightEnds = board.height - (found + fullLines - fullLinesFound)
|
|
} else {
|
|
diffrence := (found + fullLines - fullLinesFound) - foundLast
|
|
if diffrence < 0 {
|
|
diffrence = -diffrence
|
|
}
|
|
bumpy += diffrence
|
|
}
|
|
foundLast = found + fullLines - fullLinesFound
|
|
|
|
for j++; j < board.height; j++ {
|
|
if board.colors[i][j] == colorBlank && !mino1.isMinoAtLocation(i, j) && !mino2.isMinoAtLocation(i, j) {
|
|
holes++
|
|
}
|
|
}
|
|
}
|
|
|
|
heightEnds += board.height - foundLast
|
|
|
|
return
|
|
}
|
|
|
|
func (ai *Ai) getScoreFromBoardStats(fullLines int, holes int, bumpy int, heightEnds int, height1 int, height2 int) int {
|
|
score := 8 * heightEnds
|
|
if fullLines > 3 {
|
|
score += 512
|
|
}
|
|
score -= 75 * holes
|
|
score -= 15 * bumpy
|
|
if height1 < 6 {
|
|
score -= 10 * (5 - height1)
|
|
}
|
|
if height2 < 6 {
|
|
score -= 10 * (5 - height2)
|
|
}
|
|
return score
|
|
}
|