Game Development 17 min read

Build a Classic Gobang Game with Python Tkinter – Step‑by‑Step Tutorial

This article walks you through creating a fully functional Gobang (Five‑in‑a‑Row) game using Python's Tkinter library, covering GUI setup, board rendering, game logic, win detection, undo and reset features, and provides the complete source code ready to run.

Programmer DD
Programmer DD
Programmer DD
Build a Classic Gobang Game with Python Tkinter – Step‑by‑Step Tutorial

This tutorial demonstrates how to implement a basic Gobang (Five‑in‑a‑Row) game for beginners using Python and the Tkinter GUI toolkit.

Setup and Style Creation

First, import the required packages and define a chessBoard class that creates the main window, sets its size, disables resizing, and draws the board grid and initial stones.

''
@Auther : gaoxin
@Date : 2019.01.01
@Version : 1.0
''
from tkinter import *
import math

# Define chess board class
class chessBoard():
    def __init__(self):
        self.window = Tk()
        self.window.title("五子棋游戏")
        self.window.geometry("660x470")
        self.window.resizable(0, 0)
        self.canvas = Canvas(self.window, bg="#EEE8AC", width=470, height=470)
        self.paint_board()
        self.canvas.grid(row=0, column=0)

    def paint_board(self):
        # Draw horizontal lines
        for row in range(0, 15):
            if row == 0 or row == 14:
                self.canvas.create_line(25, 25 + row * 30, 25 + 14 * 30, 25 + row * 30, width=2)
            else:
                self.canvas.create_line(25, 25 + row * 30, 25 + 14 * 30, 25 + row * 30, width=1)
        # Draw vertical lines
        for column in range(0, 15):
            if column == 0 or column == 14:
                self.canvas.create_line(25 + column * 30, 25, 25 + column * 30, 25 + 14 * 30, width=2)
            else:
                self.canvas.create_line(25 + column * 30, 25, 25 + column * 30, 25 + 14 * 30, width=1)
        # Draw corner points
        self.canvas.create_oval(112, 112, 118, 118, fill="black")
        self.canvas.create_oval(352, 112, 358, 118, fill="black")
        self.canvas.create_oval(112, 352, 118, 358, fill="black")
        self.canvas.create_oval(232, 232, 238, 238, fill="black")
        self.canvas.create_oval(352, 352, 358, 358, fill="black")

Game Logic Implementation

The Gobang class encapsulates the game state, including the board matrix, move order, current player color, and flags for win/empty status. It provides methods for switching colors, handling mouse clicks to place stones, boundary checks, counting consecutive stones, win detection, undoing moves, clearing the board, and starting a new game.

