1  Source Code

Đây là toàn bộ mã nguồn của Gomoku

import random


# Implement code for 3.1 here
def game_menu():
    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")


# Implement function 3.2
# Returns the inicial state of the game, with all the board empty
def create_board(size: int):
    board = []
    for i in range(size):
        board.append([' ' for j in range(size)])
    return board


# Implement 3.3
def is_occupied(board: list, x: int, y: int):
    return board[x][y] != ' '


# Implement 3.4
# convert position type string (e.g., 5 A) -> type int (e.g., 5 0)
def convert_position(position: tuple):
    return (int(position[0]), ord(position[1]) - ord('A'))

def place_on_board(board: list, stone: str, position: tuple):
    position_ = convert_position(position=position)
    
    if not is_occupied(board, x=position_[0], y=position_[1]):
        board[position_[0]][position_[1]] = stone
        return True
    return False


# Implement 3.5
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) + "\t"
    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] + " "  + "--\t"
        
        string = string[0:-3] + str(i)
        print(string.expandtabs(size_tab))
        print((size * '|\t').expandtabs(size_tab)) if (row < size - 1) else 0
        i += 1 


 # Implement code for 3.6 here
def check_available_moves(board: list):
    available_moves = []
    size = len(board)
    
    for row in range(size):
        for col in range(size):
            if not is_occupied(board, row, col):
                available_moves.append((str(row), chr(col + ord('A'))))
                
    return available_moves


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

def check_sequences_horizontal(board: list):
    size = len(board)
    
    for row in range(size):
        count_player1 = board[row].count('●')
        count_player2 = board[row].count('○')
        
        if (count_player1 == 5): return '●'
        elif (count_player2 == 5): return '○'
        else: return None

def check_sequences_vertical(board: list):
    size = len(board)
    
    for column_index in range(size):
        column = get_column(board, column_index)
        
        count_player1 = column.count('●')
        count_player2 = column.count('○')
        
        if (count_player1 == 5): return '●'
        elif (count_player2 == 5): return '○'
        else: return None

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

    # There will be ROW+COL-1 lines in the output
    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
        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
        count = min(line, (COL - start_col), ROW)

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

        diagonal_list.append(diagonal_item)
    
    for diagonal in diagonal_list:
        if diagonal.count('●') == 5: return '●'
        elif diagonal.count('○') == 5: return '○'
    
    return None

def check_sequences_diagonal2(board: list):
    h, w = len(board), len(board[0])
    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:
        if diagonal.count('●') == 5: return '●'
        elif diagonal.count('○') == 5: return '○'
        
    return None

def check_for_winner(board: list):
    white_stone = '○'
    black_stone = '●'
    
    sequences_horizontal = check_sequences_horizontal(board)
    sequences_vertical = check_sequences_vertical(board)
    sequences_diagonal1 = check_sequences_diagonal1(board)
    sequences_diagonal2 = check_sequences_diagonal2(board)
    
    for result in (sequences_horizontal, sequences_vertical,
                   sequences_diagonal1, sequences_diagonal2):
        if result is not None: return result
        
    if check_available_moves(board) == []:
        return "Draw"
    
    return None


 # Implement code for 3.8 here
def random_computer_player(board: list, player_move: tuple):
    size = len(board)
    player_row, player_col = convert_position(player_move)
    
    valid_moves = []
    
    for i in range(-1, 2):
        for j in range(-1, 2):
            new_row = player_row + i
            new_col = player_col + j
            
            if 0 <= new_row < size and 0 <= new_col < size and not is_occupied(board, new_row, new_col):
                valid_moves.append((str(new_row), chr(new_col + ord('A'))))
    
    if len(valid_moves) == 0:
        valid_moves = check_available_moves(board)
    
    return random.choice(valid_moves)


 # Implement code for 3.9 here
def input_choice():
    choice = None
    
    while True:
        choice = input("Enter your choice (e.g. 1 2 3...): ")
        if choice in ('1', '2', '3', '4', '5'):
            break
    return choice

def 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():
    game_mode_str = ''
    game_mode = None
    
    while True:
        game_mode = input("Choose game mode (1 - PvP, 2 - PvC). Please on input 1 or 2: ")
        if game_mode in ('1', '2'):
            break
    
    match game_mode:
        case '1':
            game_mode_str = 'pvp'
        case _:
            game_mode_str = 'pvc'
    
    return game_mode_str

def play_game():
    game_board      = None
    game_mode       = None
    current_turn    = '●'
    
    while True:
        game_menu()
        
        #Input choice value
        choice = input_choice()
        
        #Match case
        match choice:
            case '1':
                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): ")
                    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':
                if game_board is not None:
                    print_board(game_board)
                else:
                    print("No game in progress.")
            
            case '3':
                print("Lượt hiện tại:", current_turn)
                if game_board is None:
                    print("No game in progress.")
                    continue
                
                print_board(game_board)
                
                position = input("Enter position to place a stone (e.g., '2 F'): ")
                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)):
                    print("Invalid position or position is already occupied.")
                    continue
                
                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:
                    computer_move = random_computer_player(game_board, (row, col))
                    print(f"Computer places stone at {computer_move[0]} {computer_move[1]}")
                    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
            
            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.")
                
play_game()