2  Explain

Giải thích từng phần:

2.1 Game menu function.

1def game_menu():
2    print('Gomoku Game--------------')
    print("Game Menu")
    print("Select this option below for game:")         
    print("1. Start a Game")
    print("2. Print the Board")
    print("3. Place a Stone")
    print("4. Reset the Game")
    print("5. Exit")
1
Định nghĩa hàm bắt đầu bằng def, sau tên hàm () để định nghĩa các biến sử dụng trong hàm, chỉ định kiểu dữ liệu. Bắt đầu nội dùng hàm bằng :, tham khảo thêm tại. 1
2
Các dòng từ dòng 2 trở đi là hàm print() dùng để xuất dữ liệu ra màn hình.

2.2 Creating the Board.

Chức năng tạo ra ma trận size hàng x size cột, mỗi phần tử trong list chứa dữ liệu của bàn cờ.

1def create_board(size: int):
    board = []
2    for i in range(size):
3        board.append([' ' for j in range(size)])
    return board
1
size: int chỉ định kiểu dữ liệu int cho biến size. Xuất hiện từ python 3.5. 2
2
Vòng lặp chạy từ i=0 đến size-1 mỗi lần tăng i sẽ tạo ra mảng kiểu list [ , , , ,…,size] có size-1 phần tử.
3
Kỹ thuật [' ' for j in range(size)] được gọi là List Comprehension 3, nó cho phép dùng vòng lặp tạo nhanh một list theo ý muốn người dùng. Phương thức board.append() để thêm một phần tử vào kiểu dữ liệu list - List Append 4.
  • Bảng giá trị size 9x9
