精华内容
下载资源
问答
  • Python 五子棋AI实现(2):棋型评估函数实现

    万次阅读 多人点赞 2019-05-23 20:17:00
    python 五子棋AI实现(2):棋型评估函数实现五子棋基本棋型介绍评估方法介绍代码实现 五子棋基本棋型介绍 参考资料:http://game.onegreen.net/wzq/HTML/142336.html 最常见的基本棋型大体有以下几种:连五,活...

    五子棋基本棋型介绍

    参考资料:http://game.onegreen.net/wzq/HTML/142336.html
    最常见的基本棋型大体有以下几种:连五,活四,冲四,活三,眠三,活二,眠二。

    连五:顾名思义,五颗同色棋子连在一起,不需要多讲。
    图2-1连5

    活四:有两个连五点(即有两个点可以形成五),图中白点即为连五点。
    稍微思考一下就能发现活四出现的时候,如果对方单纯过来防守的话,是已经无法阻止自己连五了。
    图2-2活四

    冲四:有一个连五点,如下面三图,均为冲四棋型。图中白点为连五点。
    相对比活四来说,冲四的威胁性就小了很多,因为这个时候,对方只要跟着防守在那个唯一的连五点上,冲四就没法形成连五。
    图2-3冲四1 图2-4冲四2 图2-5冲四3

    活三:可以形成活四的三,如下图,代表两种最基本的活三棋型。图中白点为活四点。
    活三棋型是我们进攻中最常见的一种,因为活三之后,如果对方不以理会,将可以下一手将活三变成活四,而我们知道活四是已经无法单纯防守住了。所以,当我们面对活三的时候,需要非常谨慎对待。在自己没有更好的进攻手段的情况下,需要对其进行防守,以防止其形成可怕的活四棋型。
    图2-6 活三1 图2-7 活三2
    其中图2-7中间跳着一格的活三,也可以叫做跳活三。

    眠三:只能够形成冲四的三,如下各图,分别代表最基础的六种眠三形状。图中白点代表冲四点。眠三的棋型与活三的棋型相比,危险系数下降不少,因为眠三棋型即使不去防守,下一手它也只能形成冲四,而对于单纯的冲四棋型,我们知道,是可以防守住的。
    图2-8 眠三1 图2-9 眠三2 图2-10 眠三3

    图2-11眠三4 图2-12 眠三5 图2-13 眠三6
    如上所示,眠三的形状是很丰富的。对于初学者,在下棋过程中,很容易忽略不常见的眠三形状,例如图2-13所示的眠三。

    有新手学了活三眠三后,会提出疑问,说活三也可以形成冲四啊,那岂不是也可以叫眠三?
    会提出这个问题,说明对眠三定义看得不够仔细:眠三的的定义是,只能够形成冲四的三。而活三可以形成眠三,但也能够形成活四。

    此外,在五子棋中,活四棋型比冲四棋型具有更大的优势,所以,我们在既能够形成活四又能够形成冲四时,会选择形成活四。

    温馨提示:学会判断一个三到底是活三还是眠三是非常重要的。所以,需要好好体会。
    后边禁手判断的时候也会有所应用。

    活二:能够形成活三的二,如下图,是三种基本的活二棋型。图中白点为活三点。
    活二棋型看起来似乎很无害,因为他下一手棋才能形成活三,等形成活三,我们再防守也不迟。但其实活二棋型是非常重要的,尤其是在开局阶段,我们形成较多的活二棋型的话,当我们将活二变成活三时,才能够令自己的活三绵绵不绝微风里,让对手防不胜防。
    图2-14 在这里插入图片描述 图2-15 在这里插入图片描述 图2-16 在这里插入图片描述

    ⑦眠二:能够形成眠三的二。图中四个为最基本的眠二棋型,细心且喜欢思考的同学会根据眠三介绍中的图2-13找到与下列四个基本眠二棋型都不一样的眠二。图中白点为眠三点。
    图2-17 眠二1 图2-18 眠二2
    图2-19 眠二3 图2-20 眠二4

    评估方法介绍

    由上面的介绍可知,有7种有效的棋型(连五,活四,冲四,活三,眠三,活二,眠二),我们可以创建黑棋和白棋两个数组,记录棋盘上黑棋和白棋分别形成的所有棋型的个数,然后按照一定的规则进行评分。

    如何记录棋盘上的棋形个数,一个很直观的方法是,棋盘上有15条水平线,15条竖直线,不考虑长度小于5的斜线,有21条从左上到右下的斜线,21条从左下到右上的斜线。然后对每一条线分别对黑棋和白棋查找是否有符合的棋型。这种方法比较直观,但是实现起来不方便。有兴趣的可以尝试下。

    这里用的方法是,对整个棋盘进行遍历,对于每一个白棋或黑棋,以它为中心,记录符合的棋型个数。
    具体实现方式如下:

    1. 遍历棋盘上的每个点,如果是黑棋或白旗,则对这个点所在四个方向形成的四条线分别进行评估。四个方向即水平,竖直,两个斜线( \ , / ),四个方向依次按照从左到右, 从上到下,从左上到右下,从左下到右上 来检测。

    2. 对于具体的一条线,如下图,已选取点为中心,取该方向上前面四个点,后面四个点,组成一个长度为9的数组。
      线1
      然后找下和中心点相连的同色棋子有几个,比如下图,相连的白色棋子有3个,根据相连棋子的个数再分别进行判断,最后得出这行属于上面说的哪一种棋型。具体判断可以看代码中的 analysisLine 函数.
      这里有个注意点,在评估白旗1的时候,白棋3和5已经被判断过,所以要标记下,下次遍历到这个方向的白棋3和5,需要跳过,避免重复统计棋型。
      线2

    3. 根据棋盘上黑棋和白棋的棋型统计信息,按照一定规则进行评分。
      假设形成该棋局的最后一步是黑棋下的,则最后的评分是(黑棋得分 - 白棋得分),在相同棋型相同个数的情况下,白棋会占优,因为下一步是白棋下。比如黑棋有个冲四,白棋有个冲四,显然白棋占优,因为下一步白棋就能成连五。
      按照下面的规则依次匹配,下面设的评分值是可以优化调整的:
      前面9条为必杀情况,会直接返回评分,

      • 黑棋连5,评分为10000
      • 白棋连5,评分为 -10000
      • 黑棋两个冲四可以当成一个活四
      • 白棋有活四,评分为 -9050
      • 白棋有冲四,评分为 -9040
      • 黑棋有活四,评分为 9030
      • 黑棋有冲四和活三,评分为 9020
      • 黑棋没有冲四,且白棋有活三,评分为 9010
      • 黑棋有2个活三,且白棋没有活三或眠三,评分为 9000
      • 下面针对黑棋或白棋的活三,眠三,活二,眠二的个数依次增加分数,评分为(黑棋得分 - 白棋得分)

    简单AI介绍

    有了评估函数,轮到AI下棋时,就要针对当前的棋局,找到一个最有利的位置来下。AI会尝试在每个空点下棋,形成一个新的棋局,然后用评估函数来获取这个棋局时的评分。从中选取评分最高的位置来就行了。
    AI 获取最有利位置的逻辑:

    • 遍历棋盘上的每一个空点:
      在这个空点下棋,获取新的棋局的评分
      如果是更高的评分,则保存该位置
      将这个位置恢复为空点
    • 获得最高评分的位置

    上面这段逻辑我们在后续的文章中会不断优化,使得AI越来越厉害。

    代码实现

    AI的实现都在ChessAI类中,record数组记录所有位置的四个方向是否被检测过。count二维数组记录黑棋和白棋的棋型个数统计。pos_score 给棋盘上每个位置设一个初始分数,越靠近棋盘中心,分数越高,用来在最开始没有任何棋型时的,AI优先选取靠中心的位置。
    reset函数每次调用评估函数前都需要清一下之前的统计数据。

    class ChessAI():
    	def __init__(self, chess_len):
    		self.len = chess_len
    		# [horizon, vertical, left diagonal, right diagonal]
    		self.record = [[[0,0,0,0] for x in range(chess_len)] for y in range(chess_len)]
    		self.count = [[0 for x in range(CHESS_TYPE_NUM)] for i in range(2)]
    		self.pos_score = [[(7 - max(abs(x - 7), abs(y - 7))) for x in range(chess_len)] for y in range(chess_len)]
    		
    	def reset(self):
    		for y in range(self.len):
    			for x in range(self.len):
    				for i in range(4):
    					self.record[y][x][i] = 0
    
    		for i in range(len(self.count)):
    			for j in range(len(self.count[0])):
    				self.count[i][j] = 0
    		
    		self.save_count = 0
    

    findBestChess 函数就是AI的入口函数。
    search 函数是上面AI逻辑的代码实现,先通过 genmove 函数获取棋盘上所有的空点,然后依次尝试,获得评分最高的位置并返回。

    	# get all positions that is empty
    	def genmove(self, board, turn):
    		moves = []
    		for y in range(self.len):
    			for x in range(self.len):
    				if board[y][x] == 0:
    					score = self.pos_score[y][x]
    					moves.append((score, x, y))
    
    		moves.sort(reverse=True)
    		return moves
    
    	def search(self, board, turn):
    		moves = self.genmove(board, turn)
    		bestmove = None
    		max_score = -0x7fffffff
    		for score, x, y in moves:
    			board[y][x] = turn.value
    			score = self.evaluate(board, turn)
    			board[y][x] = 0
    			
    			if score > max_score:
    				max_score = score
    				bestmove = (max_score, x, y)
    		return bestmove
    		
    	def findBestChess(self, board, turn):
    		time1 = time.time()
    		score, x, y = self.search(board, turn)
    		time2 = time.time()
    		print('time[%f] (%d, %d), score[%d] save[%d]' % ((time2-time1), x, y, score, self.save_count))
    		return (x, y)
    

    evaluate函数, 就是上面评估方法的代码实现,参数turn表示最近一手棋是谁下的,根据turn决定的mine(表示自己棋的值)和oppoent(表示对手棋的值,下一步棋由对手下),在对棋型评分时会用到。checkWin 是游戏用来判断是否有一方获胜了。
    其中调用的getScore函数就是对黑棋和白棋进行评分。
    evaluatePoint函数就是对于一个位置的四个方向分别进行检查。

    	def evaluate(self, board, turn, checkWin=False):
    		self.reset()
    		
    		if turn == MAP_ENTRY_TYPE.MAP_PLAYER_ONE:
    			mine = 1
    			opponent = 2
    		else:
    			mine = 2
    			opponent = 1
    		
    		for y in range(self.len):
    			for x in range(self.len):
    				if board[y][x] == mine:
    					self.evaluatePoint(board, x, y, mine, opponent)
    				elif board[y][x] == opponent:
    					self.evaluatePoint(board, x, y, opponent, mine)
    		
    		mine_count = self.count[mine-1]
    		opponent_count = self.count[opponent-1]
    		if checkWin:
    			return mine_count[FIVE] > 0
    		else:	
    			mscore, oscore = self.getScore(mine_count, opponent_count)
    			return (mscore - oscore)
    
    	def evaluatePoint(self, board, x, y, mine, opponent):
    		dir_offset = [(1, 0), (0, 1), (1, 1), (1, -1)] # direction from left to right
    		for i in range(4):
    			if self.record[y][x][i] == 0:
    				self.analysisLine(board, x, y, i, dir_offset[i], mine, opponent, self.count[mine-1])
    			else:
    				self.save_count += 1
    

    analysisLine函数是判断一条线上自己棋能形成棋型的代码, mine表示自己棋的值,opponent表示对手棋的值。
    要根据中心点相邻己方棋子能连成的个数来分别判断,己方棋值设为M,对方棋值设为P,空点值设为X。具体可以看代码中的注释。

    • 连成5个点,可以直接返回
    • 连成4个点,要考虑是否被对手棋档着,比如 PMMMMX
    • 连成3个点,要考虑隔一个空点的情况,比如 MXMMMX。
    • 连成2个点,要考虑隔一个空点的如情况,比如 MXMMX,MMXMM。
    • 只有1个点,要考虑隔一个或二个空点的情况,比如 XMXMX,XMXXMX。

    getLine函数,根据棋子的位置和方向,获取上面说的长度为9的线。有个取巧的地方,如果线上的位置超出了棋盘范围,就将这个位置的值设为对手的值,因为超出范围和被对手棋挡着,对棋型判断的结果是一样的。
    setRecord函数 标记已经检测过,需要跳过的棋子。

    	# line is fixed len 9: XXXXMXXXX
    	def getLine(self, board, x, y, dir_offset, mine, opponent):
    		line = [0 for i in range(9)]
    		
    		tmp_x = x + (-5 * dir_offset[0])
    		tmp_y = y + (-5 * dir_offset[1])
    		for i in range(9):
    			tmp_x += dir_offset[0]
    			tmp_y += dir_offset[1]
    			if (tmp_x < 0 or tmp_x >= self.len or 
    				tmp_y < 0 or tmp_y >= self.len):
    				line[i] = opponent # set out of range as opponent chess
    			else:
    				line[i] = board[tmp_y][tmp_x]
    						
    		return line
    		
    	def analysisLine(self, board, x, y, dir_index, dir_offset, mine, opponent, count):
    		# record line range[left, right] as analysized
    		def setRecord(self, x, y, left, right, dir_index, dir_offset):
    			tmp_x = x + (-5 + left) * dir_offset[0]
    			tmp_y = y + (-5 + left) * dir_offset[1]
    			for i in range(left, right+1):
    				tmp_x += dir_offset[0]
    				tmp_y += dir_offset[1]
    				self.record[tmp_y][tmp_x][dir_index] = 1
    	
    		empty = MAP_ENTRY_TYPE.MAP_EMPTY.value
    		left_idx, right_idx = 4, 4
    		
    		line = self.getLine(board, x, y, dir_offset, mine, opponent)
    
    		while right_idx < 8:
    			if line[right_idx+1] != mine:
    				break
    			right_idx += 1
    		while left_idx > 0:
    			if line[left_idx-1] != mine:
    				break
    			left_idx -= 1
    		
    		left_range, right_range = left_idx, right_idx
    		while right_range < 8:
    			if line[right_range+1] == opponent:
    				break
    			right_range += 1
    		while left_range > 0:
    			if line[left_range-1] == opponent:
    				break
    			left_range -= 1
    		
    		chess_range = right_range - left_range + 1
    		if chess_range < 5:
    			setRecord(self, x, y, left_range, right_range, dir_index, dir)
    			return CHESS_TYPE.NONE
    		
    		setRecord(self, x, y, left_idx, right_idx, dir_index, dir)
    		
    		m_range = right_idx - left_idx + 1
    		
    		# M:mine chess, P:opponent chess or out of range, X: empty
    		if m_range == 5:
    			count[FIVE] += 1
    		
    		# Live Four : XMMMMX 
    		# Chong Four : XMMMMP, PMMMMX
    		if m_range == 4:
    			left_empty = right_empty = False
    			if line[left_idx-1] == empty:
    				left_empty = True			
    			if line[right_idx+1] == empty:
    				right_empty = True
    			if left_empty and right_empty:
    				count[FOUR] += 1
    			elif left_empty or right_empty:
    				count[SFOUR] += 1
    		
    		# Chong Four : MXMMM, MMMXM, the two types can both exist
    		# Live Three : XMMMXX, XXMMMX
    		# Sleep Three : PMMMX, XMMMP, PXMMMXP
    		if m_range == 3:
    			left_empty = right_empty = False
    			left_four = right_four = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine: # MXMMM
    					setRecord(self, x, y, left_idx-2, left_idx-1, dir_index, dir)
    					count[SFOUR] += 1
    					left_four = True
    				left_empty = True
    				
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine: # MMMXM
    					setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    					count[SFOUR] += 1
    					right_four = True 
    				right_empty = True
    			
    			if left_four or right_four:
    				pass
    			elif left_empty and right_empty:
    				if chess_range > 5: # XMMMXX, XXMMMX
    					count[THREE] += 1
    				else: # PXMMMXP
    					count[STHREE] += 1
    			elif left_empty or right_empty: # PMMMX, XMMMP
    				count[STHREE] += 1
    		
    		# Chong Four: MMXMM, only check right direction
    		# Live Three: XMXMMX, XMMXMX the two types can both exist
    		# Sleep Three: PMXMMX, XMXMMP, PMMXMX, XMMXMP
    		# Live Two: XMMX
    		# Sleep Two: PMMX, XMMP
    		if m_range == 2:
    			left_empty = right_empty = False
    			left_three = right_three = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine:
    					setRecord(self, x, y, left_idx-2, left_idx-1, dir_index, dir)
    					if line[left_idx-3] == empty:
    						if line[right_idx+1] == empty: # XMXMMX
    							count[THREE] += 1
    						else: # XMXMMP
    							count[STHREE] += 1
    						left_three = True
    					elif line[left_idx-3] == opponent: # PMXMMX
    						if line[right_idx+1] == empty:
    							count[STHREE] += 1
    							left_three = True
    						
    				left_empty = True
    				
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine:
    					if line[right_idx+3] == mine:  # MMXMM
    						setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    						count[SFOUR] += 1
    						right_three = True
    					elif line[right_idx+3] == empty:
    						#setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    						if left_empty:  # XMMXMX
    							count[THREE] += 1
    						else:  # PMMXMX
    							count[STHREE] += 1
    						right_three = True
    					elif left_empty: # XMMXMP
    						count[STHREE] += 1
    						right_three = True
    						
    				right_empty = True
    			
    			if left_three or right_three:
    				pass
    			elif left_empty and right_empty: # XMMX
    				count[TWO] += 1
    			elif left_empty or right_empty: # PMMX, XMMP
    				count[STWO] += 1
    		
    		# Live Two: XMXMX, XMXXMX only check right direction
    		# Sleep Two: PMXMX, XMXMP
    		if m_range == 1:
    			left_empty = right_empty = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine:
    					if line[left_idx-3] == empty:
    						if line[right_idx+1] == opponent: # XMXMP
    							count[STWO] += 1
    				left_empty = True
    
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine:
    					if line[right_idx+3] == empty:
    						if left_empty: # XMXMX
    							#setRecord(self, x, y, left_idx, right_idx+2, dir_index, dir)
    							count[TWO] += 1
    						else: # PMXMX
    							count[STWO] += 1
    				elif line[right_idx+2] == empty:
    					if line[right_idx+3] == mine and line[right_idx+4] == empty: # XMXXMX
    						count[TWO] += 1
    						
    		return CHESS_TYPE.NONE
    

    完整代码

    一共有三个文件,新增加了一个ChessAI.py

    main.py

    增加了对于ChessAI类的 findBestChess 函数的调用,获取AI选择的下棋位置。

    import pygame
    from pygame.locals import *
    from GameMap import *
    from ChessAI import *
    
    
    class Button():
    	def __init__(self, screen, text, x, y, color, enable):
    		self.screen = screen
    		self.width = BUTTON_WIDTH
    		self.height = BUTTON_HEIGHT
    		self.button_color = color
    		self.text_color = (255, 255, 255)
    		self.enable = enable
    		self.font = pygame.font.SysFont(None, BUTTON_HEIGHT*2//3)
    		
    		self.rect = pygame.Rect(0, 0, self.width, self.height)
    		self.rect.topleft = (x, y)
    		self.text = text
    		self.init_msg()
    		
    	def init_msg(self):
    		if self.enable:
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
    		else:
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
    		self.msg_image_rect = self.msg_image.get_rect()
    		self.msg_image_rect.center = self.rect.center
    		
    	def draw(self):
    		if self.enable:
    			self.screen.fill(self.button_color[0], self.rect)
    		else:
    			self.screen.fill(self.button_color[1], self.rect)
    		self.screen.blit(self.msg_image, self.msg_image_rect)
    		
    
    class StartButton(Button):
    	def __init__(self, screen, text, x, y):
    		super().__init__(screen, text, x, y, [(26, 173, 25),(158, 217, 157)], True)
    	
    	def click(self, game):
    		if self.enable: 
    			game.start()
    			game.winner = None
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
    			self.enable = False
    			return True
    		return False
    	
    	def unclick(self):
    		if not self.enable:
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
    			self.enable = True
    		
    class GiveupButton(Button):
    	def __init__(self, screen, text, x, y):
    		super().__init__(screen, text, x, y, [(230, 67, 64),(236, 139, 137)], False)
    		
    	def click(self, game):
    		if self.enable:
    			game.is_play = False
    			if game.winner is None:
    				game.winner = game.map.reverseTurn(game.player)
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
    			self.enable = False
    			return True
    		return False
    
    	def unclick(self):
    		if not self.enable:
    			self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
    			self.enable = True
    
    class Game():
    	def __init__(self, caption):
    		pygame.init()
    		self.screen = pygame.display.set_mode([SCREEN_WIDTH, SCREEN_HEIGHT])
    		pygame.display.set_caption(caption)
    		self.clock = pygame.time.Clock()
    		self.buttons = []
    		self.buttons.append(StartButton(self.screen, 'Start', MAP_WIDTH + 30, 15))
    		self.buttons.append(GiveupButton(self.screen, 'Giveup', MAP_WIDTH + 30, BUTTON_HEIGHT + 45))
    		self.is_play = False
    
    		self.map = Map(CHESS_LEN, CHESS_LEN)
    		self.player = MAP_ENTRY_TYPE.MAP_PLAYER_ONE
    		self.action = None
    		self.AI = ChessAI(CHESS_LEN)
    		self.useAI = False
    		self.winner = None
    	
    	def start(self):
    		self.is_play = True
    		self.player = MAP_ENTRY_TYPE.MAP_PLAYER_ONE
    		self.map.reset()
    
    	def play(self):
    		self.clock.tick(60)
    		
    		light_yellow = (247, 238, 214)
    		pygame.draw.rect(self.screen, light_yellow, pygame.Rect(0, 0, MAP_WIDTH, SCREEN_HEIGHT))
    		pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(MAP_WIDTH, 0, INFO_WIDTH, SCREEN_HEIGHT))
    		
    		for button in self.buttons:
    			button.draw()
    		
    		if self.is_play and not self.isOver():
    			if self.useAI:
    				x, y = self.AI.findBestChess(self.map.map, self.player)
    				self.checkClick(x, y, True)
    				self.useAI = False
    
    			if self.action is not None:
    				self.checkClick(self.action[0], self.action[1])
    				self.action = None
    			
    			if not self.isOver():
    				self.changeMouseShow()
    			
    		if self.isOver():
    			self.showWinner()
    
    		self.map.drawBackground(self.screen)
    		self.map.drawChess(self.screen)
    
    	
    	def changeMouseShow(self):
    		map_x, map_y = pygame.mouse.get_pos()
    		x, y = self.map.MapPosToIndex(map_x, map_y)
    		if self.map.isInMap(map_x, map_y) and self.map.isEmpty(x, y):
    			pygame.mouse.set_visible(False)
    			light_red = (213, 90, 107)
    			pos, radius = (map_x, map_y), CHESS_RADIUS
    			pygame.draw.circle(self.screen, light_red, pos, radius)
    		else:
    			pygame.mouse.set_visible(True)
    	
    	def checkClick(self,x, y, isAI=False):
    		self.map.click(x, y, self.player)
    		if self.AI.isWin(self.map.map, self.player):
    			self.winner = self.player
    			self.click_button(self.buttons[1])
    		else:	
    			self.player = self.map.reverseTurn(self.player)
    			if not isAI:	
    				self.useAI = True
    	
    	def mouseClick(self, map_x, map_y):
    		if self.is_play and self.map.isInMap(map_x, map_y) and not self.isOver():
    			x, y = self.map.MapPosToIndex(map_x, map_y)
    			if self.map.isEmpty(x, y):
    				self.action = (x, y)
    	
    	def isOver(self):
    		return self.winner is not None
    
    	def showWinner(self):
    		def showFont(screen, text, location_x, locaiton_y, height):
    			font = pygame.font.SysFont(None, height)
    			font_image = font.render(text, True, (0, 0, 255), (255, 255, 255))
    			font_image_rect = font_image.get_rect()
    			font_image_rect.x = location_x
    			font_image_rect.y = locaiton_y
    			screen.blit(font_image, font_image_rect)
    		if self.winner == MAP_ENTRY_TYPE.MAP_PLAYER_ONE:
    			str = 'Winner is White'
    		else:
    			str = 'Winner is Black'
    		showFont(self.screen, str, MAP_WIDTH + 25, SCREEN_HEIGHT - 60, 30)
    		pygame.mouse.set_visible(True)
    	
    	def click_button(self, button):
    		if button.click(self):
    			for tmp in self.buttons:
    				if tmp != button:
    					tmp.unclick()
    					
    	def check_buttons(self, mouse_x, mouse_y):
    		for button in self.buttons:
    			if button.rect.collidepoint(mouse_x, mouse_y):
    				self.click_button(button)
    				break
    			
    game = Game("FIVE CHESS " + GAME_VERSION)
    while True:
    	game.play()
    	pygame.display.update()
    	
    	for event in pygame.event.get():
    		if event.type == pygame.QUIT:
    			pygame.quit()
    			exit()
    		elif event.type == pygame.MOUSEBUTTONDOWN:
    			mouse_x, mouse_y = pygame.mouse.get_pos()
    			game.mouseClick(mouse_x, mouse_y)
    			game.check_buttons(mouse_x, mouse_y)
    

    GameMap.py

    这个文件没有修改,可以直接使用上一篇中的代码。

    ChessAI.py

    AI实现的代码,这个AI目前很简单,只会思考一步,后续会增加思考的步数,比如2步或4步。

    from GameMap import *
    from enum import IntEnum
    import copy
    import time
    
    class CHESS_TYPE(IntEnum):
    	NONE = 0,
    	SLEEP_TWO = 1,
    	LIVE_TWO = 2,
    	SLEEP_THREE = 3
    	LIVE_THREE = 4,
    	CHONG_FOUR = 5,
    	LIVE_FOUR = 6,
    	LIVE_FIVE = 7,
    	
    CHESS_TYPE_NUM = 8
    
    FIVE = CHESS_TYPE.LIVE_FIVE.value
    FOUR, THREE, TWO = CHESS_TYPE.LIVE_FOUR.value, CHESS_TYPE.LIVE_THREE.value, CHESS_TYPE.LIVE_TWO.value
    SFOUR, STHREE, STWO = CHESS_TYPE.CHONG_FOUR.value, CHESS_TYPE.SLEEP_THREE.value, CHESS_TYPE.SLEEP_TWO.value
    			
    class ChessAI():
    	def __init__(self, chess_len):
    		self.len = chess_len
    		# [horizon, vertical, left diagonal, right diagonal]
    		self.record = [[[0,0,0,0] for x in range(chess_len)] for y in range(chess_len)]
    		self.count = [[0 for x in range(CHESS_TYPE_NUM)] for i in range(2)]
    		self.pos_score = [[(7 - max(abs(x - 7), abs(y - 7))) for x in range(chess_len)] for y in range(chess_len)]
    		
    	def reset(self):
    		for y in range(self.len):
    			for x in range(self.len):
    				for i in range(4):
    					self.record[y][x][i] = 0
    
    		for i in range(len(self.count)):
    			for j in range(len(self.count[0])):
    				self.count[i][j] = 0
    		
    		self.save_count = 0
    	
    	def isWin(self, board, turn):
    		return self.evaluate(board, turn, True)
    	
    	# get all positions that is empty
    	def genmove(self, board, turn):
    		moves = []
    		for y in range(self.len):
    			for x in range(self.len):
    				if board[y][x] == 0:
    					score = self.pos_score[y][x]
    					moves.append((score, x, y))
    
    		moves.sort(reverse=True)
    		return moves
    
    	def search(self, board, turn):
    		moves = self.genmove(board, turn)
    		bestmove = None
    		max_score = -0x7fffffff
    		for score, x, y in moves:
    			board[y][x] = turn.value
    			score = self.evaluate(board, turn)
    			board[y][x] = 0
    			
    			if score > max_score:
    				max_score = score
    				bestmove = (max_score, x, y)
    		return bestmove
    		
    	def findBestChess(self, board, turn):
    		time1 = time.time()
    		score, x, y = self.search(board, turn)
    		time2 = time.time()
    		print('time[%f] (%d, %d), score[%d] save[%d]' % ((time2-time1), x, y, score, self.save_count))
    		return (x, y)
    	
    	# calculate score, FIXME: May Be Improved
    	def getScore(self, mine_count, opponent_count):
    		mscore, oscore = 0, 0
    		if mine_count[FIVE] > 0:
    			return (10000, 0)
    		if opponent_count[FIVE] > 0:
    			return (0, 10000)
    				
    		if mine_count[SFOUR] >= 2:
    			mine_count[FOUR] += 1
    			
    		if opponent_count[FOUR] > 0:
    			return (0, 9050)
    		if opponent_count[SFOUR] > 0:
    			return (0, 9040)
    		
    		if mine_count[FOUR] > 0:
    			return (9030, 0)
    		if mine_count[SFOUR] > 0 and mine_count[THREE] > 0:
    			return (9020, 0)
    			
    		if opponent_count[THREE] > 0 and mine_count[SFOUR] == 0:
    			return (0, 9010)
    			
    		if (mine_count[THREE] > 1 and opponent_count[THREE] == 0 and opponent_count[STHREE] == 0):
    			return (9000, 0)
    		
    		if mine_count[SFOUR] > 0:
    			mscore += 2000
    
    		if mine_count[THREE] > 1:
    			mscore += 500
    		elif mine_count[THREE] > 0:
    			mscore += 100
    			
    		if opponent_count[THREE] > 1:
    			oscore += 2000
    		elif opponent_count[THREE] > 0:
    			oscore += 400
    
    		if mine_count[STHREE] > 0:
    			mscore += mine_count[STHREE] * 10
    		if opponent_count[STHREE] > 0:
    			oscore += opponent_count[STHREE] * 10
    			
    		if mine_count[TWO] > 0:
    			mscore += mine_count[TWO] * 4
    		if opponent_count[TWO] > 0:
    			oscore += opponent_count[TWO] * 4
    				
    		if mine_count[STWO] > 0:
    			mscore += mine_count[STWO] * 4
    		if opponent_count[STWO] > 0:
    			oscore += opponent_count[STWO] * 4
    		
    		return (mscore, oscore)
    
    	def evaluate(self, board, turn, checkWin=False):
    		self.reset()
    		
    		if turn == MAP_ENTRY_TYPE.MAP_PLAYER_ONE:
    			mine = 1
    			opponent = 2
    		else:
    			mine = 2
    			opponent = 1
    		
    		for y in range(self.len):
    			for x in range(self.len):
    				if board[y][x] == mine:
    					self.evaluatePoint(board, x, y, mine, opponent)
    				elif board[y][x] == opponent:
    					self.evaluatePoint(board, x, y, opponent, mine)
    		
    		mine_count = self.count[mine-1]
    		opponent_count = self.count[opponent-1]
    		if checkWin:
    			return mine_count[FIVE] > 0
    		else:	
    			mscore, oscore = self.getScore(mine_count, opponent_count)
    			return (mscore - oscore)
    	
    	def evaluatePoint(self, board, x, y, mine, opponent):
    		dir_offset = [(1, 0), (0, 1), (1, 1), (1, -1)] # direction from left to right
    		for i in range(4):
    			if self.record[y][x][i] == 0:
    				self.analysisLine(board, x, y, i, dir_offset[i], mine, opponent, self.count[mine-1])
    			else:
    				self.save_count += 1
    	
    	# line is fixed len 9: XXXXMXXXX
    	def getLine(self, board, x, y, dir_offset, mine, opponent):
    		line = [0 for i in range(9)]
    		
    		tmp_x = x + (-5 * dir_offset[0])
    		tmp_y = y + (-5 * dir_offset[1])
    		for i in range(9):
    			tmp_x += dir_offset[0]
    			tmp_y += dir_offset[1]
    			if (tmp_x < 0 or tmp_x >= self.len or 
    				tmp_y < 0 or tmp_y >= self.len):
    				line[i] = opponent # set out of range as opponent chess
    			else:
    				line[i] = board[tmp_y][tmp_x]
    						
    		return line
    		
    	def analysisLine(self, board, x, y, dir_index, dir, mine, opponent, count):
    		def setRecord(self, x, y, left, right, dir_index, dir_offset):
    			tmp_x = x + (-5 + left) * dir_offset[0]
    			tmp_y = y + (-5 + left) * dir_offset[1]
    			for i in range(left, right):
    				tmp_x += dir_offset[0]
    				tmp_y += dir_offset[1]
    				self.record[tmp_y][tmp_x][dir_index] = 1
    	
    		empty = MAP_ENTRY_TYPE.MAP_EMPTY.value
    		left_idx, right_idx = 4, 4
    		
    		line = self.getLine(board, x, y, dir, mine, opponent)
    
    		while right_idx < 8:
    			if line[right_idx+1] != mine:
    				break
    			right_idx += 1
    		while left_idx > 0:
    			if line[left_idx-1] != mine:
    				break
    			left_idx -= 1
    		
    		left_range, right_range = left_idx, right_idx
    		while right_range < 8:
    			if line[right_range+1] == opponent:
    				break
    			right_range += 1
    		while left_range > 0:
    			if line[left_range-1] == opponent:
    				break
    			left_range -= 1
    		
    		chess_range = right_range - left_range + 1
    		if chess_range < 5:
    			setRecord(self, x, y, left_range, right_range, dir_index, dir)
    			return CHESS_TYPE.NONE
    		
    		setRecord(self, x, y, left_idx, right_idx, dir_index, dir)
    		
    		m_range = right_idx - left_idx + 1
    		
    		# M:mine chess, P:opponent chess or out of range, X: empty
    		if m_range == 5:
    			count[FIVE] += 1
    		
    		# Live Four : XMMMMX 
    		# Chong Four : XMMMMP, PMMMMX
    		if m_range == 4:
    			left_empty = right_empty = False
    			if line[left_idx-1] == empty:
    				left_empty = True			
    			if line[right_idx+1] == empty:
    				right_empty = True
    			if left_empty and right_empty:
    				count[FOUR] += 1
    			elif left_empty or right_empty:
    				count[SFOUR] += 1
    		
    		# Chong Four : MXMMM, MMMXM, the two types can both exist
    		# Live Three : XMMMXX, XXMMMX
    		# Sleep Three : PMMMX, XMMMP, PXMMMXP
    		if m_range == 3:
    			left_empty = right_empty = False
    			left_four = right_four = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine: # MXMMM
    					setRecord(self, x, y, left_idx-2, left_idx-1, dir_index, dir)
    					count[SFOUR] += 1
    					left_four = True
    				left_empty = True
    				
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine: # MMMXM
    					setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    					count[SFOUR] += 1
    					right_four = True 
    				right_empty = True
    			
    			if left_four or right_four:
    				pass
    			elif left_empty and right_empty:
    				if chess_range > 5: # XMMMXX, XXMMMX
    					count[THREE] += 1
    				else: # PXMMMXP
    					count[STHREE] += 1
    			elif left_empty or right_empty: # PMMMX, XMMMP
    				count[STHREE] += 1
    		
    		# Chong Four: MMXMM, only check right direction
    		# Live Three: XMXMMX, XMMXMX the two types can both exist
    		# Sleep Three: PMXMMX, XMXMMP, PMMXMX, XMMXMP
    		# Live Two: XMMX
    		# Sleep Two: PMMX, XMMP
    		if m_range == 2:
    			left_empty = right_empty = False
    			left_three = right_three = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine:
    					setRecord(self, x, y, left_idx-2, left_idx-1, dir_index, dir)
    					if line[left_idx-3] == empty:
    						if line[right_idx+1] == empty: # XMXMMX
    							count[THREE] += 1
    						else: # XMXMMP
    							count[STHREE] += 1
    						left_three = True
    					elif line[left_idx-3] == opponent: # PMXMMX
    						if line[right_idx+1] == empty:
    							count[STHREE] += 1
    							left_three = True
    						
    				left_empty = True
    				
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine:
    					if line[right_idx+3] == mine:  # MMXMM
    						setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    						count[SFOUR] += 1
    						right_three = True
    					elif line[right_idx+3] == empty:
    						#setRecord(self, x, y, right_idx+1, right_idx+2, dir_index, dir)
    						if left_empty:  # XMMXMX
    							count[THREE] += 1
    						else:  # PMMXMX
    							count[STHREE] += 1
    						right_three = True
    					elif left_empty: # XMMXMP
    						count[STHREE] += 1
    						right_three = True
    						
    				right_empty = True
    			
    			if left_three or right_three:
    				pass
    			elif left_empty and right_empty: # XMMX
    				count[TWO] += 1
    			elif left_empty or right_empty: # PMMX, XMMP
    				count[STWO] += 1
    		
    		# Live Two: XMXMX, XMXXMX only check right direction
    		# Sleep Two: PMXMX, XMXMP
    		if m_range == 1:
    			left_empty = right_empty = False
    			if line[left_idx-1] == empty:
    				if line[left_idx-2] == mine:
    					if line[left_idx-3] == empty:
    						if line[right_idx+1] == opponent: # XMXMP
    							count[STWO] += 1
    				left_empty = True
    
    			if line[right_idx+1] == empty:
    				if line[right_idx+2] == mine:
    					if line[right_idx+3] == empty:
    						if left_empty: # XMXMX
    							#setRecord(self, x, y, left_idx, right_idx+2, dir_index, dir)
    							count[TWO] += 1
    						else: # PMXMX
    							count[STWO] += 1
    				elif line[right_idx+2] == empty:
    					if line[right_idx+3] == mine and line[right_idx+4] == empty: # XMXXMX
    						count[TWO] += 1
    						
    		return CHESS_TYPE.NONE
    
    展开全文
  • 五子棋评分表算法

    千次阅读 2016-09-21 09:43:18
    评分表算法一般情况下棋盘是15*15的。那么应该是572个五元组。而这五元组由黑棋白棋还有空格组成。也就是 3^5 * 572 = 138996; 看着很多吧,其实是可以分类的。每个五元组就是一种局势。我们只用统计只有己方棋子的...

    评分表算法

    一般情况下棋盘是15*15的。那么应该是572个五元组。而这五元组由黑棋白棋还有空格组成。也就是 3^5 * 572 = 138996; 看着很多吧,其实是可以分类的。每个五元组就是一种局势。

    我们只用统计只有己方棋子的局势,以及只有敌方棋子的局势,针对这两种局势给予确定的评分,而其他局势直接评分为0。

    下面是我使用的是评分表

    // tuple is empty    
    Blank,    
    // tuple contains a black chess    
    B,    
    // tuple contains two black chesses    
    BB,    
    // tuple contains three black chesses    
    BBB,    
    // tuple contains four black chesses    
    BBBB,    
    // tuple contains a white chess    
    W,    
    // tuple contains two white chesses    
    WW,    
    // tuple contains three white chesses    
    WWW,    
    // tuple contains four white chesses    
    WWWW,    
    // tuple does not exist    
    Virtual,    
    // tuple contains at least one black and at least one white    
    Polluted    
    
    
    tupleScoreTable[0] = 7;    
    tupleScoreTable[1] = 35;    
    tupleScoreTable[2] = 800;    
    tupleScoreTable[3] = 15000;    
    tupleScoreTable[4] = 800000;    
    tupleScoreTable[5] = 15;    
    tupleScoreTable[6] = 400;    
    tupleScoreTable[7] = 1800;    
    tupleScoreTable[8] = 100000;    
    tupleScoreTable[9] = 0;    
    tupleScoreTable[10] = 0;    

    很多人可能没看懂,其实就是当前五元组中黑白棋子个数以及对应的得分,tupleScoreTable[0] 对应 没有棋子的得分,tupleScoreTable[1 - 4] 代表 1个黑棋 到 4个黑棋的得分, tupleScoreTable[5 - 8]代表 1 个白棋到4个白棋的得分,tupleScoreTable[9] 代表黑白棋共存的情况,tupleScoreTable[10] 代表其他情况(貌似没用?)

    当然具体怎么来统计那?onezeros的博客没有给出来,对很多初学者来说实现还是有一定困难,这里就详细的讲一讲吧。

    首先我们用一个棋盘大小的二维数组来储存每个点的得分。int scoreTabel[15][15];
    每次,当People或者Computer下完棋后,以该点为中心统计周围5个点的分数(上下左右对角)。
    这里写图片描述

    即红点,统计每个点的分数。

    对于每个红点,同样的,我们向其周围8个方向找到所有包括其自身的五元组,并统计白棋和黑棋的个数,从而根据上面的评分表进行加分。

    举个例子
    这里写图片描述

    对于1号位置,我们先统计以它为底端的五元组,即第一个矩形,然后是第二个,第三个,一直到以1号位置为顶端的五元组,这样我们就统计了y轴方向的分数。

    同理,接着统计x轴方向,两种对角线方向的五元组的得分,这样1号点位置的得分就出来了,我们将这个值赋值在对应的scoreTable[][]里面;

    对图1中所有红点的得分统计完成后,我们就可以遍历整个得分表scoreTable[][],其中的分最高而且为空的位置就是计算机下子的位置。

    当然,这种方法思路和编程都很简单,自然效果比不上正规军中极大极小值搜索中应用alpha-beta剪枝这种方法。

    展开全文
  • 之前搞了个五子棋的算法,我在写好算法之前参考网上很多的五子棋算法像是博弈树算法这些的一开始写起来不太好理解,今天就分享一个简单的五子棋算法——评分法。 评分法 原理: 评分法,有的也叫评分表法,不管叫...

    之前搞了个五子棋的算法,我在写好算法之前参考网上很多的五子棋算法像是博弈树算法这些的一开始写起来不太好理解,今天就分享一个简单的五子棋算法——评分法。

    评分法

    原理:
    评分法,有的也叫评分表法,不管叫什么,名字不重要,重要的是思路。我们写棋总是下在对自己优势最大的位置上,但是可以下的位置有很多啊,电脑怎么知道下在哪里最好啊?评分法就是给每个可下的位置评分,电脑通过分数高低来判断下在哪个位置。

    方法:
    评分法的原理是一致的,但方法各种各样,我的方法是这样的(仅供参考)。
    1、棋型分析
    五子棋的棋型很多很多,在这里不详细介绍,只介绍一些简单的棋型。
    连五棋型:有五个相同颜色的棋子连成一线;
    活四棋型:有四个相同颜色的棋子连成一线且两端均可下棋;
    眠四棋型:有四个相同颜色的棋子连成一线但两端只有一端可下棋;
    类似的还有活三、眠三、活二等棋型。
    下图,便是我以1为黑棋,2为白棋,0为空位置排列组合所得到的棋型示例,
    图一是以所选位置为中心判断的,
    图二则是以所选位置为起点判断的,
    我们可以把这些棋型分别用一个二维数组来保存,方便与所选位置可组成的棋型比较。
    在这里插入图片描述
    图一
    在这里插入图片描述
    图二
    2、根据棋型打分(可以自己设置评分标准)
    在这里插入图片描述
    算法代码怎么写:
    每次下棋前扫描棋盘上所有可以下的位置,把它们保存起来,然后用循环得出每一个位置八个方向可组成的棋型,再根据评分标准给位置评分,再判断最高的评分(要考虑有多个的情况)。

    步骤

    1、创建position类作为下棋的位置,代码如下:

    package chess;
    
    public class position {
    
    	private int x;//棋子位置横坐标
    	private int y;//棋子位置纵坐标
    	//构造方法
    	public position(int x,int y) {
    		this.x = x;
    		this.y = y;
    	}
    	//通过set和get方法赋值和访问
    	public void setX(int x) {
    		this.x = x;
    	}
    	public int getX() {
    		return x;
    	}
    	public void setY(int y) {
    		this.y = y;
    	}
    	public int getY() {
    		return y;
    	}
    }
    
    

    2、创建chess类
    这里是核心,其中包含五子棋游戏初始化、下棋、棋盘情况判断、评分算法等方法,详情见下面代码:

    package chess;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    
    public class chess {
      
    	private int size;//棋盘大小
    	private int[][] chessboard;//棋盘
    	private int color=1;//1为黑子,2为白子
    	/*
    	 * 以所选位置为中心的各种棋型,有连5,活4,眠4,活3,眠3,活2,眠2;
    	 * 连5:1111,2222;
    	 * 活4:1110,0111,2220,0222; 
    	 * 眠4:1112,2111,2221,1222; 
    	 * 活3:1100,0011,0110,2200,0022,0220;
    	 * 眠3:1120,1121,0211,1211,2110,0112,2210,2211,0122,2122,1220,0221;
    	 * 活2:0100,0010,0200,0020; 
    	 * 眠2:2100,0012,1200,0021;
    	 */
    	private int[][] stiuation = { { 1, 1, 1, 1 }, { 2, 2, 2, 2 }, { 1, 1, 1, 0 }, { 0, 1, 1, 1 }, { 2, 2, 2, 0 },
    			{ 0, 2, 2, 2 }, { 1, 1, 1, 2 }, { 2, 1, 1, 1 }, { 2, 2, 2, 1 }, { 1, 2, 2, 2 }, { 1, 1, 0, 0 },
    			{ 0, 0, 1, 1 }, { 0, 1, 1, 0 }, { 2, 2, 0, 0 }, { 0, 0, 2, 2 }, { 0, 2, 2, 0 }, { 1, 1, 2, 0 },
    			{ 1, 1, 2, 1 }, { 0, 2, 1, 1 }, { 1, 2, 1, 1 }, { 2, 1, 1, 0 }, { 0, 1, 1, 2 }, { 2, 2, 1, 0 },
    			{ 2, 2, 1, 1 }, { 0, 1, 2, 2 }, { 2, 1, 2, 2 }, { 1, 2, 2, 0 }, { 0, 2, 2, 1 }, { 0, 1, 0, 0 },
    			{ 0, 0, 1, 0 }, { 0, 2, 0, 0 }, { 0, 0, 2, 0 }, { 2, 1, 0, 0 }, { 0, 0, 1, 2 }, { 1, 2, 0, 0 },
    			{ 0, 0, 2, 1 } };
    	/*
    	 * 以所选位置为起点的棋型,连5,活4,眠4,活3,眠3; 
    	 * 连5:1111,2222; 
    	 * 活4:1110,2220; 
    	 * 眠4:1112,2221;
    	 * 活3:1100,1101,1102,2200,2201,2202; 
    	 * 眠3:1120,1121,1122,2210,2211,2122;
    	 */
    	private int[][] stiuation2 = { { 1, 1, 1, 1 }, { 2, 2, 2, 2 }, { 1, 1, 1, 0 }, { 2, 2, 2, 0 }, { 1, 1, 1, 2 },
    			{ 2, 2, 2, 1 }, { 1, 1, 0, 0 }, { 1, 1, 0, 1 }, { 1, 1, 0, 2 }, { 2, 2, 0, 0 }, { 2, 2, 0, 1 },
    			{ 2, 2, 0, 2 }, { 1, 1, 2, 0 }, { 1, 1, 2, 1 }, { 1, 1, 2, 2 }, { 2, 2, 1, 0 }, { 2, 2, 1, 1 },
    			{ 2, 2, 1, 2 } };
    
    	//设置游戏,size为棋盘大小,color为电脑所选择棋子的颜色
    	public void setGame(int size,int color) {
    		this.size=size;
    		this.color=color;
    		this.chessboard=new int[this.size][this.size];
    		//初始化棋盘
    		for (int i = 0; i < chessboard.length; i++) {
    			for (int j = 0; j < chessboard.length; j++) {
    				this.chessboard[i][j]=0;
    			}
    		}
    	}
    	//下棋,x和y分别是人下棋的横坐标和纵坐标,电脑根据坐标来判断下棋位置下棋
    	public void getMove(int x,int y) {
    		//人下棋
    		if(this.color==1)
    		this.chessboard[x][y]=2;
    		else
    		this.chessboard[x][y]=1;
    		System.out.println("人下棋:("+x+","+y+")");
    		print();
    		if(isWin(x,y)) {
    			System.out.println("人获胜");
    			System.exit(0);
    		}
    		else if(isFull()) {
    			System.out.println("平局");
    			System.exit(0);
    		}
    		//存储所有未下棋的位置
    		ArrayList<position> positions=new ArrayList<>();
    		//存储每个位置的评分
    		ArrayList<Integer> numbers=new ArrayList<>();
    		
    		for (int i = 0; i < chessboard.length; i++) {
    			for (int j = 0; j < chessboard.length; j++) {
    				positions.add(new position(i,j));
    				numbers.add(numJudge(i,j));
    			}
    		}
    		int index = 0;// 最高评分在numbers中的下标
    		for (int i = 0; i < numbers.size(); i++) {
    			if (numbers.get(i) > numbers.get(index)) {
    				index = i;
    			}
    		}
    		// 储存所有最高评分在numbers中的下标(当最高评分有多个时)
    		ArrayList<Integer> max = new ArrayList<>();
    		for (int i = 0; i < numbers.size(); i++) {
    			if (numbers.get(i) == numbers.get(index)) {
    				max.add(i);
    			}
    		}
    		//电脑下棋
    		if(max.size()==1){
    			System.out.println("电脑下棋:("+positions.get(max.get(0)).getX()+","+positions.get(max.get(0)).getY()+")");
    			if(this.color==1)
    			this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=1;
    			else
    			this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=2;
    			if(isWin(positions.get(max.get(0)).getX(), positions.get(max.get(0)).getY())) {
    				System.out.println("平局");
    				System.exit(0);
    			}
            }
    		else{
            	index = (int) (Math.random() * max.size());
            	System.out.println("电脑下棋:("+positions.get(max.get(index)).getX()+","+positions.get(max.get(index)).getY()+")");
            	if(this.color==1)
        		this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=1;
        		else
        		this.chessboard[positions.get(max.get(0)).getX()][positions.get(max.get(0)).getY()]=2;
        		if(isWin(positions.get(max.get(0)).getX(), positions.get(max.get(0)).getY())) {
    				System.out.println("电脑获胜");
    				System.exit(0);
    			}
    			else if(isFull()) {
    				System.out.println("平局");
    				System.exit(0);
    			}
            }
    		print();
    	}
        //评分算法
    	private int numJudge(int x, int y) {
    		int sum = 0;
    		sum += mark1(x, y);// 左右方向
    		sum += mark2(x, y);// 上下方向
    		sum += mark3(x, y);// 主对角线方向
    		sum += mark4(x, y);// 副对角线方向
    		sum += mark5(x, y);// 八个方向
    		return sum;
    	}
    	// 给分标准1,根据棋型给分
    		private int calculate(int[] position) {
    			int sum = 0;
    			int index = -1;
    			for (int i = 0; i < this.stiuation.length; i++) {
    				if (Arrays.equals(position, this.stiuation[i])) {
    					index = i;
    					if (i < 2) {
    						sum += 100000;// 连5棋型
    						if (color == 1) {
    							if (i == 0) {
    								sum += 10000000;// 我方胜利加分
    							}
    						} else {
    							if (i == 1) {
    								sum += 10000000;// 我方胜利加分
    							}
    						}
    					} else if (i < 6) {
    						sum += 10000;// 活4棋型
    						if(color==1){
    							if(i<=3){
    								sum+=50000;//我方先连4子加分
    							}
    						}else{
    							if(i>3){
    								sum+=50000;//我方先连4子加分
    							}
    						}
    					} else if (i < 10) {
    						sum += 5000;// 眠4棋型
    					} else if (i < 16) {
    						sum += 1000;// 活3棋型
    						if(color==1){
    							if(i<=12){
    								sum+=3000;//我方先连3子加分
    							}
    						}else{
    							if(i>12){
    								sum+=3000;//我方先连3子加分
    							}
    						}
    					} else if (i < 28) {
    						sum += 500;// 眠3棋型
    					} else if (i < 32) {
    						sum += 100;// 活2棋型
    					} else {
    						sum += 50;// 眠2棋型
    					}
    				}
    			}
    			if (index < 0) {
    				sum += 1;// 其它情况
    			}
    			return sum;
    		}
    		// 评分标准2
    			private int calculate2(int[] position) {
    				int sum = 0;
    				for (int i = 0; i < this.stiuation2.length; i++) {
    					if (Arrays.equals(position, this.stiuation2[i])) {
    						if (i < 2) {
    							
    							sum += 100000;// 连5棋型
    							if (color == 1) {
    								if (i == 0) {
    									sum += 10000000;// 一子定胜负加分
    								}
    							} else {
    								if (i == 1) {
    									sum += 10000000;// 一子定胜负加分
    								}
    							}
    						} else if (i < 4) {
    							sum += 10000;// 活4棋型
    							if(color==1){
    								if(i==2){
    									sum+=50000;//我方先连4子加分
    								}
    							}else{
    								if(i==3){
    									sum+=50000;//我方先连4子加分
    								}
    							}
    						} else if (i < 6) {
    							sum += 5000;// 眠4棋型
    						} else if (i < 12) {
    							sum += 1000;// 活3棋型
    							if(color==1){
    								if(i<=8){
    									sum+=3000;//我方先连3子加分
    								}
    							}else{
    								if(i>8){
    									sum+=3000;//我方先连3子加分
    								}
    							}
    						} else {
    							sum += 500;// 眠3棋型
    						}
    					}
    				}
    				return sum;
    			}
    
    		// 左右方向评分
    		private int mark1(int x, int y) {
    			int[] position = new int[4];
    			if (x - 2 >= 0) {
    				position[0] = this.chessboard[x - 2][y];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 1 >= 0) {
    				position[1] = this.chessboard[x - 1][y];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 1 < this.chessboard.length) {
    				position[2] = this.chessboard[x + 1][y];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 2 < this.chessboard.length) {
    				position[3] = this.chessboard[x + 2][y];
    			} else {
    				position[3] = 0;
    			}
    			int sum = 0;
    			sum += calculate(position);
    			return sum;
    		}
    
    		// 上下方向评分
    		private int mark2(int x, int y) {
    			int[] position = new int[4];
    			if (y - 2 >= 0) {
    				position[0] = this.chessboard[x][y - 2];
    			} else {
    				position[0] = 0;
    			}
    			if (y - 1 >= 0) {
    				position[1] = this.chessboard[x][y - 1];
    			} else {
    				position[1] = 0;
    			}
    			if (y + 1 < this.chessboard.length) {
    				position[2] = this.chessboard[x][y + 1];
    			} else {
    				position[2] = 0;
    			}
    			if (y + 2 < this.chessboard.length) {
    				position[3] = this.chessboard[x][y + 2];
    			} else {
    				position[3] = 0;
    			}
    			int sum = 0;
    			sum += calculate(position);
    			return sum;
    		}
    
    		// 主对角线方向评分
    		private int mark3(int x, int y) {
    			int[] position = new int[4];
    			if (x - 2 >= 0 && y - 2 >= 0) {
    				position[0] = this.chessboard[x - 2][y - 2];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 1 >= 0 && y - 1 >= 0) {
    				position[1] = this.chessboard[x - 1][y - 1];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 1 < this.chessboard.length && y + 1 < this.chessboard.length) {
    				position[2] = this.chessboard[x + 1][y + 1];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 2 < this.chessboard.length && y + 2 < this.chessboard.length) {
    				position[3] = this.chessboard[x + 2][y + 2];
    			} else {
    				position[3] = 0;
    			}
    			int sum = 0;
    			sum += calculate(position);
    			return sum;
    		}
    
    		// 副对角线方向评分
    		private int mark4(int x, int y) {
    			int[] position = new int[4];
    			if (x - 2 >= 0 && y + 2 < this.chessboard.length) {
    				position[0] = this.chessboard[x - 2][y + 2];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 1 >= 0 && y + 1 < this.chessboard.length) {
    				position[1] = this.chessboard[x - 1][y + 1];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 1 < this.chessboard.length && y - 1 >= 0) {
    				position[2] = this.chessboard[x + 1][y - 1];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 2 < this.chessboard.length && y - 2 >= 0) {
    				position[3] = this.chessboard[x + 2][y - 2];
    			} else {
    				position[3] = 0;
    			}
    			int sum = 0;
    			sum += calculate(position);
    			return sum;
    		}
    
    		
    		// 八个方向评分
    		private int mark5(int x, int y) {
    			int sum = 0;
    			int[] position = new int[4];
    			// 上方向
    			if (x - 1 >= 0) {
    				position[0] = this.chessboard[x - 1][y];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 2 >= 0) {
    				position[1] = this.chessboard[x - 2][y];
    			} else {
    				position[1] = 0;
    			}
    			if (x - 3 >= 0) {
    				position[2] = this.chessboard[x - 3][y];
    			} else {
    				position[2] = 0;
    			}
    			if (x - 4 >= 0) {
    				position[3] = this.chessboard[x - 4][y];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 下方向
    			if (x + 1 < this.chessboard.length) {
    				position[0] = this.chessboard[x + 1][y];
    			} else {
    				position[0] = 0;
    			}
    			if (x + 2 < this.chessboard.length) {
    				position[1] = this.chessboard[x + 2][y];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 3 < this.chessboard.length) {
    				position[2] = this.chessboard[x + 3][y];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 4 < this.chessboard.length) {
    				position[3] = this.chessboard[x + 4][y];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 左方向
    			if (y - 1 >= 0) {
    				position[0] = this.chessboard[x][y - 1];
    			} else {
    				position[0] = 0;
    			}
    			if (y - 2 >= 0) {
    				position[1] = this.chessboard[x][y - 2];
    			} else {
    				position[1] = 0;
    			}
    			if (y - 3 >= 0) {
    				position[2] = this.chessboard[x][y - 3];
    			} else {
    				position[2] = 0;
    			}
    			if (y - 4 >= 0) {
    				position[3] = this.chessboard[x][y - 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 右方向
    			if (y + 1 < this.chessboard.length) {
    				position[0] = this.chessboard[x][y + 1];
    			} else {
    				position[0] = 0;
    			}
    			if (y + 2 < this.chessboard.length) {
    				position[1] = this.chessboard[x][y + 2];
    			} else {
    				position[1] = 0;
    			}
    			if (y + 3 < this.chessboard.length) {
    				position[2] = this.chessboard[x][y + 3];
    			} else {
    				position[2] = 0;
    			}
    			if (y + 4 < this.chessboard.length) {
    				position[3] = this.chessboard[x][y + 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 左斜上方向
    			if (x - 1 >= 0 && y - 1 >= 0) {
    				position[0] = this.chessboard[x - 1][y - 1];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 2 >= 0 && y - 2 >= 0) {
    				position[1] = this.chessboard[x - 2][y - 2];
    			} else {
    				position[1] = 0;
    			}
    			if (x - 3 >= 0 && y - 3 >= 0) {
    				position[2] = this.chessboard[x - 3][y - 3];
    			} else {
    				position[2] = 0;
    			}
    			if (x - 4 >= 0 && y - 4 >= 0) {
    				position[3] = this.chessboard[x - 4][y - 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 左斜下方向
    			if (x + 1 < this.chessboard.length && y + 1 < this.chessboard.length) {
    				position[0] = this.chessboard[x + 1][y + 1];
    			} else {
    				position[0] = 0;
    			}
    			if (x + 2 < this.chessboard.length && y + 2 < this.chessboard.length) {
    				position[1] = this.chessboard[x + 2][y + 2];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 3 < this.chessboard.length && y + 3 < this.chessboard.length) {
    				position[2] = this.chessboard[x + 3][y + 3];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 4 < this.chessboard.length && y + 4 < this.chessboard.length) {
    				position[3] = this.chessboard[x + 4][y + 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 右斜上方向
    			if (x - 1 >= 0 && y + 1 < this.chessboard.length) {
    				position[0] = this.chessboard[x - 1][y + 1];
    			} else {
    				position[0] = 0;
    			}
    			if (x - 2 >= 0 && y + 2 < this.chessboard.length) {
    				position[1] = this.chessboard[x - 2][y + 2];
    			} else {
    				position[1] = 0;
    			}
    			if (x - 3 >= 0 && y + 3 < this.chessboard.length) {
    				position[2] = this.chessboard[x - 3][y + 3];
    			} else {
    				position[2] = 0;
    			}
    			if (x - 4 >= 0 && y + 4 < this.chessboard.length) {
    				position[3] = this.chessboard[x - 4][y + 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			// 右斜下
    			if (x + 1 < this.chessboard.length && y - 1 >= 0) {
    				position[0] = this.chessboard[x + 1][y - 1];
    			} else {
    				position[0] = 0;
    			}
    			if (x + 2 < this.chessboard.length && y - 2 >= 0) {
    				position[1] = this.chessboard[x + 2][y - 2];
    			} else {
    				position[1] = 0;
    			}
    			if (x + 3 < this.chessboard.length && y - 3 >= 0) {
    				position[2] = this.chessboard[x + 3][y - 3];
    			} else {
    				position[2] = 0;
    			}
    			if (x + 4 < this.chessboard.length && y - 4 >= 0) {
    				position[3] = this.chessboard[x + 4][y - 4];
    			} else {
    				position[3] = 0;
    			}
    			sum += calculate2(position);
    			return sum;
    		}
    	//打印棋盘
    	public void print() {
    	
    		for (int i = 0; i < chessboard.length; i++) {
    			for (int j = 0; j < chessboard.length; j++) {
    				System.out.print(this.chessboard[i][j]+"  ");
    			}
    			System.out.println();
    		}
    	}
    	//判断输赢
    	public Boolean isWin(int x,int y) {
    		return false;
    	}
    	
    	//判断棋盘是否满了
    	public Boolean isFull() {
    		int judge=1;
    		for (int i = 0; i < chessboard.length; i++) {
    			for (int j = 0; j < chessboard.length; j++) {
    				if(this.chessboard[i][j]==0) {
    					judge=0;
    				}
    			}
    		}
    		return judge==1?true:false;
    		
    	}
    }
    
    

    3、创建测试类,代码如下:

    package chess;
    
    import java.util.Scanner;
    
    public class chessTest {
    
    	public static void main(String[] args) {
    		Scanner sc=new Scanner(System.in);
    		chess ch=new chess();
    		System.out.println("请输入棋盘大小n(n行n列):");
    		int size=sc.nextInt();
    	    ch.setGame(size,2);
    	    ch.print();
            while (true) {
    			System.out.println("请输入你想下的位置");
    			int x=sc.nextInt();
    			int y=sc.nextInt();
    			ch.getMove(x, y);
    		}
    	}
    
    }
    
    

    测试结果图:
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
    算法大概就是这样子了,算法很简单,效果差强人意吧。
    PS:后面发现chess类里面有个判断胜负的方法没写,也懒得补上了(就循环判断,很简单,就是耗点时间),希望大家不会介意。

    展开全文
  • 五子棋

    2021-04-14 16:50:50
    **###简单的五子棋代码** ```python from enum import IntEnum import pygame from pygame.locals import * import copy import time version = 'FiveChessV2.0 author:ZeHao Cao most_recent_updated:2020/6/4 21:...
    **###简单的五子棋代码**
    ```python
    from enum import IntEnum
    import pygame
    from pygame.locals import *
    import copy
    import time
    
    version = 'FiveChessV2.0  author:ZeHao Cao  most_recent_updated:2020/6/4 21:24'
    
    # 基础参数设置
    square_size = 40  # 单格的宽度(不是格数!是为了方便绘制棋盘用的变量
    chess_size = square_size // 2 - 2  # 棋子大小
    web_broad = 17  # 棋盘格数+1(nxn)
    map_w = web_broad * square_size  # 棋盘长度
    map_h = web_broad * square_size  # 棋盘高度
    info_w = 60  # 按钮界面宽度
    button_w = 140  # 按钮长宽
    button_h = 50
    screen_w = map_w   # 总窗口长宽
    screen_h = map_h + info_w
    
    
    # 地图绘制模块
    
    class MAP_ENUM(IntEnum):  # 用数字表示当前格的情况
        be_empty = 0,  # 无人下
        player1 = 1,  # 玩家一,执白
        player2 = 2,  # 玩家二,执黑
        out_of_range = 3,  # 出界
    
    
    class Map:  # 地图类
        def __init__(self, width, height):  # 构造函数
            self.width = width
            self.height = height
            self.map = [[0 for x in range(self.width)] for y in range(self.height)]  # 存储棋盘的二维数组
            self.steps = []  # 记录步骤先后
    
        def get_init(self):  # 重置棋盘
            for y in range(self.height):
                for x in range(self.width):
                    self.map[y][x] = 0
            self.steps = []
    
        def intoNextTurn(self, turn):  # 进入下一回合,交换下棋人
            if turn == MAP_ENUM.player1:
                return MAP_ENUM.player2
            else:
                return MAP_ENUM.player1
    
        def getLocate(self, x, y):  # 输入下标,返回具体位置
            map_x = x * square_size
            map_y = y * square_size
            return (map_x, map_y, square_size, square_size)  # 返回位置信息
    
        def getIndex(self, map_x, map_y):  # 输入具体位置,返回下标
            x = map_x // square_size
            y = map_y // square_size
            return (x, y)
    
        def isInside(self, map_x, map_y):  # 是否在有效范围内
            if (map_x <= 0 or map_x >= map_w or
                    map_y <= 0 or map_y >= map_h):
                return False
            return True
    
        def isEmpty(self, x, y):  # 当前格子是否已经有棋子
            return (self.map[y][x] == 0)
    
        def click(self, x, y, type):  # 点击的下棋动作
            self.map[y][x] = type.value  # 下棋
            self.steps.append((x, y))  # 记录步骤信息
    
        def printChessPiece(self, screen):  # 绘制棋子
            player_one = (255, 245, 238)  # 象牙白
            player_two = (41, 36, 33)  # 烟灰
            player_color = [player_one, player_two]
            for i in range(len(self.steps)):
                x, y = self.steps[i]
                map_x, map_y, width, height = self.getLocate(x, y)
                pos, radius = (map_x + width // 2, map_y + height // 2), chess_size
                turn = self.map[y][x]
                pygame.draw.circle(screen, player_color[turn - 1], pos, radius)  # 画
    
        def drawBoard(self, screen):  # 画棋盘
            color = (0, 0, 0)  # 线色
            for y in range(self.height):
                # 画横着的棋盘线
                start_pos, end_pos = (square_size // 2, square_size // 2 + square_size * y), (
                    map_w - square_size // 2, square_size // 2 + square_size * y)
                pygame.draw.line(screen, color, start_pos, end_pos, 1)
            for x in range(self.width):
                # 画竖着的棋盘线
                start_pos, end_pos = (square_size // 2 + square_size * x, square_size // 2), (
                    square_size // 2 + square_size * x, map_h - square_size // 2)
                pygame.draw.line(screen, color, start_pos, end_pos, 1)
    
    
    # 高级AI模块(参考文献)
    
    class SITUATION(IntEnum):  # 棋型
        NONE = 0,  # 无
        SLEEP_TWO = 1,  # 眠二
        LIVE_TWO = 2,  # 活二
        SLEEP_THREE = 3,  # 眠三
        LIVE_THREE = 4,  # 活三
        CHONG_FOUR = 5,  # 冲四
        LIVE_FOUR = 6,  # 活四
        LIVE_FIVE = 7,  # 活五
    
    
    SITUATION_NUM = 8  # 长度
    
    # 方便后续调用枚举内容
    FIVE = SITUATION.LIVE_FIVE.value
    FOUR, THREE, TWO = SITUATION.LIVE_FOUR.value, SITUATION.LIVE_THREE.value, SITUATION.LIVE_TWO.value
    SFOUR, STHREE, STWO = SITUATION.CHONG_FOUR.value, SITUATION.SLEEP_THREE.value, SITUATION.SLEEP_TWO.value
    
    
    class MyChessAI():
        def __init__(self, chess_len):  # 构造函数
            self.len = chess_len  # 当前棋盘大小
            # 二维数组,每一格存的是:横评分,纵评分,左斜评分,右斜评分
            self.record = [[[0, 0, 0, 0] for i in range(chess_len)] for j in range(chess_len)]
            # 存储当前格具体棋型数量
            self.count = [[0 for i in range(SITUATION_NUM)] for j in range(2)]
            # 位置分(同条件下越靠近棋盘中央越高)
            self.position_isgreat = [[(web_broad - max(abs(i - web_broad / 2 + 1), abs(j - web_broad / 2 + 1 ))) for i in range(chess_len)]
                                     for j in range(chess_len)]
    
        def get_init(self):  # 初始化
            for i in range(self.len):
                for j in range(self.len):
                    for k in range(4):
                        self.record[i][j][k] = 0
            for i in range(len(self.count)):
                for j in range(len(self.count[0])):
                    self.count[i][j] = 0
            self.save_count = 0
    
        def isWin(self, board, turn):  # 当前人胜利
            return self.evaluate(board, turn, True)
    
        # 返回所有未下棋坐标(位置从好到坏)
        def genmove(self, board, turn):
            moves = []
            for y in range(self.len):
                for x in range(self.len):
                    if board[y][x] == 0:
                        score = self.position_isgreat[y][x]
                        moves.append((score, x, y))
            moves.sort(reverse=True)
            return moves
    
        # 返回当前最优解下标
        def search(self, board, turn):
            moves = self.genmove(board, turn)
            bestmove = None
            max_score = -99999  # 无穷小
            for score, x, y in moves:
                board[y][x] = turn.value
                score = self.evaluate(board, turn)
                board[y][x] = 0
                if score > max_score:
                    max_score = score
                    bestmove = (max_score, x, y)
            return bestmove
    
        # 主要用于测试的函数,现在已经没什么用
        def findBestChess(self, board, turn):
            # time1 = time.time()
            score, x, y = self.search(board, turn)
            # time2 = time.time()
            # print('time[%f] (%d, %d), score[%d] save[%d]' % ((time2 - time1), x, y, score, self.save_count))
            return (x, y)
    
        # 根据论文,得出一点的评分(核心代码
        # 直接列举所有棋型
        def getScore(self, mychess, yourchess):
            mscore, oscore = 0, 0
            if mychess[FIVE] > 0:
                return (10000, 0)
            if yourchess[FIVE] > 0:
                return (0, 10000)
            if mychess[SFOUR] >= 2:
                mychess[FOUR] += 1
            if yourchess[FOUR] > 0:
                return (0, 9050)
            if yourchess[SFOUR] > 0:
                return (0, 9040)
            if mychess[FOUR] > 0:
                return (9030, 0)
            if mychess[SFOUR] > 0 and mychess[THREE] > 0:
                return (9020, 0)
            if yourchess[THREE] > 0 and mychess[SFOUR] == 0:
                return (0, 9010)
            if (mychess[THREE] > 1 and yourchess[THREE] == 0 and yourchess[STHREE] == 0):
                return (9000, 0)
            if mychess[SFOUR] > 0:
                mscore += 2000
            if mychess[THREE] > 1:
                mscore += 500
            elif mychess[THREE] > 0:
                mscore += 100
            if yourchess[THREE] > 1:
                oscore += 2000
            elif yourchess[THREE] > 0:
                oscore += 400
            if mychess[STHREE] > 0:
                mscore += mychess[STHREE] * 10
            if yourchess[STHREE] > 0:
                oscore += yourchess[STHREE] * 10
            if mychess[TWO] > 0:
                mscore += mychess[TWO] * 4
            if yourchess[TWO] > 0:
                oscore += yourchess[TWO] * 4
            if mychess[STWO] > 0:
                mscore += mychess[STWO] * 4
            if yourchess[STWO] > 0:
                oscore += yourchess[STWO] * 4
            return (mscore, oscore)  # 自我辅助效果,counter对面效果
    
        # 对上述得分进行进一步处理
        def evaluate(self, board, turn, checkWin=False):
            self.get_init()
            if turn == MAP_ENUM.player1:
                me = 1
                you = 2
            else:
                me = 2
                you = 1
            for y in range(self.len):
                for x in range(self.len):
                    if board[y][x] == me:
                        self.evaluatePoint(board, x, y, me, you)
                    elif board[y][x] == you:
                        self.evaluatePoint(board, x, y, you, me)
            mychess = self.count[me - 1]
            yourchess = self.count[you - 1]
            if checkWin:
                return mychess[FIVE] > 0  # 检查是否已经胜利
            else:
                mscore, oscore = self.getScore(mychess, yourchess)
                return (mscore - oscore)  # 自我辅助效果,counter对面效果
    
        def evaluatePoint(self, board, x, y, me, you):
            direction = [(1, 0), (0, 1), (1, 1), (1, -1)]  # 四个方向
            for i in range(4):
                if self.record[y][x][i] == 0:
                    # 检查当前方向棋型
                    self.getBasicSituation(board, x, y, i, direction[i], me, you, self.count[me - 1])
                else:
                    self.save_count += 1
    
        # 把当前方向棋型存储下来,方便后续使用
        def getLine(self, board, x, y, direction, me, you):
            line = [0 for i in range(9)]
            # “光标”移到最左端
            tmp_x = x + (-5 * direction[0])
            tmp_y = y + (-5 * direction[1])
            for i in range(9):
                tmp_x += direction[0]
                tmp_y += direction[1]
                if (tmp_x < 0 or tmp_x >= self.len or tmp_y < 0 or tmp_y >= self.len):
                    line[i] = you  # 出界
                else:
                    line[i] = board[tmp_y][tmp_x]
            return line
    
        # 把当前方向的棋型识别成具体情况(如把MMMMX识别成冲四)
        def getBasicSituation(self, board, x, y, dir_index, dir, me, you, count):
            # record赋值
            def setRecord(self, x, y, left, right, dir_index, direction):
                tmp_x = x + (-5 + left) * direction[0]
                tmp_y = y + (-5 + left) * direction[1]
                for i in range(left, right):
                    tmp_x += direction[0]
                    tmp_y += direction[1]
                    self.record[tmp_y][tmp_x][dir_index] = 1
    
            empty = MAP_ENUM.be_empty.value
            left_index, right_index = 4, 4
            line = self.getLine(board, x, y, dir, me, you)
            while right_index < 8:
                if line[right_index + 1] != me:
                    break
                right_index += 1
            while left_index > 0:
                if line[left_index - 1] != me:
                    break
                left_index -= 1
            left_range, right_range = left_index, right_index
            while right_range < 8:
                if line[right_range + 1] == you:
                    break
                right_range += 1
            while left_range > 0:
                if line[left_range - 1] == you:
                    break
                left_range -= 1
            chess_range = right_range - left_range + 1
            if chess_range < 5:
                setRecord(self, x, y, left_range, right_range, dir_index, dir)
                return SITUATION.NONE
            setRecord(self, x, y, left_index, right_index, dir_index, dir)
            m_range = right_index - left_index + 1
            if m_range == 5:
                count[FIVE] += 1
            # 活四冲四
            if m_range == 4:
                left_empty = right_empty = False
                if line[left_index - 1] == empty:
                    left_empty = True
                if line[right_index + 1] == empty:
                    right_empty = True
                if left_empty and right_empty:
                    count[FOUR] += 1
                elif left_empty or right_empty:
                    count[SFOUR] += 1
            # 活三眠三
            if m_range == 3:
                left_empty = right_empty = False
                left_four = right_four = False
                if line[left_index - 1] == empty:
                    if line[left_index - 2] == me:  # MXMMM
                        setRecord(self, x, y, left_index - 2, left_index - 1, dir_index, dir)
                        count[SFOUR] += 1
                        left_four = True
                    left_empty = True
                if line[right_index + 1] == empty:
                    if line[right_index + 2] == me:  # MMMXM
                        setRecord(self, x, y, right_index + 1, right_index + 2, dir_index, dir)
                        count[SFOUR] += 1
                        right_four = True
                    right_empty = True
                if left_four or right_four:
                    pass
                elif left_empty and right_empty:
                    if chess_range > 5:  # XMMMXX, XXMMMX
                        count[THREE] += 1
                    else:  # PXMMMXP
                        count[STHREE] += 1
                elif left_empty or right_empty:  # PMMMX, XMMMP
                    count[STHREE] += 1
            # 活二眠二
            if m_range == 2:
                left_empty = right_empty = False
                left_three = right_three = False
                if line[left_index - 1] == empty:
                    if line[left_index - 2] == me:
                        setRecord(self, x, y, left_index - 2, left_index - 1, dir_index, dir)
                        if line[left_index - 3] == empty:
                            if line[right_index + 1] == empty:  # XMXMMX
                                count[THREE] += 1
                            else:  # XMXMMP
                                count[STHREE] += 1
                            left_three = True
                        elif line[left_index - 3] == you:  # PMXMMX
                            if line[right_index + 1] == empty:
                                count[STHREE] += 1
                                left_three = True
                    left_empty = True
                if line[right_index + 1] == empty:
                    if line[right_index + 2] == me:
                        if line[right_index + 3] == me:  # MMXMM
                            setRecord(self, x, y, right_index + 1, right_index + 2, dir_index, dir)
                            count[SFOUR] += 1
                            right_three = True
                        elif line[right_index + 3] == empty:
                            # setRecord(self, x, y, right_index+1, right_index+2, dir_index, dir)
                            if left_empty:  # XMMXMX
                                count[THREE] += 1
                            else:  # PMMXMX
                                count[STHREE] += 1
                            right_three = True
                        elif left_empty:  # XMMXMP
                            count[STHREE] += 1
                            right_three = True
                    right_empty = True
                if left_three or right_three:
                    pass
                elif left_empty and right_empty:  # XMMX
                    count[TWO] += 1
                elif left_empty or right_empty:  # PMMX, XMMP
                    count[STWO] += 1
            # 特殊活二眠二(有空格
            if m_range == 1:
                left_empty = right_empty = False
                if line[left_index - 1] == empty:
                    if line[left_index - 2] == me:
                        if line[left_index - 3] == empty:
                            if line[right_index + 1] == you:  # XMXMP
                                count[STWO] += 1
                    left_empty = True
                if line[right_index + 1] == empty:
                    if line[right_index + 2] == me:
                        if line[right_index + 3] == empty:
                            if left_empty:  # XMXMX
                                count[TWO] += 1
                            else:  # PMXMX
                                count[STWO] += 1
                    elif line[right_index + 2] == empty:
                        if line[right_index + 3] == me and line[right_index + 4] == empty:  # XMXXMX
                            count[TWO] += 1
            # 以上都不是则为none棋型
            return SITUATION.NONE
    
    
    # 主程序实现部分
    
    # 控制进程按钮类(父类)
    class Button:
        def __init__(self, screen, text, x, y, color, enable):  # 构造函数
            self.screen = screen
            self.width = button_w
            self.height = button_h
            self.button_color = color
            self.text_color = (255, 255, 255)  # 纯白
            self.enable = enable
            self.font = pygame.font.SysFont(None, button_h * 2 // 3)
            self.rect = pygame.Rect(0, 0, self.width, self.height)
            self.rect.topleft = (x, y)
            self.text = text
            self.init_msg()
    
        # 重写pygame内置函数,初始化我们的按钮
        def init_msg(self):
            if self.enable:
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
            else:
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
            self.msg_image_rect = self.msg_image.get_rect()
            self.msg_image_rect.center = self.rect.center
    
        # 根据按钮enable状态填色,具体颜色在后续子类控制
        def draw(self):
            if self.enable:
                self.screen.fill(self.button_color[0], self.rect)
            else:
                self.screen.fill(self.button_color[1], self.rect)
            self.screen.blit(self.msg_image, self.msg_image_rect)
    
    
    class WhiteStartButton(Button):  # 开始按钮(选白棋)
        def __init__(self, screen, text, x, y):  # 构造函数
            super().__init__(screen, text, x, y, [(26, 173, 25), (158, 217, 157)], True)
    
        def click(self, game):  # 点击,pygame内置方法
            if self.enable:  # 启动游戏并初始化,变换按钮颜色
                game.start()
                game.winner = None
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
                self.enable = False
                return True
            return False
    
        def unclick(self):  # 没点击时(pygame内置
            if not self.enable:
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
                self.enable = True
    
    
    class BlackStartButton(Button):  # 开始按钮(选黑棋)
        def __init__(self, screen, text, x, y):  # 构造函数
            super().__init__(screen, text, x, y, [(26, 173, 25), (158, 217, 157)], True)
    
        def click(self, game):  # 点击,pygame内置方法
            if self.enable:  # 启动游戏并初始化,变换按钮颜色,安排AI先手
                game.start()
                game.winner = None
                game.useAI = True
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
                self.enable = False
                return True
            return False
    
        def unclick(self):  # 没点击时(pygame内置
            if not self.enable:
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
                self.enable = True
    
    
    class GiveupButton(Button):  # 投降按钮(任何模式都能用
        def __init__(self, screen, text, x, y):
            super().__init__(screen, text, x, y, [(230, 67, 64), (236, 139, 137)], False)
    
        def click(self, game):  # 结束游戏,判断赢家
            if self.enable:
                game.is_play = False
                if game.winner is None:
                    game.winner = game.map.intoNextTurn(game.player)
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[1])
                self.enable = False
                return True
            return False
    
        def unclick(self):  # 保持不变,填充颜色
            if not self.enable:
                self.msg_image = self.font.render(self.text, True, self.text_color, self.button_color[0])
                self.enable = True
    
    
    class Game:  # pygame类,以下所有功能都是根据需要重写
        def __init__(self, caption):
            pygame.init()
            self.screen = pygame.display.set_mode([screen_w, screen_h])
            pygame.display.set_caption(caption)
            self.clock = pygame.time.Clock()
            self.buttons = []
            self.buttons.append(WhiteStartButton(self.screen, 'Pick White', 50, map_h))
            self.buttons.append(BlackStartButton(self.screen, 'Pick Black', 270 , map_h))
            self.buttons.append(GiveupButton(self.screen, 'Surrender', 490 , map_h))
            self.is_play = False
            self.map = Map(web_broad, web_broad)
            self.player = MAP_ENUM.player1
            self.action = None
            self.AI = MyChessAI(web_broad)
            self.useAI = False
            self.winner = None
    
        def start(self):
            self.is_play = True
            self.player = MAP_ENUM.player1  # 白棋先手
            self.map.get_init()
    
        def play(self):
            # 画底板
            self.clock.tick(60)
            wood_color = (210, 180, 140)
            pygame.draw.rect(self.screen, wood_color, pygame.Rect(0, 0, map_w, screen_h))
            pygame.draw.rect(self.screen, (255, 255, 255), pygame.Rect(map_w, 0, info_w, screen_h))
            # 画按钮
            for button in self.buttons:
                button.draw()
            if self.is_play and not self.isOver():
                if self.useAI:
                    x, y = self.AI.findBestChess(self.map.map, self.player)
                    self.checkClick(x, y, True)
                    self.useAI = False
                if self.action is not None:
                    self.checkClick(self.action[0], self.action[1])
                    self.action = None
                if not self.isOver():
                    self.changeMouseShow()
            if self.isOver():
                self.showWinner()
                # self.buttons[0].enable = True
                # self.buttons[1].enable = True
                # self.buttons[2].enable = False
            self.map.drawBoard(self.screen)
            self.map.printChessPiece(self.screen)
    
        def changeMouseShow(self):  # 开始游戏的时候把鼠标预览切换成预览棋子的样子
            map_x, map_y = pygame.mouse.get_pos()
            x, y = self.map.getIndex(map_x, map_y)
            if self.map.isInside(map_x, map_y) and self.map.isEmpty(x, y):  # 在棋盘内且当前无棋子
                pygame.mouse.set_visible(False)
                smoke_blue = (176, 224, 230)
                pos, radius = (map_x, map_y), chess_size
                pygame.draw.circle(self.screen, smoke_blue, pos, radius)
            else:
                pygame.mouse.set_visible(True)
    
        def checkClick(self, x, y, isAI=False):  # 后续处理
            self.map.click(x, y, self.player)
            if self.AI.isWin(self.map.map, self.player):
                self.winner = self.player
                self.click_button(self.buttons[2])
            else:
                self.player = self.map.intoNextTurn(self.player)
                if not isAI:
                    self.useAI = True
    
        def mouseClick(self, map_x, map_y):  # 处理下棋动作
            if self.is_play and self.map.isInside(map_x, map_y) and not self.isOver():
                x, y = self.map.getIndex(map_x, map_y)
                if self.map.isEmpty(x, y):
                    self.action = (x, y)
    
        def isOver(self):  # 中断条件
            return self.winner is not None
    
        def showWinner(self):  # 输出胜者
            def showFont(screen, text, location_x, locaiton_y, height):
                font = pygame.font.SysFont(None, height)
                font_image = font.render(text, True, (255, 215, 0), (255, 255, 255))  # 金黄色
                font_image_rect = font_image.get_rect()
                font_image_rect.x = location_x
                font_image_rect.y = locaiton_y
                screen.blit(font_image, font_image_rect)
            if self.winner == MAP_ENUM.player1:
                str = 'White Wins!'
            else:
                str = 'Black Wins!'
            showFont(self.screen, str, map_w / 5, screen_h / 8, 100)  # 居上中,字号100
            pygame.mouse.set_visible(True)
    
        def click_button(self, button):
            if button.click(self):
                for tmp in self.buttons:
                    if tmp != button:
                        tmp.unclick()
    
        def check_buttons(self, mouse_x, mouse_y):
            for button in self.buttons:
                if button.rect.collidepoint(mouse_x, mouse_y):
                    self.click_button(button)
                    break
    
    
    # 以下为pygame1.9帮助文档的示例代码
    if __name__ == '__main__':
        game = Game(version)
        while True:
            game.play()
            pygame.display.update()
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    exit()
                elif event.type == pygame.MOUSEBUTTONDOWN:
                    mouse_x, mouse_y = pygame.mouse.get_pos()
                    game.mouseClick(mouse_x, mouse_y)
                    game.check_buttons(mouse_x, mouse_y)
    
    
    
    
    
    展开全文
  • 迷茫五子棋1.1

    2006-05-25 00:00:00
    可设置棋型评分 设置搜索深度,步长 查看某点棋型, 查看某局面步法 观看电脑思考过程 如有bug,或建议,请联系我 Email: chenzhichao2008@yahoo.com.cn MSN: chenzhi_gyl2008@hotmail.com
  • Java学习笔记(七):五子棋AI算法

    千次阅读 2019-02-08 12:44:27
    一、五子棋棋型分析 参考:http://game.onegreen.net/wzq/HTML/142336.html 最常见的基本棋型大体有以下几种:连五,活四,冲四,活三,眠三,活二,眠二。 ①连五:顾名思义,五颗同色棋子连在一起,不需要多讲。 ...
  • 五子棋算法

    2011-09-07 15:37:23
    任何一种棋类游戏其关键是对当前棋局是否有正确的评分,评分越准确则电脑的AI越高。五子棋游戏也是如此,但在打分之前,我们先扫描 整个棋盘,把每个空位从八个方向上的棋型填入数组gStyle(2, 15, 15, 8, 2),其中第...
  • C++五子棋大作业,人机对战模式,打分机制。棋型判断方法:1、空位上添加要判断颜色的棋子;2、以空位为中心判断单个方向两边各四个未知信息;3、找出与中心点相连而成的连续子;4、下一步判断。
  • 任何一种棋类游戏其关键是对当前棋局是否有正确的评分,评分越准确则电脑的AI越高。五子棋游戏也是如此,但在打分之前,我们先扫描 整个棋盘,把每个空位从八个方向上的棋型填入数组gStyle(2, 15, 15, 8, 2),其中第...
  • 五子棋程序进展

    2020-09-16 23:30:50
    其实在写《下步计划》时已经将五子棋界面图片和画棋子问题解决了。 这段时间一直在查看自己的VB6五子棋源码,回忆原来的思路。这个程序放得时间很久了,我是知道的,但没想到竟有近20年了!单机版,功能还是不少的,...
  • 总的来说(我们假定您熟悉五子棋的基本规则),要让电脑知道该在哪一点下子,就要根据盘面的形势,为每一可能落子的点计算其重要程度,也就是当这子落下后会形成什么棋型(如:“冲四”、“活三”等),然后通览全盘...
  • 基于java的人机五子棋

    2019-09-15 08:05:12
    本项目要实现的是五子棋人机版,通过制定棋型评分表使机器能够对棋盘局势评估。五子棋玩家双方分别称为“人”、“机器” ,当人落子后,机器对棋盘扫描获取可行棋的位置集合,然后遍历该集合,利用评估函数对每个...
  • 五子棋人机对战设计

    2017-12-05 17:31:58
    人机对战属于一种弱人工智能算法,其核心是:当玩家落下一枚棋子后,计算出这枚棋子构成的所有棋型,找出威胁程度最大的棋型,并破解其产生的威胁。
  • 五子棋AI

    2019-06-16 19:18:20
    前言: 这是一次偶然与必然的相撞,在我学习了诸多算法之后,对于极大极小值搜索这个命题依然两眼一抹黑,那天在51nod上刷题映入眼帘第一题就是 [...在一个3*4的棋盘上下三...以上,就是一位拖延症患者手撸五子棋游戏的...
  • 首先,要先了解一下五子棋棋型知识。 棋型知识库主要包括各种既定的棋盘形式,有如下几种: 活二:能够形成活三的二,如下图,是三种基本的活二棋型。图中白点为活三点。 眠二:能够形成眠三的二。图中四个为...
  • 人机五子棋实现原理

    万次阅读 多人点赞 2018-05-12 10:41:07
    人机五子棋 近期整理代码的时候,发现大二的时候(目前大三)做的几个课程设计还不错,所以把这部分的代码以及设计文档都开源出来,以供后者参考学习使用。 完整代码以及本文的word都在放在了Github上,你可以...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 133
精华内容 53
关键字:

五子棋棋型评分