from .cache import Cache from .eval import FIVE # Checked MAX = 1000000000 cache_hits = { "search": 0, "total": 0, "hit": 0 } onlyThreeThreshold = 6 cache = Cache() def factory(onlyThree=False, onlyFour=False): def helper(board, role, depth, cDepth=0, path=(), alpha=-MAX, beta=MAX): cache_hits["search"] += 1 if cDepth >= depth or board.isGameOver(): return [board.evaluate(role), None, (path)] hash = board.hash() prev = cache.get(hash) if prev and prev["role"] == role: if ( (abs(prev["value"]) >= FIVE or prev["depth"] >= depth - cDepth) and prev["onlyThree"] == onlyThree and prev["onlyFour"] == onlyFour ): cache_hits["hit"] += 1 return [prev["value"], prev["move"], path + prev["path"]] value = -MAX move = None bestPath = path # Copy the current path bestDepth = 0 # points = board.getValuableMoves(role, cDepth, onlyThree or cDepth > onlyThreeThreshold, onlyFour) points = board.getValuableMoves(role, cDepth, onlyThree or cDepth > onlyThreeThreshold, onlyFour) if cDepth == 0: print('points:', points) if not len(points): return [board.evaluate(role), None, path] for d in range(cDepth + 1, depth + 1): # 迭代加深过程中只找己方能赢的解,因此只搜索偶数层即可 if d % 2 != 0: continue breakAll = False for point in points: board.put(point[0], point[1], role) newPath = tuple(list(path) + [point]) # Add current move to path currentValue, currentMove, currentPath = helper(board, -role, d, cDepth + 1, tuple(newPath) , -beta, -alpha) currentValue = -currentValue board.undo() ## 迭代加深的过程中,除了能赢的棋,其他都不要 ## 原因是:除了必胜的,其他评估不准。比如必输的棋,由于走的步数偏少,也会变成没有输,比如5 ### 步之后输了,但是1步肯定不会输,这时候1步的分数是不准确的,显然不能选择。 if currentValue >= FIVE or d == depth: # 必输的棋,也要挣扎一下,选择最长的路径 if ( currentValue > value or (currentValue <= -FIVE and value <= -FIVE and len(currentPath) > bestDepth) ): value = currentValue move = point bestPath = currentPath bestDepth = len(currentPath) alpha = max(alpha, value) if alpha >= FIVE: breakAll = True break if alpha >= beta: break if breakAll: break if (cDepth < onlyThreeThreshold or onlyThree or onlyFour) and (not prev or prev["depth"] < depth - cDepth): cache_hits["total"] += 1 cache.put(hash, { "depth": depth - cDepth, "value": value, "move": move, "role": role, "path": bestPath[cDepth:], "onlyThree": onlyThree, "onlyFour": onlyFour, }) return [value, move, bestPath] return helper _minmax = factory() vct = factory(True) vcf = factory(False, True) def minmax(board, role, depth=4, enableVCT=False): if enableVCT: vctDepth = depth + 8 value, move, bestPath = vct(board, role, vctDepth) if value >= FIVE: return [value, move, bestPath] value, move, bestPath = _minmax(board, role, depth) ''' // 假设对方有杀棋,先按自己的思路走,走完之后看对方是不是还有杀棋 // 如果对方没有了,那么就说明走的是对的 // 如果对方还是有,那么要对比对方的杀棋路径和自己没有走棋时的长短 // 如果走了棋之后路径变长了,说明走的是对的 // 如果走了棋之后,对方杀棋路径长度没变,甚至更短,说明走错了,此时就优先封堵对方 ''' board.put(move[0], move[1], role) value2, move2, bestPath2 = vct(board.reverse(), role, vctDepth) board.undo() if value < FIVE and value2 == FIVE and len(bestPath2) > len(bestPath): value3, move3, bestPath3 = vct(board.reverse(), role, vctDepth) if len(bestPath2) <= len(bestPath3): return [value, move2, bestPath2] # value2 是被挡住的,所以这里还是用value return [value, move, bestPath] else: return _minmax(board, role, depth)