Last active
December 9, 2016 07:28
-
-
Save dnldsht/1caa7f017f1210b50db011be8934b340 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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