diff --git a/ai2048.py b/ai2048.py new file mode 100644 index 0000000..3ebd503 --- /dev/null +++ b/ai2048.py @@ -0,0 +1,271 @@ +#人工智障 +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) #缓存下来 \ No newline at end of file