[[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
 [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']]
  • Hình bên dưới minh hoạ cho bàn cờ (Board) size 9x9, các kích thước khác tương tự.

Visualize board 9x9

Visualize board 9x9

2.3 Is the target position occupied?

Chức năng hàm yêu cầu để kiểm tra tại vị trí (x, y) đã tồn tại một quân cờ chưa.

def is_occupied(board: list, x: int, y: int):
1    return board[x][y] != ' '
1
Mặc định khi khởi tạo dữ liệu tại [Creating the Board], các giá trị là ' ' nên tại vị trí board[x][y] không giống các giá trị mặc định thì trả về TRUE. Thay vì cách code như bên dưới, chúng ta có thể trả về trực tiếp giá trị boolean giảm bớt một dòng lệnh. Tham khảo thêm chức năng - Python predicate 5.
def is_occupied(board: list, x: int, y: int):
    if (board[x][y] != ' '):
        return TRUE

    return FALSE

2.4 Placing a Stone at a Specific Intersection.

  • Các yêu cầu chính được mô tả ở đây
# convert position type string (e.g., 5 A) -> type int (e.g., 5 0)
1def convert_position(position: tuple):
2    return (int(position[0]), ord(position[1]) - ord('A'))

# place on board function
3def place_on_board(board: list, stone: str, position: tuple):
4    position_ = convert_position(position=position)
    
5    if not is_occupied(board, x=position_[0], y=position_[1]):
        board[position_[0]][position_[1]] = stone
        return True
    return False
1
Chức năng hàm yêu cầu khi người dùng truyền vào toạ độ (5 A) thì ghi giá trị vào mảng đã tạo ở [Creating the Board.], nhưng trong lập trình thì Python dùng index kiểu int (1, 2, 3, 4,…). Vậy nên, mục tiêu của hàm convert_position(position: tuple) là chuyển từ giá trị str (5 A) sang (5 0).
2
Trong bảng ASCII, giá trị của kí tự A là 65 B là 66 và các kí tự tiếp theo có giá trị tăng dần. Vậy trường hợp cột B ta muốn lấy index trong ngôn ngữ lập trình thì lấy ASCII(B) - ASCII(A) có giá trị là 66 - 65 = 1; tương tự với cột C ASCII(C) - ASCII(A) có giá trị là 67 - 65 = 2. Kết luận: lấy giá trị ascii(A) = 65 làm mốc để tính các cột khác. Biến đầu vào position: tuple có kiểu dữ liệu mẫu (5 A), truy cập giá trị 5 với phương thức position[0], A với phương thức position[1]. Hàm ord() với giá trị nhận vào là str để thực hiện chức năng chuyển ascii(A) sang int.
3
Lý do tại sao tách hàm convert_position ra khỏi hàm chính, vì tăng tính tường minh mỗi hàm mỗi tính năng dễ bảo trì hơn.
4
Khi nhận giá trị thuần từ người dùng (5 A) sẽ thực hiện chức năng chuyển đổi kiểu dữ liệu.
5
Sử dụng hàm #3.3.is-occupied, để kiểm tra đã tồn tại stone, nếu không tồn tại thì ghi dữ liệu vào mảng dữ liệu board đã khởi tạo ở #3.2.create-board sau đó thoát hàm và trả về True. Nếu không thì hàm luôn trả về False.

2.5 Printing the Board

  • Các yêu cầu được mô tả ở đây

  • Mã nguồn của hàm:

def print_board(board: list):
    s = ""
1    size_tab = 5
2    size = len(board)
    
    # Create column index (e.g., A B C)
3    for index in range(65, 65 + size):
4        s += chr(index) + "\t"
5    print(s.expandtabs(size_tab))
    
    # Create grid and row index
6    i = 0
7    for row in range(size):
        string = ""
        
8        for column in range(size): # Loop all value in each row
            string += board[row][column] + " "  + "--\t"
        
9        string = string[0:-3] + str(i)
10        print(string.expandtabs(size_tab))
11        print((size * '|\t').expandtabs(size_tab)) if (row < size - 1) else 0
        i += 1 
1
Chỉ định giá trị cho hàm Expand Tabs, trong hàm print_board() dùng \t là tabs để căn thẳng các kí tự để in ra đẹp hơn.
2
Lấy kích thước bàn cờ để phục vụ việc xuất ra màn hình.
3
Như đã giải thích ở mục 2 phần Placing a Stone at a Specific Intersection., các index cột là alphabet nên bắt đầu từ A tương ứng trong mã ascii(A)65 nên vòng lặp bắt đầu từ 65, dừng lại khi đủ số cột 65 + size.
4
Với s ban đầu rỗng, sau mỗi lần lặp s += nghĩa là s = s + str_value. Ví dụ, s += chr(index) + "\t" sau lần lặp đầu tiên sẽ là s = "" + chr(65) + "\t" tương ứng s = "" + "A" + "\t" rút gọn s = "A\t", lần lặp tiếp theo sẽ được s = "A\t B\t" . Nhận xét nếu không có có kí tự \t thì không thể căn thẳng cột được, vậy nên dùng tabs là phù hợp nhất.
5
Sau vòng lặp thu được chuỗi s = "A\tB\tC\tD\tE\tF\tG\tH\t", size_tab mặc định là 2 xuất ra màn hình không đẹp nên chọn giá trị 5. Sẽ được kết quả như mục Expand Tab.1.
6
Biến i được khởi tạo để dùng cho in index dòng (0, 1, 2, …)
7
Vòng lặp được dùng để lấy từng hàng trong bảng board được tạo ở #3.2.table-size9x9.
8
Vòng lặp thứ hai để đọc dữ liệu từng ô trong một hàng và kết hợp với xử lý string để in ra màn hình từng dòng. Sau mỗi vòng lặp chuỗi sẽ được cộng thêm giá trị board[row][column] + " " + "--\t" tương ứng board[row][colmn] --\t, \t là tabs để căn thẳng với index A, B, C, D,…
9
Kết thúc vòng lặp thu được chuỗi board[row][colmn] --\tboard[row][colmn] --\t...board[row][colmn] --\tboard[row][colmn] --\t, nó sẽ luôn dư chuỗi --\t vì vậy dùng String Slicing 6 để cắt bớt chuỗi đi, các giá trị bị bỏ đi là [-3 -2 -1] tương ứng [- - \t].
10
Tiếp tục dùng expandtabs() như đã giải thích ở mục 5, để các giá trị được thẳng hàng với các index cột.
11
Được giải thích kỹ hơn ở #3.5.2.rewrite-blockcode.

2.5.1 Expand Tab

  1. Sử dụng tabs:
A    B    C    D    E    F    G    H    I    
  --   --   --   --   --   --   --   --   0
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   1
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   2
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   3
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   4
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   5
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   6
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   7
|    |    |    |    |    |    |    |    |    
  --   --   --   --   --   --   --   --   8
  1. Không dùng tabs:
# Loai bo ki tu tab
def print_board_(board: list):
    s = ""
    size_tab = 5
    size = len(board)
    
    # Create column index (e.g., A B C)
    for index in range(65, 65 + size):
        s += chr(index) + " "
    print(s.expandtabs(size_tab))
    
    # Create grid and row index
    i = 0
    for row in range(size):
        string = ""
        
        for column in range(size): # Loop all value in each row
            string += board[row][column] + " "  + "-- "
        
        string = string[0:-3] + str(i)
        print(string.expandtabs(size_tab))
        print((size * '| ').expandtabs(size_tab)) if (row < size - 1) else 0
        i += 1 

# Ket qua
A B C D E F G H I 
  --   --   --   --   --   --   --   --   0
| | | | | | | | | 
  --   --   --   --   --   --   --   --   1
| | | | | | | | | 
  --   --   --   --   --   --   --   --   2
| | | | | | | | | 
  --   --   --   --   --   --   --   --   3
| | | | | | | | | 
  --   --   --   --   --   --   --   --   4
| | | | | | | | | 
  --   --   --   --   --   --   --   --   5
| | | | | | | | | 
  --   --   --   --   --   --   --   --   6
| | | | | | | | | 
  --   --   --   --   --   --   --   --   7
| | | | | | | | | 
  --   --   --   --   --   --   --   --   8

2.6 Check Available Moves

  • Yêu cầu của hàm ở đây
  • Mã nguồn:
1def check_available_moves(board: list):
    available_moves = []
2    size = len(board)
    
3    for row in range(size):
        for col in range(size):
4            if not is_occupied(board, row, col):
5                available_moves.append((str(row), chr(col + ord('A'))))
                
    return available_moves
1
Hàm để tìm kiếm tất cả các vị trí trên bàn cờ còn trống và thêm vào list available_moves.
2
Lấy kích thước board.
3
Vòng lặp sẽ duyệt qua từng hàng, vòng lặp tiếp theo sẽ đi đến giá trị từng cột trong hàng.
4
Sử dụng hàm #3.3.is-occupied để kiểm tra tại vị trí (row, col) đã được đánh chưa.
5
available_moves.append sẽ thêm phần tử mới vào available_moves 7. (str(row), chr(col + ord('A'))) mục đích chuyển kiểu dữ liệu từ (5 2) sang (5 C).

2.7 Check for the Winner

  • Hãy xem lại bảng đã xuất ra ở #3.5.1.print-with-tabs và hình minh hoạ #2.2_01_visualize-board.

  • Ý tưởng là sẽ sẽ quét qua từng hàng, cột và các đường chéo để đếm giá trị '●' hoặc '○' có tổng số là 5. Khi tìm thấy sẽ dừng lại toàn bộ hàm.

    • Hàm check_sequences_horizontal để kiểm tra theio chiều ngang.

    • Hàm check_sequences_vertical để kiểm tra theo chiều dọc.

    • Hàm check_sequences_diagonal1 để kiểm tra các đường chéo từ dưới bên trái lên

      Diagonally 1
    • Hàm check_sequences_diagonal2 để kiểm tra các đường chéo từ trên bên trái xuống dưới

      Diagonally 2
    • Cuối cùng, hàm check_for_winner sẽ tổng hợp lại các hàm trên.

2.7.1 Hàm check_sequences_horizontal

1def get_column(board: list, i: int):
    return [row[i] for row in board]

def check_sequences_horizontal(board: list):
    size = len(board)
    
2    for row in range(size):
        count_player1 = board[row].count('●')
        count_player2 = board[row].count('○')
        
3        if (count_player1 == 5): return '●'
        elif (count_player2 == 5): return '○'
        else: return None
1
Hàm phụ được viết thêm để lấy dữ liệu trong một cột của board, dùng List Comprehension. Trong [row[i] for row in board] mỗi row được lặp qua thì giá trị row[i] tương ứng cột i cần lấy sẽ được thêm vào list. Cuối cùng hàm trả về list đó.
2
Code chính đếm số lượng stone trên một hàng, cứ mỗi row in range(size) trong vòng lặp được đi qua thì stone '●' được đếm bằng count_player1 = board[row].count('●'), hàng tiếp theo tương ứng cho '○'.
3
Tại đây sẽ kiểm tra nếu số lượng stone nào bằng 5 thì return giá trị đó tương ứng. Nếu không có stone nào đủ hàm sẽ luôn trả về None.

2.7.2 Hàm check_sequences_vertical

def check_sequences_vertical(board: list):
    size = len(board)
    
    for column_index in range(size):
1        column = get_column(board, column_index)
        
2        count_player1 = column.count('●')
        count_player2 = column.count('○')
        
        if (count_player1 == 5): return '●'
        elif (count_player2 == 5): return '○'
        else: return None
1
Hàm sẽ dùng đến hàm get_column#3.7.1.check-horizontal
2
Còn lại cấu trúc cách hoạt động giống [Hàm check_sequences_horizontal] chỉ khác là trục dọc.

2.7.3 Hàm check_sequences_diagonal1

def check_sequences_diagonal1(board: list):
    ROW = len(board)
    COL = len(board)
    diagonal_list = []

    # There will be ROW+COL-1 lines in the output
1    for line in range(1, (ROW + COL)):
        # Get column index of the first element
        # in this line of output. The index is 0
        # for first ROW lines and line - ROW for
        # remaining lines
2        start_col = max(0, line - ROW)

        # Get count of elements in this line.
        # The count of elements is equal to
        # minimum of line number, COL-start_col and ROW
3        count = min(line, (COL - start_col), ROW)

        # Print elements of this line
4        diagonal_item = []
        for j in range(0, count):
5            diagonal_item.append(board[min(ROW, line) - j - 1]
                                        [start_col + j])

6        diagonal_list.append(diagonal_item)
    
7    for diagonal in diagonal_list:
        if diagonal.count('●') == 5: return '●'
        elif diagonal.count('○') == 5: return '○'
    
    return None
1
Số đường chéo sẽ luôn là row + col -1 như ở ví dụ #3.7.3.check_sequences_diagonal1_example
2
Lấy chỉ mục để bắt đầu cột đầu tiên cho các đường chéo, bắt đầu ở giá trị 1 trong ví dụ #3.7.3.check_sequences_diagonal1_example nên có cột bằng 0.
3
Số phần tử trên mỗi đường chéo là khác nhau nên dòng này để lấy số phần tử cần thiết mà không quá index của mảng.
4
Chứa đường chéo sau mỗi vòng lặp.
5
Trong vòng lặp qua các phần tử từ (0 -> count), các phần tử sẽ được thêm vào mảng đường chéo diagonal_item.
6
Sau đó, diagonal_item sẽ được thêm vào chứa các đường chéo diagonal_list
7
Tương tự như hai hàm trên, trên mỗi đường chéo sẽ được đếm số lượng stone nếu đủ 5 sẽ trả về giá trị.
# 3.7.3.check_sequences_diagonal1
# Example matrix
1     2     3     4
5     6     7     8
9    10    11    12
13    14    15    16
17    18    19    20

# Output
1
5 2
9 6 3
13 10 7 4
17 14 11 8
18 15 12
19 16
20

2.7.4 Hàm check_sequences_diagonal2

def check_sequences_diagonal2(board: list):
1    h, w = len(board), len(board[0])
2    diagonal_list = [[board[h - p + q - 1][q]
                    for q in range(max(p-h+1, 0), min(p+1, w))]       
                    for p in range(h + w - 1) ]
    
    for diagonal in diagonal_list:
3        if diagonal.count('●') == 5: return '●'
        elif diagonal.count('○') == 5: return '○'
        
    return None
1
Lấy chiều cao và chiều rộng
2
Sử dụng List Comprehension để tạo mảng diagonal_list.
3
Đếm giá trị stone có đủ số lượng để thắng
Input: 
M[][] = {{11, 42, 25, 51}, 
         {14, 17, 61, 23},
         {22, 38, 19, 12},
         {27, 81, 29, 71}} 
Output: 
51 
25 23 
42 61 12 
11 17 19 71 
14 38 29 
22 81 
27 

2.7.5 Hàm check_for_winner

  • Đây là hàm chính kiểm tra người chơi nào đã thắng hoặc không.

  • Mã nguồn

def check_for_winner(board: list):
1    white_stone = '○'
    black_stone = '●'
    
2    sequences_horizontal = check_sequences_horizontal(board)
3    sequences_vertical = check_sequences_vertical(board)
4    sequences_diagonal1 = check_sequences_diagonal1(board)
5    sequences_diagonal2 = check_sequences_diagonal2(board)
    
6    for result in (sequences_horizontal, sequences_vertical,
                   sequences_diagonal1, sequences_diagonal2):
        if result is not None: return result
        
7    if check_available_moves(board) == []:
        return "Draw"
    
    return None
1
Định nghĩa ký tự đại diện người chơi
2
Gọi [Hàm check_sequences_horizontal]
3
Gọi [Hàm check_sequences_vertical]
4
Gọi [Hàm check_sequences_diagonal1]
5
Gọi [Hàm check_sequences_diagonal2]
6
Vòng lặp sẽ đi qua từng giá trị của tuple (sequences_horizontal, sequences_vertical, sequences_diagonal1, sequences_diagonal2), các hàm trên khi không trả về giá trị None chắc chắn sẽ trả về giá trị người thắng.
7
Hàm #3.6.check-available-moves nếu trả về rỗng thì bàn cờ không đi được nữa, và chưa có người thắng nên trả về Draw.

2.8 Random Computer Player

  • Các yêu cầu của hàm ở đây
  • Mã nguồn:
def random_computer_player(board: list, player_move: tuple):
    size = len(board)
1    player_row, player_col = convert_position(player_move)
    
2    valid_moves = []
    
3    for i in range(-1, 2):
        for j in range(-1, 2):
4            new_row = player_row + i
            new_col = player_col + j
            
5            if (0 <= new_row < size and 0 <= new_col < size)
            and not is_occupied(board, new_row, new_col):
6                valid_moves.append((str(new_row), chr(new_col + ord('A'))))
    
    if len(valid_moves) == 0:
7        valid_moves = check_available_moves(board)
    
8    return random.choice(valid_moves)
1
Chuyển đổi player_move: tuple sang kiểu int để tương tác với mảng.
2
List rỗng được khởi tạo để chứa lượt đi có thể.
3
Theo yêu cầu đề bài, lượt đi mới được random trong frame 3x3 với player_move ở trung tâm của frame. Giá trị i chạy từ (-1, 0, 1), khi i=-1 thì cột lùi về một cột, ở 0 thì bằng với player_move, ở 1 thì tăng lên 1 cột nữa. Tương tự cho vòng lặp với j tương ứng với hàng.
4
Biến trung gian để hoạt động cho frame 3x3 cho hàng và cột.
5
Khi lượt chơi player_move ở các biên trên dưới trái phải, nên để không bị lỗi out of index điều kiện (0 <= new_row < size and 0 <= new_col < size) để bỏ qua các valid_moves có thể có vượt ra ngoài biên. Ngoài ra, nếu tại vị trí đó không tồn tại dữ liệu stone với hàm is_occupied(board, new_row, new_col) để kiểm tra việc đó thì mới được liệt kê vào valid_moves.
6
.append() thêm nước đi đã được chuyển đổi từ kiểu int bằng (str(new_row), chr(new_col + ord('A'))) sang kiểu str vào valid_moves.
7
Khi không thể tim được nước đi có thể trong frame 3x3 từ nước đi player_move của người chơi. Dùng #3.6.check-available-moves tìm toàn bộ nước đi trên board còn lại.
8
Random với trọng số bằng nhau cho toàn bộ giá trị trong valid_moves để trả về nước đi kế tiếp.

2.9 Play Game

  • Hàm chính để tương tác với các chức năng của toàn bộ chương trình, mô tả ở đây
  • Mã nguồn các hàm hỗ trợ hàm chính:
1def input_choice():
    choice = None
    
    while True:
2        choice = input("Enter your choice (e.g. 1 2 3...): ")
3        if choice in ('1', '2', '3', '4', '5'):
4            break
    return choice

5def input_board_size():
    board_size = None
    
    while True:
        board_size = int(input("Enter board size (9, 13, or 15): "))
        if board_size in (9, 13, 15):
            break
    return board_size

def input_game_mode():
6    game_mode_str = ''
    game_mode = None
    
    while True:
        game_mode = input("Choose game mode (1 - PvP, 2 - PvC). Please on input 1 or 2: ")
7        if game_mode in ('1', '2'):
            break
    
8    match game_mode:
        case '1':
            game_mode_str = 'pvp'
        case _:
            game_mode_str = 'pvc'
    
    return game_mode_str
1
Hàm input_choice() để nhập lựa chọn từ người dùng, trả về giá trị tương ứng với #3.1.game-menu.
2
Hàm input là hàm build-in để nhận dữu liệu người dùng nhập vào 8.
3
Ở đây, vòng lặp while được thiết kế để khi người dùng nhập vào giá trị choice không nằm trong tuple ('1', '2', '3', '4', '5') thì vòng lặp sẽ tiếp tục cho nhập đến khi đúng giá trị.
4
Ngắt vòng lặp while bằng lệnh break.
5
Tương tự, cho hàm input_board_size() cũng cùng cách hoạt động.
6
Hàm game_mode() nhập chế độ chơi, lý do có game_modegame_mode_str là để nhập vào dữ liệu game_mode là kiểu int cho thuận tiện và trả về kiểu str cho việc tương tác với các hàm khác.
7
Điều này tương tự như hai hàm bên trên, nếu nhập sai sẽ bắt nhập lại.
8
Chức năng match…case chỉ xuất hiện từ Python 3.10 trở lên và nó rất thuận tiện trong trường hợp này.9 Khi chúng ta nhập vào choice = 1 thì hàm sẽ trả về pvp.
  • Mã nguồn hàm chính:
def play_game():
1    game_board      = None
2    game_mode       = None
3    current_turn    = '●'
    
4    while True:
5        game_menu()
        
        #Input choice value
6        choice = input_choice()
        
        #Match case
7        match choice:
            case '1':
8                if game_board is not None:
                    print("A game is already in progress ^^.")
                    reset_choice = input("Do you want to reset and start a new game? (y/n): ")
9                    if reset_choice.lower() == 'y':
                        game_board = None
                    else:
                        continue
                
                board_size      = input_board_size()
                game_mode       = input_game_mode()
                game_board      = create_board(board_size)
                current_turn    = '●'
            
            case '2':
10                if game_board is not None:
                    print_board(game_board)
                else:
                    print("No game in progress.")
            
            case '3':
11                print("Lượt hiện tại:", current_turn)
                if game_board is None:
12                    print("No game in progress.")
                    continue
                
                print_board(game_board)
                
                position = input("Enter position to place a stone (e.g., '2 F'): ")
13                row, col = position.split()
                
                if current_turn == '○' and game_mode == 'pvc':
                    print("It's not your turn.")
                    continue
                if not place_on_board(game_board, current_turn, (row, col)):  
14                    print("Invalid position or position is already occupied.").
                    continue
                
15                winner = check_for_winner(game_board)
                
                if winner is not None:
                    print_board(game_board)
                    if winner == 'Draw':
                        print("It's a draw!")
                    else:
                        print(f"Player {winner} wins!")
                    game_board = None
                    continue
                
                if current_turn == '●':
                    current_turn = '○'
                
                if game_mode == 'pvc' and winner is None:
16                    computer_move = random_computer_player(game_board, (row, col))
                    print(f"Computer places stone at {computer_move[0]} {computer_move[1]}")
17                    place_on_board(game_board, '○', computer_move)
                    winner = check_for_winner(game_board)
                    current_turn = '●'
                    if winner is not None:
                        print_board(game_board)
                        if winner == 'Draw':
                            print("It's a draw!")
                        else:
                            print(f"Player {winner} wins!")
                        game_board = None
            
18            case '4':
                game_board = None
                game_mode = None
                current_turn = '●'
                print("Game has been reset.")
            
            case '5':
                print("Exiting the game.")
                break
            
            case _:
                print("Invalid choice. Please choose a valid option.")
1
Biến chứa dữ liệu của bàn cờ - board, khi chưa khởi tạo nó sẽ là None.
2
Biến chứa dữ liệu chế độ chơi, PvP or PvC.
3
Khởi tạo mặc định lượt đi đầu tiên luôn là black stone.
4
Vòng lặp while trong python 10, khi là while True vòng lặp sẽ luôn chạy vì vậy cân có lệnh break để thoát vòng lặp.
5
Xuất ra #3.1.game-menu cho người dùng lựa chọn.
6
Để nhận lựa chọn người dùng muốn từ hàm input_choice#3.9.play-game-support-function.
7
Chức năng đã được giới thiệu phía trên, khi người dùng nhập số tương ứng Python sẽ thực hiện những chức năng bên trong trường hợp tương ứng đó.
8
Với case = 1 nếu board đã được khởi tạo thì hỏi có muốn reset dữ liệu không?
9
Chuyển giá trị nhập sang kiểu viết thường y nếu lỡ nhập in hoa.
10
Khi giá trị board đã tồn tại thì mới in ra màn hình.
11
Thông báo lượt hiện tại bkack or while.
12
Game chưa khởi tạo thông báo không có gam hoạt động.
13
Lấy giá trị row, column
14
Thông báo khi chọn ô đã tồn tại.
15
Luôn kiểm tra đã có người thắng hay chưa?
16
Quá trình random vị trí mới trong chế độ PvC
17
Khi đã có nước đi computer_move thực hiện đặt nó lên bàn cờ, sau đó check có người đã thắng chưa.
18
Phương thức đặt lại toàn bộ kích cỡ và dữ liệu của bàn cợ.

  1. Function Definition↩︎

  2. Type hints↩︎

  3. List Comprehension↩︎

  4. List Append↩︎

  5. Python predicate↩︎

  6. String Slicing↩︎

  7. Append↩︎

  8. Input function↩︎

  9. Python Match-Case↩︎

  10. While loop↩︎