五子棋算法
前几天看到的一个比较有意思的算法;然后 实现了一下;最后给它加了一个UI界面;
核心获胜逻辑如下:
def __color_continius(self, arr, color): """ 判断某一颜色的棋子最大连续个数 """ max_count = 1 _count = 1 for i in range(0, len(arr) - 1): x1, y1 = arr[i] x2, y2 = arr[i + 1] if self.__pieces[x1][y1] == color and self.__pieces[x2][y2] == color: _count += 1 else: _count = 1 if _count >= max_count: max_count = _count return max_count def __check_win(self, selected_x, selected_y, color): """ 判断某一颜色棋子的输赢,以当前棋子为中心,4子为半径判断四个方向即可 """ _row = [] for x in range(selected_x - 4, selected_x + 5): if x >= 0 and x < self.__width: _row.append((x, selected_y)) if self.__color_continius(_row, color) >= 5: return True _col = [] for x in range(selected_y - 4, selected_y + 5): if x >= 0 and x < self.__height: _col.append((selected_x, x)) if self.__color_continius(_col, color) >= 5: return True lb_rt = [] for x in range(-4, 5): s_x = selected_x + x s_y = selected_y - x if s_x >= 0 and s_x < self.__width and s_y > 0 and s_y < self.__height: lb_rt.append((s_x, s_y)) if self.__color_continius(lb_rt, color) >= 5: return True lt_rb = [] for x in range(-4, 5): s_x = selected_x + x s_y = selected_y + x if s_x >= 0 and s_x < self.__width and s_y > 0 and s_y < self.__height: lt_rb.append((s_x, s_y)) if self.__color_continius(lt_rb, color) >= 5: return True return False def __is_draw(self): """ 判断是否是和棋 """ is_draw = True for x in self.__pieces: for y in x: if y == Piece.NotDefiend: is_draw = False return is_draw
所有代码如下;python版本
''' blog:https://www.cnblogs.com/rianley author: rianley ''' from tkinter import * from tkinter.messagebox import * from enum import Enum import sys class Piece(Enum): White = 1 Black = 2 NotDefiend = 3 class Gobang: def __init__(self): """ 初始化 """ self.__root = Tk() self.__root.title("程小航版本五子棋") self.__width = 15 self.__height = 15 self.__radius = 30 self.__w = (self.__width) * self.__radius * 2 self.__h = (self.__height) * self.__radius * 2 self.__root.geometry("{}x{}".format(self.__w, self.__h)) self.__canvas = Canvas(self.__root, width=self.__w, height=self.__h) self.__last_x = -1 self.__last_y = -1 self.__last_point = None self.__color = Piece.White self.__pieces = [[Piece.NotDefiend for y in range(0, self.__height)] for x in range(0, self.__width)] def __draw_lines(self): for x in range(0, self.__width): self.__canvas.create_line(self.__radius + x * self.__radius * 2, self.__radius, self.__radius + x * self.__radius * 2, self.__radius + (self.__height - 1) * self.__radius * 2) for y in range(0, self.__height): self.__canvas.create_line(self.__radius, self.__radius + y * self.__radius * 2, self.__radius + (self.__width - 1) * self.__radius * 2, self.__radius + y * self.__radius * 2) def __get_current_position(self, x, y): """ 根据当前鼠标位置计算棋子位置 """ selected_x = max(0, min(int(x / (self.__radius * 2)), self.__width - 1)) selected_y = max(0, min(int(y / (self.__radius * 2)), self.__height - 1)) return (selected_x, selected_y) def __color_continius(self, arr, color): """ 判断某一颜色的棋子最大连续个数 """ max_count = 1 _count = 1 for i in range(0, len(arr) - 1): x1, y1 = arr[i] x2, y2 = arr[i + 1] if self.__pieces[x1][y1] == color and self.__pieces[x2][y2] == color: _count += 1 else: _count = 1 if _count >= max_count: max_count = _count return max_count def __check_win(self, selected_x, selected_y, color): """ 判断某一颜色棋子的输赢,以当前棋子为中心,4子为半径判断四个方向即可 """ _row = [] for x in range(selected_x - 4, selected_x + 5): if x >= 0 and x < self.__width: _row.append((x, selected_y)) if self.__color_continius(_row, color) >= 5: return True _col = [] for x in range(selected_y - 4, selected_y + 5): if x >= 0 and x < self.__height: _col.append((selected_x, x)) if self.__color_continius(_col, color) >= 5: return True lb_rt = [] for x in range(-4, 5): s_x = selected_x + x s_y = selected_y - x if s_x >= 0 and s_x < self.__width and s_y > 0 and s_y < self.__height: lb_rt.append((s_x, s_y)) if self.__color_continius(lb_rt, color) >= 5: return True lt_rb = [] for x in range(-4, 5): s_x = selected_x + x s_y = selected_y + x if s_x >= 0 and s_x < self.__width and s_y > 0 and s_y < self.__height: lt_rb.append((s_x, s_y)) if self.__color_continius(lt_rb, color) >= 5: return True return False def __is_draw(self): """ 判断是否是和棋 """ is_draw = True for x in self.__pieces: for y in x: if y == Piece.NotDefiend: is_draw = False return is_draw def __bind_mouse_functions(self): """ 绑定鼠标事件 """ def move_handler(event): """ 鼠标移过棋盘,显示棋子 """ selected_x, selected_y = self.__get_current_position(event.x, event.y) # 如果已经有棋子了,当鼠标移动到点上时显示红色 color = ("black" if self.__color == Piece.Black else "white") if self.__pieces[selected_x][ selected_y] == Piece.NotDefiend else "red" if selected_x != self.__last_x or selected_y != self.__last_y: self.__canvas.delete(self.__last_point) self.__last_point = self.__canvas.create_oval( self.__radius + selected_x * self.__radius * 2 - self.__radius, self.__radius + selected_y * self.__radius * 2 - self.__radius, self.__radius + selected_x * self.__radius * 2 + self.__radius, self.__radius + selected_y * self.__radius * 2 + self.__radius, fill=color) self.__last_x = selected_x self.__last_y = selected_y def click_handler(event): """ 鼠标点击棋盘,落子并判断输赢 """ selected_x, selected_y = self.__get_current_position(event.x, event.y) if self.__pieces[selected_x][selected_y] != Piece.NotDefiend: # 棋子已经存在,不可落子 pass else: self.__pieces[selected_x][selected_y] = self.__color color = "black" if self.__color == Piece.Black else "white" self.__canvas.create_oval(self.__radius + selected_x * self.__radius * 2 - self.__radius, self.__radius + selected_y * self.__radius * 2 - self.__radius, self.__radius + selected_x * self.__radius * 2 + self.__radius, self.__radius + selected_y * self.__radius * 2 + self.__radius, fill=color) # 如果某一方赢了,则重置游戏 if self.__check_win(selected_x, selected_y, self.__color): message = "{} win!".format(color) showinfo("Info", message) self.__reset() # 否则判断是否是和棋,如果是和棋,则重置 elif self.__is_draw(): message = "draw chess!" showinfo("Info", message) self.__reset() if self.__color == Piece.White: self.__color = Piece.Black else: self.__color = Piece.White #绑定事件canvas事件 self.__canvas.bind('<Motion>', move_handler) self.__canvas.bind('<Button-1>', click_handler) def __reset(self): """ 重置游戏 """ self.__canvas.quit() self.__root.quit() self.__root.destroy() self.__init__() self.start() def start(self): # 绘制棋盘 self.__canvas.pack() #绘制线 self.__draw_lines() #鼠标经过是处理落棋,判断胜负条件 self.__bind_mouse_functions() #循环执行 self.__root.mainloop() if __name__ == "__main__": gobang = Gobang() gobang.start()
朋友写的 js版本
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>五子棋</title> <style type="text/css"> canvas{ display: block; margin: 50px auto; box-shadow: -2px -2px 2px #F3F2F2, 5px 5px 5px #6F6767; } </style> </head> <body> <canvas id="mycanvas" width="600px" height="600px"></canvas> <script type="text/javascript"> var chess = document.getElementById("mycanvas"); var context = chess.getContext('2d'); var me = true; var chessBox = [];//用于存放棋盘中落子的情况 for(var i=0;i<20;i++){ chessBox[i]=[]; for(var j=0;j<20;j++){ chessBox[i][j]=0;//初始值为0 } } function drawChessBoard(){ for(var i=0;i<20;i++){ context.strokeStyle="#D6D1D1"; context.moveTo(15+i*30,15);//垂直方向画15根线,相距30px; context.lineTo(15+i*30,585); context.stroke(); context.moveTo(15,15+i*30);//水平方向画15根线,相距30px;棋盘为14*14; context.lineTo(585,15+i*30); context.stroke(); } } drawChessBoard();//绘制棋盘 function oneStep(i,j,k){ context.beginPath(); context.arc(15+i*30,15+j*30,13,0,2*Math.PI);//绘制棋子 var g=context.createRadialGradient(15+i*30,15+j*30,13,15+i*30,15+j*30,0);//设置渐变 if(k){ //k=true是黑棋,否则是白棋 g.addColorStop(0,'#0A0A0A');//黑棋 g.addColorStop(1,'#636766'); }else { g.addColorStop(0,'#D1D1D1');//白棋 g.addColorStop(1,'#F9F9F9'); } context.fillStyle=g; context.fill(); context.closePath(); } var n=19; function f_x(arr) { for(var i in arr){ let len = 1; let res = arr[i].split('_'); let x = res[0]; let y = res[1]; let left = x; let right = x; while (left >= 0){ left -= 1 if(arr.includes(left+'_'+y)){ len+=1; }else{ break; } } while (right <= n){ right += 1 if(arr.includes(right+'_'+y)){ len+=1; }else{ break; } } if(len >= 5){ return arr[i]; } } return false; } function f_y(arr) { for(var i in arr){ let len = 1; let res = arr[i].split('_'); let x = res[0]; let y = res[1]; let left = y; let right = y; while (left >= 0){ left -= 1 if(arr.includes(x+'_'+left)){ len+=1; }else{ break; } } while (right <= n){ right += 1 if(arr.includes(x+'_'+right)){ len+=1; }else{ break; } } if(len >= 5){ return arr[i]; } } return false; } function f_xy1(arr) { for(var i in arr){ let len = 1; let res = arr[i].split('_'); let x = res[0]; let y = res[1]; let left = x; let right = y; while (left >= 0 && right >= 0){ left -= 1 right -= 1 if(arr.includes(left+'_'+right)){ len+=1; }else{ break; } } left = x; right = y; while (right <= n && left <= n){ left += 1 right += 1 if(arr.includes(left+'_'+right)){ len+=1; }else{ break; } } if(len >= 5){ return arr[i]; } } return false; } function f_xy2(arr) { for(var i in arr){ let len = 1; let res = arr[i].split('_'); let x = res[0]; let y = res[1]; let left = x; let right = y; while (left >= 0 && right <= n){ left -= 1 right += 1 if(arr.includes(left+'_'+right)){ len+=1; }else{ break; } } left = x; right = y; while (right >= 0 && left <= n){ left += 1 right -= 1 if(arr.includes(left+'_'+right)){ len+=1; }else{ break; } } if(len >= 5){ return arr[i]; } } return false; } var arr_all = []; var arr1 = []; var arr2 = []; var setIntervalxxx = setInterval(function () { while (1){ var a = Math.floor(Math.random() * 20); var b = Math.floor(Math.random() * 20); var strs = a+'_'+b; if(arr_all.includes(strs)){ continue; }else{ break; } } arr_all.push(strs); if(arr1.length == arr2.length){ arr1.push(strs); oneStep(a,b,true); var r1 = f_x(arr1); var r2 = f_y(arr1); var r3 = f_xy1(arr1); var r4 = f_xy2(arr1); if(r1 || r2 || r3 || r4){ alert('黑棋赢!!!'); console.log(r1,r2,r3,r4,'黑棋赢!!!',arr1); clearInterval(setIntervalxxx); } }else{ arr2.push(strs); oneStep(a,b,false); var r1 = f_x(arr2); var r2 = f_y(arr2); var r3 = f_xy1(arr2); var r4 = f_xy2(arr2); if(r1 || r2 || r3 || r4){ alert('白棋赢!!!'); console.log(r1,r2,r3,r4,'白棋赢!!!',arr2); clearInterval(setIntervalxxx); } } },200); chess.onclick=function(e){ var x = e.offsetX;//相对于棋盘左上角的x坐标 var y = e.offsetY;//相对于棋盘左上角的y坐标 var i = Math.floor(x/30); var j = Math.floor(y/30); if( chessBox[i][j] == 0 ) { oneStep(i,j,me); if(me){ chessBox[i][j]=1; }else{ chessBox[i][j]=2; } me=!me;//下一步白棋 } } </script> </body> </html>