Tic-Tac-Toe man-machine game program (python implementation)

Table of contents

1 Introduction

2. Process flow

3. Code

4. Code Description

4.1 Checkerboard display

4.2 Initialization

4.3 The next step for human chess players

4.4 The Next Step of AI Chess Players

4.5 Final game and judgment of victory or defeat

5. Example chess game


1 Introduction

        The previous blogs (in a step-by-step manner) realized the game search, traversal and calculation of all possible game numbers and board states of the Tic-Tac-Toe game, see:

        Implementation of Tic-Tac-Toe possible game search (python)

        Implementation of Tic-Tac-Toe possible game traversal (python)

        How many different chess games and board states are there in Tic-Tac-Toe (python implementation)

        This article first implements a simple Tic-Tac-Toe man-machine game program, making a preparation for the next step to implement the Tic-Tac-Toe man-machine game program based on the minimax algorithm.

2. Process flow

3. Code

# Tic Tac Toe
# Created by chenxy in 2017-06-23, with only drawBoard() copied from <<inventwithpython>>
# 2023-01-04 refined
import random
import sys

def drawBoard(board, initFlag = 0):
    # This function prints out the board that it was passed.

    brd_copy = board.copy()
    if initFlag:
        brd_copy = ['0','1','2','3','4','5','6','7','8','9']

    # "board" is a list of 10 strings representing the board (ignore index 0)
    print('=============')
    # print('   |   |')
    print(' ' + brd_copy[7] + ' | ' + brd_copy[8] + ' | ' + brd_copy[9])
    # print('   |   |')
    print('-----------')
    # print('   |   |')
    print(' ' + brd_copy[4] + ' | ' + brd_copy[5] + ' | ' + brd_copy[6])
    # print('   |   |')
    print('-----------')
    # print('   |   |')
    print(' ' + brd_copy[1] + ' | ' + brd_copy[2] + ' | ' + brd_copy[3])
    # print('   |   |')
    print('=============')
    print()

def askGameStart():
    # Ask human start a game or not;
    print('Do you want to start a game? Y or y to start; Others to exit');
    inputWord = input().lower();
    if inputWord.startswith('y'):
        startNewGame = True;
    else:
        startNewGame = False;
    return(startNewGame);    

# Decide whether the number human input for the next move has been already used or not.
# It can be decided by whether the corrsponding element is empty or not.
def isValidInput(board, humanNextMove):
    isValid = 1;
    if humanNextMove == 0:
        print('Please input 1~9, 0 is not an valid input for the next move!');
        isValid = 0;
    elif board[humanNextMove] != ' ':
        print('The space has already been used! Please select an empty space for the next move');
        isValid = 0;    
    return(isValid);    

# Ask the human player for the next move.
def askNextMove(board):
    while True:
        print('Please input the next move!');
        c = input()
        if not c.isdigit():
            print('Invalid input! Please input [1-9]!');
            continue
        nextMove = int(c);
        if board[nextMove] == ' ':
            break;
        else:
            continue;
    isValid = isValidInput(board, nextMove)
    return isValid,nextMove
        
def gameRsltDisplay(winner):    
    if   'A' == winner:
        print('AI win!');
    elif 'H' == winner:
        print('Human win!');
    else:    
        print('A tie game!');        

# Decide AI's next move.
# Decide whether the three input are all the same
def isTripleGoalReachedNext(board,idx1, idx2, idx3, role):
    in1 = board[idx1];
    in2 = board[idx2];
    in3 = board[idx3];
    
    if   in1 == ' ' and in2 == in3 and in2 == role:
        return idx1;
    elif in2 == ' ' and in1 == in3 and in3 == role:
        return idx2;
    elif in3 == ' ' and in1 == in2 and in1 == role:
        return idx3;
    else:
        return 0;   # Invalid space index.

def isGoalReachedNext(board, role):

    nextMove        = 0;
        
    nextMove  = isTripleGoalReachedNext(board, 1, 4, 7, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 1, 2, 3, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 1, 5, 9, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 2, 5, 8, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 3, 5, 7, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 3, 6, 9, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 4, 5, 6, role);
    if nextMove > 0:
        return True, nextMove
    nextMove  = isTripleGoalReachedNext(board, 7, 8, 9, role);
    if nextMove > 0:
        return True, nextMove

    return False, nextMove;
    
