├── .gitignore ├── .idea ├── Gomoku.iml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── GUI.py ├── Point.py ├── README.md ├── Record.py ├── main.py └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /.idea/Gomoku.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 75 | 76 | 77 | 78 | 79 | true 80 | DEFINITION_ORDER 81 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 122 | 123 | 124 | 125 | 128 | 129 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 163 | 164 | 183 | 184 | 185 | 186 | 187 | 200 | 201 | 214 | 215 | 232 | 233 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 275 | 276 | 295 | 296 | 317 | 318 | 340 | 341 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 1465214550054 384 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | -------------------------------------------------------------------------------- /GUI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding: utf-8 -*- 3 | 4 | import Tkinter 5 | import math 6 | import Point 7 | import Record 8 | 9 | class Chess_Board_Canvas(Tkinter.Canvas): 10 | #棋盘绘图板,继承自Tkinter.Canvas类 11 | def __init__(self, master=None, height=0, width=0): 12 | ''' 13 | 棋盘类初始化 14 | :param master: 画到那个对象 15 | :param height: 棋盘的高度 16 | :param width: 棋盘的宽度 17 | ''' 18 | Tkinter.Canvas.__init__(self, master, height=height, width=width) 19 | self.step_record_chess_board = Record.Step_Record_Chess_Board() 20 | #初始化计步器对象 21 | self.init_chess_board_points() #画点 22 | self.init_chess_board_canvas() #绘制棋盘 23 | 24 | def init_chess_board_points(self): 25 | ''' 26 | 生成各个棋盘点,根据棋盘坐标生成像素表座 27 | 并且保存到 chess_board_points 属性 28 | :return: 29 | ''' 30 | self.chess_board_points = [[None for i in range(15)] for j in range(15)] 31 | 32 | for i in range(15): 33 | for j in range(15): 34 | self.chess_board_points[i][j] = Point.Point(i, j); #棋盘坐标向像素坐标转化 35 | 36 | def init_chess_board_canvas(self): 37 | ''' 38 | 初始化棋盘 39 | :return: 40 | ''' 41 | 42 | for i in range(15): #绘制竖线 43 | self.create_line(self.chess_board_points[i][0].pixel_x, self.chess_board_points[i][0].pixel_y, self.chess_board_points[i][14].pixel_x, self.chess_board_points[i][14].pixel_y) 44 | 45 | for j in range(15): #绘制横线 46 | self.create_line(self.chess_board_points[0][j].pixel_x, self.chess_board_points[0][j].pixel_y, self.chess_board_points[14][j].pixel_x, self.chess_board_points[14][j].pixel_y) 47 | 48 | for i in range(15): #绘制椭圆,但是这个功能似乎是要加强交点的视觉效果,但是效果一般,视错觉没有出现 49 | for j in range(15): 50 | r = 1 51 | self.create_oval(self.chess_board_points[i][j].pixel_x-r, self.chess_board_points[i][j].pixel_y-r, self.chess_board_points[i][j].pixel_x+r, self.chess_board_points[i][j].pixel_y+r); 52 | 53 | def click1(self, event): #为何是click1因为关键字重复 54 | ''' 55 | 侦听鼠标事件,根据鼠标的位置判断落点 56 | :param event: 57 | :return: 58 | ''' 59 | for i in range(15): 60 | for j in range(15): 61 | square_distance = math.pow((event.x - self.chess_board_points[i][j].pixel_x), 2) + math.pow((event.y - self.chess_board_points[i][j].pixel_y), 2) 62 | #计算鼠标的位置和点的距离 63 | #距离小于14的点 64 | #这里其实有更优化的做法就是根据鼠标的位置计算点 65 | #pyganme开发里面似乎有这个算法 66 | 67 | if (square_distance <= 200) and (not self.step_record_chess_board.has_record(i, j)): #距离小于14并且没有落子 68 | if self.step_record_chess_board.who_to_play() == 1: 69 | #若果根据步数判断是奇数次,那么白下 70 | self.create_oval(self.chess_board_points[i][j].pixel_x-10, self.chess_board_points[i][j].pixel_y-10, self.chess_board_points[i][j].pixel_x+10, self.chess_board_points[i][j].pixel_y+10, fill='red') 71 | 72 | elif self.step_record_chess_board.who_to_play() == 2: 73 | self.create_oval(self.chess_board_points[i][j].pixel_x-10, self.chess_board_points[i][j].pixel_y-10, self.chess_board_points[i][j].pixel_x+10, self.chess_board_points[i][j].pixel_y+10, fill='green') 74 | 75 | self.step_record_chess_board.insert_record(i, j) 76 | #插入落子数据,落子最多225,这个程序没有实现AI 77 | 78 | result = self.step_record_chess_board.check() 79 | #判断是否有五子连珠 80 | 81 | 82 | if result == 1: 83 | self.create_text(240, 550, text='the white wins') 84 | #解除鼠标左键绑定 85 | self.unbind('') 86 | # """Unbind for this widget for event SEQUENCE the 87 | # function identified with FUNCID.""" 88 | 89 | elif result == 2: 90 | self.create_text(240, 550, text='the black wins') 91 | #解除鼠标左键绑定 92 | self.unbind('') 93 | 94 | 95 | class Chess_Board_Frame(Tkinter.Frame): 96 | def __init__(self, master=None): 97 | Tkinter.Frame.__init__(self, master) 98 | self.create_widgets() 99 | 100 | def create_widgets(self): 101 | self.chess_board_label_frame = Tkinter.LabelFrame(self, text="Chess Board", padx=5, pady=5) 102 | self.chess_board_canvas = Chess_Board_Canvas(self.chess_board_label_frame, height=600, width=480) 103 | 104 | self.chess_board_canvas.bind('', self.chess_board_canvas.click1) 105 | 106 | self.chess_board_label_frame.pack(); 107 | self.chess_board_canvas.pack(); 108 | -------------------------------------------------------------------------------- /Point.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding: utf-8 -*- 3 | 4 | class Point: 5 | 6 | def __init__(self, x, y): 7 | ''' 8 | 点,生成棋盘上的交点,并计算每个棋子多赢的实际像素坐标 9 | 棋盘坐标和像素坐标的转换 10 | :param x: 11 | :param y: 12 | ''' 13 | self.x = x; 14 | self.y = y; 15 | self.pixel_x = 30 + 30 * self.x 16 | self.pixel_y = 30 + 30 * self.y 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #GOMOKU 2 | 3 | ##Overview 4 | *Gomoku*, a very easy and light computher game implemented with python and Tkinter. 5 | 6 | Gomoku, 这是一个非常简单的轻量级的电脑游戏,这款游戏被用Python语言和Tkinter图形库实现。 7 | 8 | ##Screenshot 9 | ![screenshot icon](./screenshot.png) 10 | 11 | ##Version 12 | * ###version 1.0 13 | Basic Functions that you can play with other player 14 | 实现了可以去其他玩家一起下棋的基本功能 15 | 16 | ##Donate 17 | if you like my application, you can donate for us. And we will release update and support more and more good applications. 18 | 如果你喜欢我的应用,你可以给我捐款。这样鼓励我们推出更多更新,并提供越来越好的应用。 19 | -------------------------------------------------------------------------------- /Record.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding: utf-8 -*- 3 | 4 | class Step_Record: 5 | def __init__(self, count): 6 | """ 7 | :param count: int default 1 8 | 9 | 记录走步,根据当前步数,初始值为1 10 | 颜色是加1模2再加1 11 | 用1和0来区分黑白两色 12 | 先走的1是黑 13 | 后奏的是白 14 | """ 15 | self.count = count 16 | self.color = (self.count+1) % 2 + 1; 17 | 18 | 19 | 20 | class Step_Record_Chess_Board: 21 | def __init__(self): 22 | """ 23 | 初始化棋盘记录 24 | """ 25 | self.count = 1; 26 | self.records = [[None for i in range(15)] for j in range(15)] 27 | #初始化棋盘15x15,如果没有落子,棋盘值是None 28 | 29 | def has_record(self, x, y): 30 | """ 31 | 32 | :param x: 棋子横坐标 33 | :param y: 棋子中坐标 34 | :return: 返回某处是否已经落子,通过判断棋盘位置是否是None实现 35 | 如果不是None就是已经落子的了 36 | """ 37 | return not self.records[x][y] == None 38 | 39 | def insert_record(self, x, y): 40 | ''' 41 | 42 | :param x: 棋子横坐标 43 | :param y: 棋子纵坐标 44 | :return: 没有返回值 45 | 和一般的程序不同的是,棋盘上每个坐标都是一个Step_Record对象 46 | 黑子还是白字看属性,这样开销是否大呢 47 | 根据步数落子,步数可以判断是黑子还是白子 48 | 步数加1 49 | ''' 50 | self.records[x][y] = Step_Record(self.count) 51 | print '{0}'.format('white' if ((self.count+1) % 2 + 1) == 1 else 'black') ,'< x:', x, ', y:', y,'>' 52 | 53 | self.count += 1; 54 | 55 | def who_to_play(self): 56 | ''' 57 | 58 | :return: 判断谁改走了 59 | ''' 60 | return (self.count+1) % 2 + 1 61 | 62 | def check_row(self, x, y): 63 | """ 64 | :param x: 65 | :param y: 66 | :return: 67 | 68 | 检测行里是否连续的5个子 69 | """ 70 | if self.has_record(x, y) and self.has_record(x, y+1) and self.has_record(x, y+2) and self.has_record(x, y+3) and self.has_record(x, y+4): 71 | #判断一个子右侧是否5个连续有子,如果是判断是否连续的黑色或者白色,返回1或者2代表胜利 72 | if self.records[x][y].color == 1 and self.records[x][y+1].color == 1 and self.records[x][y+2].color == 1 and self.records[x][y+3].color == 1 and self.records[x][y+4].color == 1: 73 | return 1; 74 | 75 | elif self.records[x][y].color == 2 and self.records[x][y+1].color == 2 and self.records[x][y+2].color == 2 and self.records[x][y+3].color == 2 and self.records[x][y+4].color == 2: 76 | return 2; 77 | 78 | else: 79 | return 0; 80 | 81 | def check_col(self, x, y): 82 | """ 83 | 检测列里是否有连续的5个子 84 | :param x: 85 | :param y: 86 | :return: 87 | """ 88 | if self.has_record(x, y) and self.has_record(x+1, y) and self.has_record(x+2, y) and self.has_record(x+3, y) and self.has_record(x+4, y): 89 | 90 | if self.records[x][y].color == 1 and self.records[x+1][y].color == 1 and self.records[x+2][y].color == 1 and self.records[x+3][y].color == 1 and self.records[x+4][y].color == 1: 91 | return 1; 92 | 93 | elif self.records[x][y].color == 2 and self.records[x+1][y].color == 2 and self.records[x+2][y].color == 2 and self.records[x+3][y].color == 2 and self.records[x+4][y].color == 2: 94 | return 2; 95 | 96 | else: 97 | return 0; 98 | 99 | def check_up(self, x, y): 100 | ''' 101 | 检测/斜线方向是否有连续的子 102 | :param x: 103 | :param y: 104 | :return: 105 | ''' 106 | if self.has_record(x, y) and self.has_record(x+1, y+1) and self.has_record(x+2, y+2) and self.has_record(x+3, y+3) and self.has_record(x+4, y+4): 107 | 108 | if self.records[x][y].color == 1 and self.records[x+1][y+1].color == 1 and self.records[x+2][y+2].color == 1 and self.records[x+3][y+3].color == 1 and self.records[x+4][y+4].color == 1: 109 | return 1; 110 | 111 | elif self.records[x][y].color == 2 and self.records[x+1][y+1].color == 2 and self.records[x+2][y+2].color == 2 and self.records[x+3][y+3].color == 2 and self.records[x+4][y+4].color == 2: 112 | return 2; 113 | 114 | else: 115 | return 0; 116 | 117 | def check_down(self, x, y): 118 | ''' 119 | 检测\方向是否有连续的子 120 | :param x: 121 | :param y: 122 | :return: 123 | ''' 124 | if self.has_record(x, y) and self.has_record(x+1, y-1) and self.has_record(x+2, y-2) and self.has_record(x+3, y-3) and self.has_record(x+4, y-4): 125 | 126 | if self.records[x][y].color == 1 and self.records[x+1][y-1].color == 1 and self.records[x+2][y-2].color == 1 and self.records[x+3][y-3].color == 1 and self.records[x+4][y-4].color == 1: 127 | return 1; 128 | 129 | elif self.records[x][y].color == 2 and self.records[x+1][y-1].color == 2 and self.records[x+2][y-2].color == 2 and self.records[x+3][y-3].color == 2 and self.records[x+4][y-4].color == 2: 130 | return 2; 131 | 132 | else: 133 | return 0; 134 | 135 | 136 | def check(self): 137 | ''' 138 | 遍历棋盘,检测是否是全部有子 139 | 这里每次都是全面扫描棋盘,实际上可以优化 140 | 初期有很多列是没有落子的所以不必扫描 141 | :return: 142 | ''' 143 | for i in range(15): 144 | for j in range(11): #没有必要到15,否则会报错的 145 | result = self.check_row(i, j) 146 | if result != 0: 147 | return result 148 | 149 | for i in range(11): 150 | for j in range(15): 151 | result = self.check_col(i, j) 152 | if result != 0: 153 | return result 154 | 155 | for i in range(11): 156 | for j in range(11): 157 | result = self.check_up(i, j) 158 | if result != 0: 159 | return result 160 | 161 | for i in range(11): 162 | for j in range(4, 15): 163 | result = self.check_down(i, j) 164 | if result != 0: 165 | return result 166 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | #-*- coding: utf-8 -*- 3 | 4 | import GUI 5 | import Tkinter 6 | 7 | if __name__ == '__main__': 8 | window = Tkinter.Tk() 9 | gui_chess_board = GUI.Chess_Board_Frame(window) 10 | gui_chess_board.pack() 11 | window.mainloop() 12 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jamesxu182/Gomoku/daa6ca1fef4147f614f36c70285def70463c7463/screenshot.png --------------------------------------------------------------------------------