Skip to content

Instantly share code, notes, and snippets.

@dnldsht
Last active December 9, 2016 07:28
Show Gist options
  • Save dnldsht/1caa7f017f1210b50db011be8934b340 to your computer and use it in GitHub Desktop.
Save dnldsht/1caa7f017f1210b50db011be8934b340 to your computer and use it in GitHub Desktop.
import random
from time import sleep
def green(s):
return '\033[92m%s\033[0m' % s
def red(s):
return '\033[91m%s\033[0m' % s
class players:
X = green('X')
O = red('O')
def get_random_player():
return random.choice([players.X, players.O])
def get_opponent(player):
if player == players.O:
return players.X
return players.O
class TTTGame(object):
_coords = [(x, y) for y in range(3) for x in range(3)]
_num_to_coord = dict([(n+1, c) for n, c in enumerate(_coords)])
_win_ways = (
# Horizontal
{1, 2, 3},
{4, 5, 6},
{7, 8, 9},
# Verticle
{1, 4, 7},
{2, 5, 8},
{3, 6, 9},
# Diagonal
{1, 5, 9},
{3, 5, 7})
def __init__(self, game=None):
if not game:
self.board = list()
self.board.append(['1', '2', '3'])
self.board.append(['4', '5', '6'])
self.board.append(['7', '8', '9'])
self.avail = set(xrange(1, 10))
self.xnums = set()
self.onums = set()
else:
self.board = map(list, game.board)
self.avail = set(game.avail)
self.xnums = set(game.xnums)
self.onums = set(game.onums)
def __str__(self):
rows = [' | '.join(row)
for row in self.board[::-1]]
divs = ['\n---+---+---\n', '\n---+---+---\n', '\n']
return ' ' + ' '.join(map(''.join, zip(rows, divs)))
def winner(self):
"""Return 'X' or 'O' or 'T' else False if game not over."""
for way in self._win_ways:
if way.issubset(self.xnums):
return players.X
if way.issubset(self.onums):
return players.O
if len(self.xnums) + len(self.onums) == 9:
return 'T'
return False
def play(self, n, piece=None):
"""
Place piece at spot numbered n and ammend attributes.
if piece is None, remove the piece at cell n and ammend attributes.
"""
x, y = self._num_to_coord[n]
if piece:
self.board[y][x] = piece
(self.xnums if piece == players.X else self.onums).add(n)
self.avail.remove(n)
else:
self.board[y][x] = '_'
self.avail.add(n)
(self.xnums if n in self.xnums else self.onums).remove(n)
def next_move(self, piece):
"""
Return the next best move for piece as cell number.
"""
if all(map(lambda x: len(set(x)) == 1, self.board)):
return random.choice(1, 3, 7, 9) # Corners are best first play.
scores = []
avail = list(self.avail)
for n in avail:
node = TTTGame(self)
node.play(n, piece)
scores.append(node._evaluate(piece))
best = max(enumerate(scores), key=lambda x: x[1])[0]
return avail[best]
def _evaluate(self, piece):
"""
Return a score for how favourable the current board is towards piece.
"""
state = self.winner()
if state:
return (1 if state == piece else 0 if state == 'T' else -1)
scores = []
apponent = get_opponent(piece)
for n in self.avail:
self.play(n, apponent)
scores.append(0-self._evaluate(apponent))
self.play(n) # reverse play
safest = min(scores)
return safest
class CLI(object):
# - Note that game_loop() is not concerned with any of the display
# attributes or refreshing the screen.
# - Each method is responsible for
# handling it's own display characteristics, for now, with the use of
# the message attribute and refresh().
# - Methods which modify the message attribute should assign an empty
# string to message attribute before returning, except in cases like
# coin_toss().
# - Methods may communicate with each other via return values, not
# via message or any other display attribute.
def __init__(self, vs_cpu = False):
# Display variables.
self.wins = 0
self.losses = 0
self.ties = 0
self.message = ''
# Piece assignments.
self.player_1 = ''
self.player_2 = ''
# TTTGame instance.
self.game = None
# Players turn or not.
self.turn_p_1 = None
self.vs_cpu = vs_cpu
def refresh(self):
screen = '\n' * 100 # Clear screen.
screen += "TicTacToe\n"
if self.vs_cpu:
screen += ("Wins: %s\tLosses: %s\tTies: %s\n\n" % (self.wins, self.losses, self.ties))
else:
screen += ("Player 1: %s\tPlayer 2: %s\tTies: %s\n\n" % (self.wins, self.losses, self.ties))
screen += str(self.game) if self.game else '\n' * 4 # The game board.
screen += '\n' + self.message + '\n'
print (screen)
def chose_player(self):
asd = raw_input("Player 1 %s or %s " % (players.X, players.O))
if asd in ['X', 'x']:
self.player_1 = players.X
elif asd in ['O', 'o']: self.player_1 = players.O
else: self.player_1 = get_random_player()
self.player_2 = get_opponent(self.player_1)
self.turn_p_1 = bool(random.getrandbits(1))
self.message = "Player 1 go first!" if self.turn_p_1 else "%s go first!" % 'CPU' if self.vs_cpu else 'Player 2'
def player_turn(self, player):
while True:
promt = 'Player %s!\ncells [%s], [hint/h] or press enter... ' % ('1' if player == self.player_1 else '2' , green(', '.join(str(s) for s in self.game.avail)))
option = raw_input(promt).strip().lower()
if option in ['hint', 'h']:
self.message = str(self.game.next_move(self.player_1))
elif option == '':
r = bool(random.getrandbits(1))
if r == True:
best = self.game.next_move(player)
self.message = "You are lucky!"
else:
best = self.game.next_move(get_opponent(player))
self.message = "I'm sorry!"
self.game.play(best, player)
self.refresh()
sleep(1) #wait a second so you can show what the faith chose for you
break
elif option.isdigit() and 1 <= int(option) <= 9:
self.message = ''
self.game.play(int(option), player)
self.refresh()
break
elif int(option) not in self.game.avail:
self.message = "That cell is already occupied!"
else: self.message = "That's not a valid option!"
self.refresh()
def computer_turn(self):
best = self.game.next_move(self.player_2)
self.game.play(best, self.player_2)
self.message = ''
self.refresh()
def play_again(self):
"""Gives user the chance to quit the program or continue."""
while True: # until user enters valid input
self.refresh()
option = raw_input("Play again (enter) or n? ").strip().lower()
if not option:
self.message = ''
return
elif option in ["no", 'n']:
import sys
sys.exit()
else:
self.message = "That's not a valid option!"
def game_loop(self):
"""Simple game loop."""
while True:
self.game = TTTGame()
self.chose_player()
self.refresh()
while not self.game.winner():
if not self.vs_cpu:
self.player_turn(self.player_1 if self.turn_p_1 else self.player_2)
elif self.turn_p_1:
self.player_turn(self.player_1)
else:
self.computer_turn()
self.turn_p_1 = not self.turn_p_1
winner = self.game.winner()
if winner == 'T':
self.message = "You tied."
self.ties += 1
elif winner == self.player_1:
self.message = "You won!"
self.wins += 1
else:
self.message = "You lost."
self.losses += 1
self.play_again()
if __name__ == '__main__':
print '\n' * 100
print ("Welome to TicTacToe\n\n" +
"Cells are numbered 1 to 9 and correspond directly\n" +
"with keys on your keyboards numpad.\n\n" +
"To make a play, type the relevent number and hit enter\n\n" +
"You can also type hint when it's your turn to play.\n\n" +
"BTW, the computer is unbeatable. Which means your win\n" +
"statistic will never show anything other than 0.\n" +
"Have fun ;)\n\n")
raw_input(".... (hit enter) ...")
while True:
random.choice = raw_input("How do you wanna play?\n1)Player vs CPU\n2)Player vs Player\nMake your choice [1, 2] ")
if random.choice in ['1', '2']: break
user_interface = CLI(vs_cpu=True if random.choice == '1' else False)
user_interface.game_loop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment