Micro Project: TIC-TAC-TOE, simple AI

In this Tic Tac Toe game, you play against a simple artificial intelligence. An artificial intelligence (or AI) is a computer program that can intelligently respond to the player’s moves. The artificial intelligence that plays Tic Tac Toe is really just a few lines of code.

How to play Tic-Tac-Toe:
Two people play Tic Tac Toe with paper and pencil. One player is X and the other player is O. Players take turns placing their X or O. If a player gets three of their marks on the board in a row, column or one of the two diagonals, they win. When the board fills up with neither player winning, the game ends in a draw.

The player makes their move by entering the number of the space they want to go. These numbers are in the same places as the number keys on your keyboard’s keypad.

———–
|  7  |  8  |  9  |
———–
|  4  |  5  |  6  |
———–
|  1  |  2  |  3  |
———–

Again, we make use of OOP. The entire game is enclosed inside a class, so that everything can be reused as need. Code is very simple and easy to follow, no need for extra explanation here.

import random

class TicTacToe:
    def __init__(self):
        self.board = [' '] * 10
        self.player_letter = ''
        self.computer_letter = ''
        self.game_status = 'CONTINUE'

    def draw_board(self, board):
        print "-" * 13
        print "| " + board[7] + " | " + board[8] + " | " + board[9] + " |"
        print "-" * 13
        print "| " + board[4] + " | " + board[5] + " | " + board[6] + " |"
        print "-" * 13
        print "| " + board[1] + " | " + board[2] + " | " + board[3] + " |"
        print "-" * 13

    def is_free_space(self, board, index):
        # check if a cell is free
        return board[index] == ' '

    def make_a_move(self, board, index, letter):
        # mark a cell
        board[index] = letter

    def undo_move(self, board, index):
        # 'unmark' a cell
        board[index] = ' '

    def player_choose_leter(self):
        # ask player to choose either 'X' or 'O'
        letter = ''
        while not (letter == 'X' or letter == 'O'):
            print 'Do you want to play X or O:'
            letter = raw_input().strip()
        return letter

    def who_goes_first(self):
        # simulate two faces of a coin
        # face up = 1, computer move first
        # face down = 0, player move first
        if random.randint(0, 1) == 1:
            return 'computer'
        else:
            return 'player'

    def play_again(self):
        # return true if player want to play again
        print "Do you want to play again? (y,n)"
        choice = raw_input().strip()
        return choice.startswith('y')

    def have_a_winner(self, board, letter):
        # determine if the player with the letter 'letter' win
        # there are 8 possible ways to win
        # across the bottom
        # across the middle
        # across the top
        # down the left side
        # down the middle
        # down the right side
        # diagonals, respectievly
        return (board[1] == letter and board[2] == letter and board[3] == letter) \
            or (board[4] == letter and board[5] == letter and board[6] == letter) \
            or (board[7] == letter and board[8] == letter and board[9] == letter) \
            or (board[7] == letter and board[4] == letter and board[1] == letter) \
            or (board[8] == letter and board[5] == letter and board[2] == letter) \
            or (board[9] == letter and board[6] == letter and board[3] == letter) \
            or (board[7] == letter and board[5] == letter and board[3] == letter) \
            or (board[9] == letter and board[5] == letter and board[1] == letter)

    def get_board_copy(self, board):
        # copy the board
        copy = []
        for i in board:
            copy.append(i)
        return copy

    def get_player_move(self, board):
        # ask the player where they want to move
        # return index of that move (1-9)
        move = ''
        while move not in '1 2 3 4 5 6 7 8 9'.split() or not self.is_free_space(board, int(move)):
            print 'What\'s your next move? (1-9)'
            move = raw_input().strip()
        return int(move)

    def choose_a_random_move(self, board, move_list):
        # get all possible moves from the list
        possible_moves = []
        for i in move_list:
            if self.is_free_space(board, i):
                possible_moves.append(i)
        if len(possible_moves) > 0:
            # pick a random move
            return random.choice(possible_moves)
        else:
            return None

    def get_computer_move(self):
        # use a simple algorithm to help computer make a move
        # return index of the move

        # copy the current board
        copy_board = self.get_board_copy(self.board)
        print 'length', len(copy_board)
        # let computer try all possible moves to find a 'win move'
        for i in range(1, len(copy_board)):
            if self.is_free_space(copy_board, i):
                self.make_a_move(copy_board, i, self.computer_letter)
                if self.have_a_winner(copy_board, self.computer_letter):
                    # return the index of the winning move
                    return i
                # undo the move
                self.undo_move(copy_board, i)

        # now, we cant find a winning move for the computer
        # let's find out if player have a winning move
        # if player has a winning move, we block that move
        for i in range(1, len(copy_board)):
            if self.is_free_space(copy_board, i):
                self.make_a_move(copy_board, i, self.player_letter)
                if self.have_a_winner(copy_board, self.player_letter):
                    print 'blocked'
                    return i
                self.undo_move(copy_board, i)

        # now, both player and computer don't have a winning move
        # we randomly choose a possible move

        # first, choose all possible moves of the conner
        move = self.choose_a_random_move(self.board, [1, 3, 7, 9])
        if move != None:
            return move
        # then if we cant move to corners, choose move to the middle
        if self.is_free_space(self.board, 5):
            return 5
        # finally, move the the rest
        move = self.choose_a_random_move(self.board, [2, 4, 6, 8])
        return move

    def is_board_full(self, board):
        # check if the board is full
        for i in range(1, len(board)):
            if self.is_free_space(board, i):
                return False
        else:
            return True

    def start_game(self):
        # game's logic is here

        ended = False
        while not ended:
            # start the game by asking player to choose 'X' or 'O'
            self.player_letter = self.player_choose_leter()
            if self.player_letter == 'X':
                self.computer_letter = 'O'
            else:
                self.computer_letter = 'X'
            # then determine who's turn
            turn = self.who_goes_first()
            while self.game_status == 'CONTINUE':
                if turn == 'computer':
                    print 'computer move : ',
                    computer_move = self.get_computer_move()
                    print computer_move
                    self.make_a_move(self.board, computer_move, self.computer_letter)
                    turn = 'player'
                elif turn == 'player':
                    player_move = self.get_player_move(self.board)
                    self.make_a_move(self.board, player_move, self.player_letter)
                    turn = 'computer'
                # print the board
                self.draw_board(self.board)
                # check if it's a draw
                if self.is_board_full(self.board):
                    self.game_status = 'DRAW'
                    break
                # check if there is a winner
                if self.have_a_winner(self.board, self.computer_letter):
                    self.game_status = 'GAME_OVER'
                    break
                if self.have_a_winner(self.board, self.player_letter):
                    self.game_status = 'WIN'
                    break

            if self.game_status == 'GAME_OVER':
                print 'Too bad, computer has beaten you! You lose.'
            elif self.game_status == 'WIN':
                print 'Good, you\'ve won the game.'
            elif self.game_status == 'DRAW':
                print 'It\'s a draw, try again to see if you can beat the computer.'

            # check if player want to play again
            if self.play_again():
                ended = False
                self.reset_game()
            else:
                ended = True

    def reset_game(self):
        self.board = [' ']*10
        self.player_letter = ''
        self.computer_letter = ''
        self.game_status = 'CONTINUE'

if __name__ == '__main__':
    game = TicTacToe()
    game.start_game()

 

Advertisements