def aiNextMove(board, gameRole):

    # Temporarily, select the first empty space.
    # 1. First, check whether AI will reach the goal in the next step.
    #    gameRole[0] represents AI's role.
    goalReachedNext, nextMove = isGoalReachedNext(board, gameRole[0]);
    
    if goalReachedNext == True:
        return nextMove;

    # 2. Secondly, check whether Human will reach the goal in the next step.
    #    gameRole[1] represents Human's role.
    #    Of course, AI should take the next move to blocking Human player to reach the goal.
    goalReachedNext, nextMove = isGoalReachedNext(board, gameRole[1]);
    
    if goalReachedNext == True:
        return nextMove;
    
    # Randomly selected from the left spaces for the next move.
    spaces = []
    for k in range(1,10):    
        if board[k] == ' ':
            spaces.append(k)
        else:
            continue;
    nextMove = random.choice(spaces)
    
    return(nextMove);

# Decide whether the three input are all the same
def isTripleSame(in1, in2, in3):
    if in1 == ' ' or in2 == ' ' or in3 == ' ':
        return False
    elif in1 == in2 and in1 == in3:
        return True
    else:
        return False

def gameJudge(board):
    if   isTripleSame(board[1],board[4],board[7]):
        gameOver = True;        winner   = board[1];
    elif isTripleSame(board[1],board[2],board[3]):    
        gameOver = True;        winner   = board[1];
    elif isTripleSame(board[1],board[5],board[9]):        
        gameOver = True;        winner   = board[1];
    elif isTripleSame(board[2],board[5],board[8]):        
        gameOver = True;        winner   = board[2];
    elif isTripleSame(board[3],board[5],board[7]):        
        gameOver = True;        winner   = board[3];
    elif isTripleSame(board[3],board[6],board[9]):        
        gameOver = True;        winner   = board[3];
    elif isTripleSame(board[4],board[5],board[6]):        
        gameOver = True;        winner   = board[4];
    elif isTripleSame(board[7],board[8],board[9]):        
        gameOver = True;        winner   = board[7];
    elif ' ' in board[1:9]:     
        gameOver = False;       winner   = ' ';
    else:
        gameOver = True;        winner   = ' ';
            
    return gameOver, winner
    
whoseTurn = 0;         #  0 : AI's turn;   1:  Human's turn.
gameRole  = ['A','H']; # [0]: AI's role;  [1]: Human's role;
board     = [' ']*10;  #  Note: '*' for string means concatenation.

drawBoard(board,1); # Draw the initial board with numbering

if not askGameStart():
    print('Bye-Bye! See you next time!');
    sys.exit();

while True:
    # Initialization.
    gameOver = 0;
    board    = [' ',' ',' ',' ',' ',' ',' ',' ',' ',' '];
    # Decide who, either human or AI, play first.
    # 0: AI; 1: human.
    role = random.randint(0,1);
    if role == 0:
        whoseTurn   = 0;
    elif role == 1:
        whoseTurn   = 1;

    while(not gameOver):    
        if whoseTurn == 0:
            print('AI\'s turn')
            nextMove = aiNextMove(board,gameRole);
            board[nextMove] = gameRole[0];
            whoseTurn = 1;
        else:
            print('Human\'s turn')
            isValid  = 0;
            while(not isValid):
                isValid, nextMove = askNextMove(board);
            board[nextMove] = gameRole[1];
            whoseTurn = 0;

        drawBoard(board);               
        gameOver,winner = gameJudge(board);     
    
    gameRsltDisplay(winner);

    #startNewGame = askGameStart();
    if not askGameStart():
        print('Bye-Bye! See you next time!');
        sys.exit();
                            

4. Code Description

4.1 Checkerboard display

        For the convenience of operation, the corresponding relationship between the grid position of the chessboard and the numbers (input by human players when playing chess) is designed to be the same as the small keyboard on the right side of the computer keyboard, as shown in the figure below. In this case, for example, if the chess player enters 7, it means the upper left corner of the chessboard, if he inputs 3, it means the lower right corner of the chessboard, and so on.

        In the code, a (character: "H", "A") array containing 10 elements is used to represent the board, and board[0] is not used. Human players are represented by the letter "H" (Human); AI players are represented by the letter "A" (Artificial Intelligence). For example, if a human chess player enters 7, it means the corner is in the upper left corner, then assign board[7] the value "H"; if an AI player inputs 1, it means the corner is in the lower left corner, then assign board[1] the value "H" A". . .

        For details, see the implementation of the drawBoard(board, initFlag = 0) function.

