You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

346 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import random
import map_game
import copy
import math
class AIMap:
def __init__(self,boardMap: list = None):
self.map = [[0 for i in range(4)] for i in range(4)] #初始化 全部清零
self.map = [[boardMap[i][j]for i in range(4)] for j in range(4)] #复制
#AI:向左转!
def ai_go_left(self):
isAdd = 0 # 表示是否成功移动
for row in range(0,4):
index = 0
for col in range(1,4):
if self.map[row][col] > 0:
if self.map[row][col] == self.map[row][index]:
self.map[row][index] = self.map[row][col] + self.map[row][index]
self.map[row][col] = 0
index += 1
isAdd = 1
elif self.map[row][index] == 0:
self.map[row][index] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
else:
index += 1
if self.map[row][index] == 0:
self.map[row][index] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
return isAdd
#AI:向右转!
def ai_go_right(self):
isAdd = 0 # 表示是否成功移动
for row in range(0,4):
index = 3
for col in range(2,-1,-1):
if self.map[row][col] > 0:
if self.map[row][col] == self.map[row][index]:
self.map[row][index] = self.map[row][col] + self.map[row][index]
self.map[row][col] = 0
index -= 1
isAdd = 1
elif self.map[row][index] == 0:
self.map[row][index] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
else:
index -= 1
if self.map[row][index] == 0:
self.map[row][index] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
return isAdd
#AI:向前进
def ai_go_up(self):
isAdd = 0 # 表示是否成功移动
for col in range(0,4):
index = 0
for row in range(1,4):
if self.map[row][col] > 0:
if self.map[row][col] == self.map[index][col]:
self.map[index][col] = self.map[row][col] + self.map[index][col]
self.map[row][col] = 0
index += 1
isAdd = 1
elif self.map[index][col] == 0:
self.map[index][col] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
else:
index += 1
if self.map[index][col] == 0:
self.map[index][col] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
return isAdd
#AI:向后退
def ai_go_down(self):
isAdd = 0 # 表示是否成功移动
for col in range(0,4):
index = 3
for row in range(2,-1,-1):
if self.map[row][col] > 0:
if self.map[row][col] == self.map[index][col]:
self.map[index][col] = self.map[row][col] + self.map[index][col]
self.map[row][col] = 0
index -= 1
isAdd = 1
elif self.map[index][col] == 0:
self.map[index][col] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
else:
index -= 1
if self.map[index][col] == 0:
self.map[index][col] = self.map[row][col]
self.map[row][col] = 0
isAdd = 1
return isAdd
#控制AI走不同的方向 走成功为True 走失败为False
def ai_go(self,dir):#0左 1右 2上 3下
if dir == 0:#左
if self.ai_go_left() == 1:
return True
else:
return False
elif dir == 1:#右
if self.ai_go_right() == 1:
return True
else:
return False
elif dir == 2:#上
if self.ai_go_up() == 1:
return True
else:
return False
elif dir == 3:#下
if self.ai_go_down() == 1:
return True
else:
return False
#数值分析
#格局评价
#作为算法的核心,如何评价当前格局的价值是重中之重。
#在2048中除了终局外中间格局并无非常明显的价值评价指标因此需要用一些启发式的指标来评价格局。
#那些分数高的好格局是容易引向胜利的格局,而分低的坏格局是容易引向失败的格局。
def ai_evaluation(self):
#参数需要进行调试
#否则会变成人工智障
#smoothWeight = 0.5
#monoWeight = 0.03
#emptyWeight = 2.7
#maxWeight = 4
#disWeight = 10
#人工智障2.0参数
smoothWeight = 0.5
monoWeight = 0.03
emptyWeight = 2.7
maxWeight = 4
disWeight = 10
result = [disWeight*math.log2(self.dis_weight()), smoothWeight * self.smothness(), monoWeight * self.monotonicity(),
emptyWeight*math.log2(self.empty_num() + 1), maxWeight*self.max_and_submax_num()]
#人工智障3.0参数
#smoothWeight = 0.1 # 0.5
#monoWeight = 1.0 # 0.03
#emptyWeight = 2.7 # 2.7
#maxWeight = 1 # 0.01
#disWeight = 0
#smoothWeight = 0.5
#monoWeight = 0.03
#emptyWeight = 2.7
#maxWeight = 4
#disWeight = 10
#result = [disWeight*self.dis_weight(), smoothWeight * self.smothness(), monoWeight *
# self.monotonicity(), emptyWeight*math.log(self.empty_num()), maxWeight*self.max_num()]
return result
#单调性
#单调性指方块从左到右、从上到下均遵从递增或递减。
##一般来说,越单调的格局越好。
#下面是一个具有良好单调格局的例子
# 8 32 64 512
# 4 8 16 256
# 2 4 8 32
# 0 0 4 8
def monotonicity(self):
totals = [0,0,0,0] # 四个方向的单调性评估 左 右 上 下(单调递增)
#1.横着
for i in range(4):
current = 0
next = current+1
while next < 4:
while next < 4 and self.map[i][next] == 0:
next += 1
if next >= 4:
next -= 1
if self.map[i][current] != 0:
currentValue = math.log2(self.map[i][current])
else:
currentValue = 0
if self.map[i][next] != 0:
nextValue = math.log2(self.map[i][next])
else:
nextValue = 0
if currentValue > nextValue:
totals[0] += nextValue-currentValue
else:
totals[1] += currentValue-nextValue
current = next
next += 1
#2.竖着
for i in range(4):
current = 0
next = current+1
while next < 4:
while next < 4 and self.map[next][i] == 0:
next += 1
if next >= 4:
next -= 1
if self.map[current][i] != 0:
currentValue = math.log2(self.map[current][i])
else:
currentValue = 0
if self.map[next][i] != 0:
nextValue = math.log2(self.map[next][i])
else:
nextValue = 0
if currentValue > nextValue:
totals[2] += nextValue-currentValue
else:
totals[3] += currentValue-nextValue
current = next
next += 1
return max(totals[:2])+max(totals[2:])
#平滑度
#平滑性是指每个方块与其直接相邻方块数值的差,其中差越小越平滑。
#例如2旁边是4就比2旁边是128平滑。
#一般认为越平滑的格局越好。
#下面是一个具有极端平滑性的例子
# 1024 1024 1024
# 1024 1024 1024 1024
# 1024 1024 1024 1024
# 1024 1024 1024 1024
def smothness(self):
lubricity = 0
for i in range(4):
for j in range(4):
if self.map[i][j] != 0:
if i >= 1:
# 左边减去该值
lubricity -= abs(math.log2(self.map[i-1][j]+1) - math.log2(self.map[i][j]+1))
if i < 3:
# 右边减去该值
lubricity -= abs(math.log2(self.map[i+1][j]+1) - math.log2(self.map[i][j]+1))
if j > 0:
# 上面减去该值
lubricity -= abs(math.log2(self.map[i][j-1]+1) - math.log2(self.map[i][j]+1))
if j < 3:
# 下面减去该值
lubricity -= abs(math.log2(self.map[i][j+1]+1) - math.log2(self.map[i][j]+1))
return lubricity
#空格数
#因为一般来说,空格子越少对玩家越不利。
#空格越多的格局越好。
def empty_num(self):
empty_num = 0
for i in range(4):
for j in range(4):
if self.map[i][j] == 0:
empty_num += 1
return empty_num
#单个最大的值
def max_num(self):
max_num = 0
for i in range(4):
for j in range(4):
if max_num < self.map[i][j]:
max_num = self.map[i][j]
return max_num
#这个指标评价棋盘的单个数的最大值和总体和
#值越大格局越好
def max_and_submax_num(self):
max_num = 0
subMaxNum = 0
for row in range(4):
for col in range(4):
if max_num < self.map[row][col]:
subMaxNum = max_num + subMaxNum;
max_num = self.map[row][col]
return math.log2(subMaxNum/3+max_num*2/3) #2.0
#return subMaxNum/3+max_num*2/3 #3.0
#分布位置权重
#整体分布越往下,越往右格局越好
def dis_weight(self):
#wei = [[12, 13, 25, 50],
# [11, 10, 9, 8],
# [4, 5, 6, 7],
# [3, 2, 1, 0]]
wei = [[3, 2, 1, 0],
[4, 5, 6, 7],
[16, 10, 9, 8],
[32,64,128,256]]
dis_sum = 0
for j in range(4):
for i in range(4):
if self.map[i][j] == 0: #对数取值不能为0 否则会计算错误
continue
dis_sum += math.log2(self.map[i][j])*wei[i][j] #对数形式
return dis_sum
# 计算分散度,越分散得分越高
def islands(self):
islandsMark = 0
self.marked = [[True]*4]*4
for i in range(4):
for j in range(4):
if self.map[i][j] != 0:
self.marked[i][j] = False
for i in range(4):
for j in range(4):
if self.map[i][j] != 0 and not self.marked[i][j]:
islandsMark += 1
self.mark(i, j, self.map[i][j])
return islandsMark
def mark(self, x, y, value):
# 四个方向向量,方便遍历时使用
vectors = [[0, 1], [1, 0], [-1, 0], [0, -1]]
if x >= 0 and x <= 3 and y >= 0 and y <= 3 and self.map[x][y] != 0 and self.map[x][y] == value and not self.marked[x][y]:
self.marked[x][y] = True
for direction in range(4):
vector = vectors[direction]
self.mark(x+vector[0], y+vector[1], value)