Skip to content

Instantly share code, notes, and snippets.

@rpip
Last active October 23, 2021 01:49
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save rpip/5608979 to your computer and use it in GitHub Desktop.
Save rpip/5608979 to your computer and use it in GitHub Desktop.
Simple Python implementation of the classic Tic Tac Toe game. Goal : Unbeatable AI.
#!/usr/bin/env python
#--------------------------------------------------------------------------
# Tic Tac Toe game in Python
# Author: Mawuli Adzaku <mawuli@mawuli.me>
# Date: 20-05-2013
# Tested with Python 2.7
# TO RUN:
# sudo chmod a+x tictactoe.py
# ./tictactoe.py
# OR JUST RUN : python tictactoe.py
# @todo: parse user commands, log game scores, run game over telnet
# ---------------------------------------------------------------------------
# import modules
import random
import sys
import copy
class Game:
"Tic-Tac-Toe class. This class holds the user interaction, and game logic"
def __init__(self):
self.board = [' '] * 9
self.player_name = ''
self.player_marker = ''
self.bot_name = 'TBot'
self.bot_marker = ''
self.winning_combos = (
[6, 7, 8], [3, 4, 5], [0, 1, 2], [0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6],
)
self.corners = [0,2,6,8]
self.sides = [1,3,5,7]
self.middle = 4
self.form = '''
\t| %s | %s | %s |
\t-------------
\t| %s | %s | %s |
\t-------------
\t| %s | %s | %s |
'''
def print_board(self,board = None):
"Display board on screen"
if board is None:
print self.form % tuple(self.board[6:9] + self.board[3:6] + self.board[0:3])
else:
# when the game starts, display numbers on all the grids
print self.form % tuple(board[6:9] + board[3:6] + board[0:3])
def get_marker(self):
marker = raw_input("Would you like your marker to be X or Y?: ").upper()
while marker not in ["X","Y"]:
marker = raw_input("Would you like your marker to be X or Y? :").upper()
if marker == "X":
return ('X', 'Y')
else:
return ('Y','X')
def help(self):
print '''
\n\t The game board has 9 sqaures(3X3).
\n\t Two players take turns in marking the spots/grids on the board.
\n\t The first player to have 3 pieces in a horizontal, vertical or diagonal row wins the game.
\n\t To place your mark in the desired square, simply type the number corresponding with the square on the grid
\n\t Press Ctrl + C to quit
'''
def quit_game(self):
"exits game"
self.print_board
print "\n\t Thanks for playing :-) \n\t Come play again soon!\n"
sys.exit()
def is_winner(self, board, marker):
"check if this marker will win the game"
# order of checks:
# 1. across the horizontal top
# 2. across the horizontal middle
# 3. across the horizontal bottom
# 4. across the vertical left
# 5. across the vertical middle
# 6. across the vertical right
# 7. across first diagonal
# 8. across second diagonal
for combo in self.winning_combos:
if (board[combo[0]] == board[combo[1]] == board[combo[2]] == marker):
return True
return False
def get_bot_move(self):
'''
find the best space on the board for the bot. Objective
is to find a winning move, a blocking move or an equalizer move.
Bot must always win
'''
# check if bot can win in the next move
for i in range(0,len(self.board)):
board_copy = copy.deepcopy(self.board)
if self.is_space_free(board_copy, i):
self.make_move(board_copy,i,self.bot_marker)
if self.is_winner(board_copy, self.bot_marker):
return i
# check if player could win on his next move
for i in range(0,len(self.board)):
board_copy = copy.deepcopy(self.board)
if self.is_space_free(board_copy, i):
self.make_move(board_copy,i,self.player_marker)
if self.is_winner(board_copy, self.player_marker):
return i
# check for space in the corners, and take it
move = self.choose_random_move(self.corners)
if move != None:
return move
# If the middle is free, take it
if self.is_space_free(self.board,self.middle):
return self.middle
# else, take one free space on the sides
return self.choose_random_move(self.sides)
def is_space_free(self, board, index):
"checks for free space of the board"
# print "SPACE %s is taken" % index
return board[index] == ' '
def is_board_full(self):
"checks if the board is full"
for i in range(1,9):
if self.is_space_free(self.board, i):
return False
return True
def make_move(self,board,index,move):
board[index] = move
def choose_random_move(self, move_list):
possible_winning_moves = []
for index in move_list:
if self.is_space_free(self.board, index):
possible_winning_moves.append(index)
if len(possible_winning_moves) != 0:
return random.choice(possible_winning_moves)
else:
return None
def start_game(self):
"welcomes user, prints help message and hands over to the main game loop"
# welcome user
print '''\n\t-----------------------------------
\n\t TIC-TAC-TOE by Mawuli Adzaku
\n\t------------------------------------
'''
self.print_board(range(1,10))
self.help()
self.player_name = self.get_player_name()
# get user's preferred marker
self.player_marker, self.bot_marker = self.get_marker()
print "Your marker is " + self.player_marker
# randomly decide who can play first
if random.randint(0,1) == 0:
print "I will go first"
# self.make_move(self.board,random.choice(self.corners), self.bot_marker)
#self.print_board()
self.enter_game_loop('b')
else:
print "You will go first"
# now, enter the main game loop
self.enter_game_loop('h')
def get_player_move(self):
move = int(input("Pick a spot to move: (1-9) "))
while move not in [1,2,3,4,5,6,7,8,9] or not self.is_space_free(self.board,move-1) :
move = int(input("Invalid move. Please try again: (1-9) "))
return move - 1
def get_player_name(self):
return raw_input("Hi, i am %s" % self.bot_name + ". What is your name? ")
def enter_game_loop(self,turn):
"starts the main game loop"
is_running = True
player = turn #h for human, b for bot
while is_running:
if player == 'h':
user_input = self.get_player_move()
self.make_move(self.board,user_input, self.player_marker)
if(self.is_winner(self.board, self.player_marker)):
self.print_board()
print "\n\tCONGRATULATIONS %s, YOU HAVE WON THE GAME!!! \\tn" % self.player_name
#self.incr_score(self.player_name)
is_running = False
#break
else:
if self.is_board_full():
self.print_board()
print "\n\t-- Match Draw --\t\n"
is_running = False
#break
else:
self.print_board()
player = 'b'
# bot's turn to play
else:
bot_move = self.get_bot_move()
self.make_move(self.board, bot_move, self.bot_marker)
if (self.is_winner(self.board, self.bot_marker)):
self.print_board()
print "\n\t%s HAS WON!!!!\t\n" % self.bot_name
#self.incr_score(self.bot_name)
is_running = False
break
else:
if self.is_board_full():
self.print_board()
print "\n\t -- Match Draw -- \n\t"
is_running = False
#break
else:
self.print_board()
player = 'h'
# when you break out of the loop, end the game
self.end_game()
def end_game(self):
play_again = raw_input("Would you like to play again? (y/n): ").lower()
if play_again == 'y':
self.__init__() # necessary for re-initialization of the board etc
self.start_game()
else:
print "\n\t-- GAME OVER!!!--\n\t"
self.quit_game()
if __name__ == "__main__":
TicTacToe = Game()
TicTacToe.start_game()
@StepanVL
Copy link

StepanVL commented Feb 3, 2016

Bot loses to 9,7,3,6. I think it should prefer center to corners in order to draw.

@deevsterr
Copy link

when i type the part where it says "board[combo[2]]" i get an invalid syntax error. Could you please help and respond quickly? thanks!

@duanxn
Copy link

duanxn commented May 20, 2016

I have a script here using +1 or -1 instead of 'X'/'O', and the most you can get from the game is a tie.

@kitepython
Copy link

Play and go first, go 1, then 2, then 5, then 9
Try it. Easy win every time.
Plz fix

@Rayyan-Amanat
Copy link

Rayyan-Amanat commented Sep 30, 2018

sir my pycharm not showing any board.but still it accepts the inputs
Any suggestion pls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment