|
|
|
|
#人工智障
|
|
|
|
|
import time
|
|
|
|
|
import pygame
|
|
|
|
|
import random
|
|
|
|
|
import map_game
|
|
|
|
|
import button_event
|
|
|
|
|
from map_config import *
|
|
|
|
|
from button import *
|
|
|
|
|
import copy
|
|
|
|
|
import math
|
|
|
|
|
import AI_map #AI专用地图
|
|
|
|
|
import sound
|
|
|
|
|
|
|
|
|
|
#导入win32api 用于弹出窗口
|
|
|
|
|
import win32api,win32con
|
|
|
|
|
|
|
|
|
|
from search_result import *
|
|
|
|
|
|
|
|
|
|
lastTime = int(time.time()*1000)
|
|
|
|
|
game_state = 0 #0为手动 1为AI
|
|
|
|
|
|
|
|
|
|
#0左 1右 2上 3下
|
|
|
|
|
def ai_find_direction():
|
|
|
|
|
#dir = random.randint(0,3) #人工智障1.0 随机数生成
|
|
|
|
|
dir = lanmeng() #人工智障2.0 (贪心算法)
|
|
|
|
|
#dir = lanmeng2() #人工智障3.0 (minimax算法 但经过试验 效果比2.0还要差 而且速度很慢所以放弃)
|
|
|
|
|
return dir
|
|
|
|
|
|
|
|
|
|
#AI继续启动吧
|
|
|
|
|
def ai_2048_game_going(speed = 500):
|
|
|
|
|
global lastTime
|
|
|
|
|
global game_state
|
|
|
|
|
if map_game.judge_gameover() == False and map_game.judge_gamewin() == False:
|
|
|
|
|
#map_game.sound_flag = 0
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
button_event.buttonBase.check_event(event)
|
|
|
|
|
button_event.buttonReturn.check_event(event)
|
|
|
|
|
button_event.buttonAI.check_event(event)
|
|
|
|
|
button_event.buttonTips.check_event(event)
|
|
|
|
|
button_event.buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
if event.type == pygame.KEYDOWN:
|
|
|
|
|
if(event.key == pygame.K_z):#Z键加快速度
|
|
|
|
|
if map_game.ai_delay_time > 50:
|
|
|
|
|
map_game.ai_delay_time = map_game.ai_delay_time - 50
|
|
|
|
|
if(event.key == pygame.K_x):#X键减慢速度
|
|
|
|
|
if map_game.ai_delay_time < 1000:
|
|
|
|
|
map_game.ai_delay_time = map_game.ai_delay_time + 50
|
|
|
|
|
|
|
|
|
|
thisTime = int(time.time()*1000)
|
|
|
|
|
if thisTime - lastTime > speed:
|
|
|
|
|
lastTime = int(time.time()*1000)
|
|
|
|
|
dir = ai_find_direction()
|
|
|
|
|
if dir == 0:
|
|
|
|
|
map_game.go_move_left()
|
|
|
|
|
elif dir == 1:
|
|
|
|
|
map_game.go_move_right()
|
|
|
|
|
elif dir == 2:
|
|
|
|
|
map_game.go_move_up()
|
|
|
|
|
elif dir == 3:
|
|
|
|
|
map_game.go_move_down()
|
|
|
|
|
else:
|
|
|
|
|
game_state = 0 #跑不了了 你爱咋整咋整
|
|
|
|
|
else:
|
|
|
|
|
game_state = 0
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
button_event.buttonBase.check_event(event)
|
|
|
|
|
button_event.buttonReturn.check_event(event)
|
|
|
|
|
button_event.buttonAI.check_event(event)
|
|
|
|
|
button_event.buttonTips.check_event(event)
|
|
|
|
|
button_event.buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
ii = 1
|
|
|
|
|
reshow_temp_buf = [ #缓存 记录
|
|
|
|
|
[0,0,0,0],
|
|
|
|
|
[0,0,0,0],
|
|
|
|
|
[0,0,0,0],
|
|
|
|
|
[0,0,0,0]
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
lstTime = int(time.time()*1000)
|
|
|
|
|
#回放模式
|
|
|
|
|
def reshow_mode(delaytime):
|
|
|
|
|
global ii
|
|
|
|
|
global lstTime
|
|
|
|
|
global game_state
|
|
|
|
|
tisTime = int(time.time()*1000)
|
|
|
|
|
|
|
|
|
|
for event in pygame.event.get():
|
|
|
|
|
#按钮事件检测
|
|
|
|
|
button_event.buttonBase.check_event(event)
|
|
|
|
|
button_event.buttonReturn.check_event(event)
|
|
|
|
|
button_event.buttonAI.check_event(event)
|
|
|
|
|
button_event.buttonTips.check_event(event)
|
|
|
|
|
button_event.buttonReshow.check_event(event)
|
|
|
|
|
|
|
|
|
|
if event.type == pygame.KEYDOWN:
|
|
|
|
|
if(event.key == pygame.K_z):#Z键加快速度
|
|
|
|
|
if map_game.ai_delay_time > 50:
|
|
|
|
|
map_game.ai_delay_time = map_game.ai_delay_time - 50
|
|
|
|
|
if(event.key == pygame.K_x):#X键减慢速度
|
|
|
|
|
if map_game.ai_delay_time < 1000:
|
|
|
|
|
map_game.ai_delay_time = map_game.ai_delay_time + 50
|
|
|
|
|
|
|
|
|
|
if tisTime - lstTime > delaytime:
|
|
|
|
|
lstTime = int(time.time()*1000)
|
|
|
|
|
cc = copy.deepcopy(map_game.board_stack)
|
|
|
|
|
if ii < len(cc):
|
|
|
|
|
map_game.board = copy.deepcopy(cc[ii])
|
|
|
|
|
ii = ii + 1
|
|
|
|
|
sound.slide_sound()
|
|
|
|
|
else:
|
|
|
|
|
map_game.board = copy.deepcopy(reshow_temp_buf)
|
|
|
|
|
game_state = 0
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
sound.background_sound()
|
|
|
|
|
|
|
|
|
|
#独创AI算法(人工智障2.0)
|
|
|
|
|
#贪心算法:
|
|
|
|
|
#从四个方向遍历 并获取移动后的格局值 四个方向哪个方向的格局值最大就选哪一个
|
|
|
|
|
def lanmeng():
|
|
|
|
|
dir_num = [0,0,0,0]
|
|
|
|
|
TestAIBoard = AI_map.AIMap(map_game.board)
|
|
|
|
|
for direction in range(0,4):#四个方向都遍历一遍
|
|
|
|
|
TestAIBoard2 = AI_map.AIMap(TestAIBoard.map)
|
|
|
|
|
if TestAIBoard2.ai_go(direction) == False:#走不了就滚蛋吧
|
|
|
|
|
dir_num[direction] = -999999
|
|
|
|
|
else:
|
|
|
|
|
dir_num[direction] = sum(TestAIBoard2.ai_evaluation())#将分析的结果加起来
|
|
|
|
|
|
|
|
|
|
if max(dir_num) == -999999:
|
|
|
|
|
return -1
|
|
|
|
|
else:
|
|
|
|
|
return dir_num.index(max(dir_num)) #选取得分最高的一个
|
|
|
|
|
|
|
|
|
|
#独创AI算法(人工智障3.0)
|
|
|
|
|
#minimax算法
|
|
|
|
|
# 定义搜索结果类,用于方便处理返回值
|
|
|
|
|
class searchResult:
|
|
|
|
|
def __init__(self, move=-1, score=0, positions=0, cutoffs=0) -> None:
|
|
|
|
|
self.move = move
|
|
|
|
|
self.positions = positions
|
|
|
|
|
self.cutoffs = cutoffs
|
|
|
|
|
self.score = score
|
|
|
|
|
|
|
|
|
|
def search(thisBoard: AI_map.AIMap, depth, alpha, beta, positions, cutoffs, playerTurn: bool):
|
|
|
|
|
bestScore = 0
|
|
|
|
|
bestMove = -1
|
|
|
|
|
result = searchResult()
|
|
|
|
|
if playerTurn == True: #玩家回合 倾向于让玩家获得最多的分数
|
|
|
|
|
bestScore = alpha # 最高分为alpha
|
|
|
|
|
for direction in range(4): # 四个方向分别进行遍历
|
|
|
|
|
newBoard = AI_map.AIMap(thisBoard.map) # 新建一个棋盘防止影响到正式游戏
|
|
|
|
|
changed = newBoard.ai_go(direction) # 相对应方向移动
|
|
|
|
|
if changed == True: # 如果这个方向可以移动
|
|
|
|
|
positions += 1 # positions自增
|
|
|
|
|
if depth == 0: # 如果已经搜索到最底层了
|
|
|
|
|
result.move = direction
|
|
|
|
|
result.score = sum(newBoard.ai_evaluation()) # 返回当前局面的评价值
|
|
|
|
|
else:
|
|
|
|
|
result = search(newBoard, depth-1, bestScore, beta, positions, cutoffs, False) # 进行min轮,即让AI下出对局面最不利的一步
|
|
|
|
|
if result.score > 9900:
|
|
|
|
|
result.score -= 1
|
|
|
|
|
positions = result.positions
|
|
|
|
|
cutoffs = result.cutoffs # 将返回值进行处理
|
|
|
|
|
|
|
|
|
|
if result.score > bestScore:
|
|
|
|
|
bestScore = result.score
|
|
|
|
|
bestMove = direction
|
|
|
|
|
|
|
|
|
|
if bestScore > beta: # 如果最高值大于beta,则已经证明该走法优于前面的最优,则本深度下后面不用继续计算。
|
|
|
|
|
cutoffs += 1
|
|
|
|
|
return searchResult(bestMove, beta, positions, cutoffs)
|
|
|
|
|
else: #AI回合 倾向于让玩家获得最少的分数
|
|
|
|
|
bestScore = beta
|
|
|
|
|
newBoard = AI_map.AIMap(thisBoard.map)
|
|
|
|
|
score_2 = []
|
|
|
|
|
score_4 = []
|
|
|
|
|
worstSituation = []
|
|
|
|
|
cells = []
|
|
|
|
|
for i in range(4): #找到空格子
|
|
|
|
|
for j in range(4):
|
|
|
|
|
if newBoard.map[i][j] == 0:
|
|
|
|
|
cells.append([i, j])
|
|
|
|
|
|
|
|
|
|
for value in [2, 4]: #生成可能的所有情况,并进行评估
|
|
|
|
|
for i in range(len(cells)):
|
|
|
|
|
if newBoard.map[cells[i][0]][cells[i][1]] == 0:
|
|
|
|
|
newBoard.map[cells[i][0]][cells[i][1]] = value
|
|
|
|
|
if value == 2:
|
|
|
|
|
score_2.append(-newBoard.smothness() + newBoard.islands())
|
|
|
|
|
if value == 4:
|
|
|
|
|
score_4.append(-newBoard.smothness() + newBoard.islands())
|
|
|
|
|
newBoard.map[cells[i][0]][cells[i][1]] = 0
|
|
|
|
|
|
|
|
|
|
maxScore = max(max(score_2), max(score_4)) #找到最差的情况
|
|
|
|
|
for i in range(len(score_2)): # 最差的情况可能不止一种,所以遍历一遍防止遗漏
|
|
|
|
|
if score_2[i] == maxScore:
|
|
|
|
|
worstSituation.append([cells[i], 2])
|
|
|
|
|
for i in range(len(score_4)):
|
|
|
|
|
if score_4[i] == maxScore:
|
|
|
|
|
worstSituation.append([cells[i], 4])
|
|
|
|
|
for situation in worstSituation: # 遍历所有最差情况
|
|
|
|
|
nnewBoard = AI_map.AIMap(thisBoard.map)
|
|
|
|
|
positions += 1
|
|
|
|
|
result = search(nnewBoard, depth, alpha, bestScore, positions, cutoffs, True) # 进一步搜索
|
|
|
|
|
positions = result.positions
|
|
|
|
|
cutoffs = result.cutoffs
|
|
|
|
|
|
|
|
|
|
if result.score < bestScore:
|
|
|
|
|
bestScore = result.score
|
|
|
|
|
|
|
|
|
|
if bestScore < alpha: # 剪枝同理
|
|
|
|
|
cutoffs += 1
|
|
|
|
|
return searchResult(-1, alpha, positions, cutoffs)
|
|
|
|
|
|
|
|
|
|
return searchResult(bestMove, bestScore, positions, cutoffs)
|
|
|
|
|
|
|
|
|
|
def lanmeng2():
|
|
|
|
|
depth = 3
|
|
|
|
|
nAIMap = AI_map.AIMap(map_game.board)
|
|
|
|
|
newBest = search(nAIMap, depth, -1000000, 1000000, 0, 0, True)
|
|
|
|
|
return newBest.move
|
|
|
|
|
|
|
|
|
|
#以下为回调函数
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#
|
|
|
|
|
#AI按键回调函数
|
|
|
|
|
def ai_button_callback():
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
global game_state
|
|
|
|
|
if game_state == 0:
|
|
|
|
|
game_state = 1
|
|
|
|
|
sound.ai_background_sound()
|
|
|
|
|
else:
|
|
|
|
|
game_state = 0
|
|
|
|
|
sound.background_sound()
|
|
|
|
|
|
|
|
|
|
#提示模式按键回调函数
|
|
|
|
|
def tips_button_event():
|
|
|
|
|
tips_direction = ai_find_direction()
|
|
|
|
|
if tips_direction == 0:
|
|
|
|
|
win32api.MessageBox(0,"下一步应该:往左","提示",win32con.MB_OK)
|
|
|
|
|
elif tips_direction == 1:
|
|
|
|
|
win32api.MessageBox(0,"下一步应该:往右","提示",win32con.MB_OK)
|
|
|
|
|
elif tips_direction == 2:
|
|
|
|
|
win32api.MessageBox(0,"下一步应该:往上","提示",win32con.MB_OK)
|
|
|
|
|
elif tips_direction == 3:
|
|
|
|
|
win32api.MessageBox(0,"下一步应该:往下","提示",win32con.MB_OK)
|
|
|
|
|
else:
|
|
|
|
|
win32api.MessageBox(0,"走不了了","提示",win32con.MB_OK)
|
|
|
|
|
|
|
|
|
|
#回放功能回调函数
|
|
|
|
|
def reshow_callback():
|
|
|
|
|
sound.stop_sound()
|
|
|
|
|
global game_state
|
|
|
|
|
global ii
|
|
|
|
|
global reshow_temp_buf
|
|
|
|
|
if game_state != 2:
|
|
|
|
|
sound.reshow_sound()
|
|
|
|
|
game_state = 2 #不管什么模式进去就是回放模式
|
|
|
|
|
ii = 1
|
|
|
|
|
reshow_temp_buf = copy.deepcopy(map_game.board) #缓存下来
|