4.2 Initialization

        Initial checkerboard display. In the initial chessboard display at the start of the game, the numbers corresponding to each position of the chessboard shown in the above figure are displayed for players' reference. During the game, each checkerboard grid displays "H" or "A" or a space (a grid that has not yet been placed).

        Guess first. Call askGameStart() to start the game and guess first.

4.3 The next step for human chess players

askNextMove (): Ask the human player for the next move. A number from 1 to 9 should be entered

isValidInput(): Used to judge the legitimacy of player input. If you enter 0 or non-digits, the program will ask the player to enter again.

4.4 The Next Step of AI Chess Players

        Only a silly AI is currently implemented in this program, which is only slightly smarter than a completely random move.

        When the AI ​​decides to place a piece, it first checks whether there is a next move that can end the game, and if so, choose that move (if there are multiple next moves that can end the game, choose the first one found); if there is no move that can end the game On the next hand, a hand is randomly selected from the remaining spaces. Obviously, this is the place to start when optimizing this AI in the future.

        aiNextMove(board, gameRole): AI agent. Decide the AI's next move.

        isGoalReachedNext(board, role): When the AI ​​decides the next move, it first traverses the empty chess grid to see if there is a next move that can end the game.

        isTripleGoalReachedNext(board,idx1, idx2, idx3, role): Used to determine whether the specified three boards (under the next move condition) meet the conditions for ending the game.

4.5 Final game and judgment of victory or defeat

        gameJudge():

        Finale and winners and losers. If there has been a situation in which three chessboards in a straight line are the same to end the game, it is determined that the game is over and the winner is determined; if the board is full but there is no "three chessboards in a straight line are the same" situation situation, a draw is determined; otherwise, the game will continue.

        isTripleSame():

        Used to determine whether the specified three squares meet the conditions for ending the game. Note how it differs from isTripleGoalReachedNext().

5. Example chess game

        The following is an example of the running process of a game of chess.

(base) E:\TicTacToe>python tictactoe.py
=============
 7 | 8 | 9
-----------
 4 | 5 | 6
-----------
 1 | 2 | 3
=============

Do you want to start a game? Y or y to start; Others to exit
y
Human's turn
Please input the next move!
7
=============
 H |   |
-----------
    |   |
-----------
    |   |
=============

AI's turn
=============
 H |   |
-----------
    |   |
-----------
 A |   |
=============

Human's turn
Please input the next move!
3
=============
 H |   |
-----------
    |   |
-----------
 A |   | H
=============

AI's turn
=============
 H |   |
-----------
    | A |
-----------
 A |   | H
=============

Human's turn
Please input the next move!
9
=============
 H |   | H
-----------
    | A |
-----------
 A |   | H
=============

AI's turn
=============
 H |   | H
-----------
    | A | A
-----------
 A |   | H
=============

Human's turn
Please input the next move!
8
=============
 H | H | H
-----------
    | A | A
-----------
 A |   | H
=============

Human win!
Do you want to start a game? Y or y to start; Others to exit

Next Step:

        Realize unbeatable TicTacToe AI based on full minimax

        Implement TicTacToe GUI 

        。。。

        Postscript (2023-01-05): In the research and study of alphaGo papers, I want to write a Tic-Tac-Toe game program first, and use this as a basis to learn algorithm concepts such as minimax, alphabeta, and MCTS. Then I looked through the files in the computer and found that I actually wrote one in 2017, and I have no impression at all ^-^. I sorted it out and sent it out first so that I don't forget it again.

        For the implementation of tic-tac-toe AI agent based on minimax algorithm, see:

        https://chenxiaoyuan.blog.csdn.net/article/details/128591542https://chenxiaoyuan.blog.csdn.net/article/details/128591542

references:

AL SWEIGART, Invent Your Own Computer Games with Python(4th)

Guess you like

Origin blog.csdn.net/chenxy_bwave/article/details/128555550