# Define Gobang game class
# 0 = black stone, 1 = white stone, 2 = empty
class Gobang():
    def __init__(self):
        self.board = chessBoard()
        self.game_print = StringVar()
        self.game_print.set("")
        # 16x16 board to avoid index errors
        self.db = [([2] * 16) for i in range(16)]
        self.order = []
        self.color_count = 0
        self.color = 'black'
        self.flag_win = 1
        self.flag_empty = 1
        self.options()

    # Switch between black and white
    def change_color(self):
        self.color_count = (self.color_count + 1) % 2
        if self.color_count == 0:
            self.color = "black"
        elif self.color_count == 1:
            self.color = "white"

    # Handle mouse click to place a stone
    def chess_moving(self, event):
        if self.flag_win == 1 or self.flag_empty == 0:
            return
        x, y = event.x - 25, event.y - 25
        x = round(x / 30)
        y = round(y / 30)
        while self.db[y][x] == 2 and self.limit_boarder(y, x):
            self.db[y][x] = self.color_count
            self.order.append(x + 15 * y)
            self.board.canvas.create_oval(25 + 30 * x - 12, 25 + 30 * y - 12,
                                          25 + 30 * x + 12, 25 + 30 * y + 12,
                                          fill=self.color, tags="chessman")
            if self.game_win(y, x, self.color_count):
                print(self.color, "获胜")
                self.game_print.set(self.color + "获胜")
            else:
                self.change_color()
                self.game_print.set("请" + self.color + "落子")

    # Ensure the click is inside the board
    def limit_boarder(self, y, x):
        if x < 0 or x > 14 or y < 0 or y > 14:
            return False
        else:
            return True

    # Count consecutive stones in all directions
    def chessman_count(self, y, x, color_count):
        count1 = count2 = count3 = count4 = 1
        # Horizontal
        for i in range(-1, -5, -1):
            if self.db[y][x + i] == color_count:
                count1 += 1
            else:
                break
        for i in range(1, 5, 1):
            if self.db[y][x + i] == color_count:
                count1 += 1
            else:
                break
        # Vertical
        for i in range(-1, -5, -1):
            if self.db[y + i][x] == color_count:
                count2 += 1
            else:
                break
        for i in range(1, 5, 1):
            if self.db[y + i][x] == color_count:
                count2 += 1
            else:
                break
        # Diagonal \ 
        for i in range(-1, -5, -1):
            if self.db[y + i][x + i] == color_count:
                count3 += 1
            else:
                break
        for i in range(1, 5, 1):
            if self.db[y + i][x + i] == color_count:
                count3 += 1
            else:
                break
        # Diagonal /
        for i in range(-1, -5, -1):
            if self.db[y + i][x - i] == color_count:
                count4 += 1
            else:
                break
        for i in range(1, 5, 1):
            if self.db[y + i][x - i] == color_count:
                count4 += 1
            else:
                break
        return max(count1, count2, count3, count4)

    # Determine if the current move wins the game
    def game_win(self, y, x, color_count):
        if self.chessman_count(y, x, color_count) >= 5:
            self.flag_win = 1
            self.flag_empty = 0
            return True
        else:
            return False

    # Undo the last move
    def withdraw(self):
        if len(self.order) == 0 or self.flag_win == 1:
            return
        self.board.canvas.delete("chessman")
        z = self.order.pop()
        x = z % 15
        y = z // 15
        self.db[y][x] = 2
        self.color_count = 1
        for i in self.order:
            ix = i % 15
            iy = i // 15
            self.change_color()
            self.board.canvas.create_oval(25 + 30 * ix - 12, 25 + 30 * iy - 12,
                                          25 + 30 * ix + 12, 25 + 30 * iy + 12,
                                          fill=self.color, tags="chessman")
        self.change_color()
        self.game_print.set("请" + self.color + "落子")

    # Clear the board
    def empty_all(self):
        self.board.canvas.delete("chessman")
        self.db = [([2] * 16) for i in range(16)]
        self.order = []
        self.color_count = 0
        self.color = 'black'
        self.flag_win = 1
        self.flag_empty = 1
        self.game_print.set("")

    # Start a new game
    def game_start(self):
        if self.flag_empty == 0:
            return
        self.flag_win = 0
        self.game_print.set("请" + self.color + "落子")

    # Bind events and add control buttons
    def options(self):
        self.board.canvas.bind("<Button-1>", self.chess_moving)
        Label(self.board.window, textvariable=self.game_print, font=("Arial", 20)).place(relx=0, rely=0, x=495, y=200)
        Button(self.board.window, text="开始游戏", command=self.game_start, width=13, font=("Verdana", 12)).place(relx=0, rely=0, x=495, y=15)
        Button(self.board.window, text="我要悔棋", command=self.withdraw, width=13, font=("Verdana", 12)).place(relx=0, rely=0, x=495, y=60)
        Button(self.board.window, text="清空棋局", command=self.empty_all, width=13, font=("Verdana", 12)).place(relx=0, rely=0, x=495, y=105)
        Button(self.board.window, text="结束游戏", command=self.board.window.destroy, width=13, font=("Verdana", 12)).place(relx=0, rely=0, x=495, y=420)
        self.board.window.mainloop()

if __name__ == "__main__":
    game = Gobang()

Running the Program

Copy all the code above into a single Python file and execute it. The GUI window will appear, allowing you to start a new game, place black and white stones by clicking on the board, undo moves, clear the board, and end the session.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

GUIPythonGame DevelopmentTutorialTkinterGobang
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.