diff --git a/AI_map.py b/AI_map.py new file mode 100644 index 0000000..be7ae59 --- /dev/null +++ b/AI_map.py @@ -0,0 +1,345 @@ +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) + + + +