Last active
May 20, 2017 22:13
-
-
Save dltacube/15e29f3d60d0fe66d1a1577a65e13c8b to your computer and use it in GitHub Desktop.
This file contains hidden or 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
from itertools import groupby | |
import os | |
from copy import deepcopy, copy | |
default_pos = [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']] | |
class Board: | |
score = [] | |
cpu = None | |
def __init__(self, positions=default_pos, row_move='', col_move=''): | |
self.pos = positions | |
self.row_move = row_move | |
self.col_move = col_move | |
self.getavailablemoves() | |
if row_move != '' and col_move != '': | |
self.update_pos(self.row_move, self.col_move) | |
o_plays, x_plays = 0, 0 | |
# determine whose turn it is anytime a new board is created | |
for row in self.pos: | |
for col in row: | |
if col == 'O': | |
o_plays += 1 | |
if col == 'X': | |
x_plays += 1 | |
if x_plays > o_plays: | |
self.turn = 'O' | |
else: | |
self.turn = 'X' | |
self.winner = False | |
self.valid_moves = [] | |
self.getavailablemoves() | |
self.check_for_winner() | |
def draw_board(self): | |
# clears the terminal | |
os.system('cls' if os.name == 'nt' else 'clear') | |
# line numbers | |
print('player ' + self.turn + "'s turn") | |
print(' ' * 3 + '0' + ' ' * 7 + '1' + ' ' * 7 + '2') | |
# board | |
print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7) | |
print('0' + ' ' * 2 + self.pos[0][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[0][1] + ' ' * 3 + '|' + ' ' * 3 + | |
self.pos[0][2] + | |
' ' * 3) | |
print('_' * 7 + '|' + '_' * 7 + '|' + '_' * 7) | |
print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7) | |
print('1' + ' ' * 2 + self.pos[1][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[1][1] + ' ' * 3 + '|' + ' ' * 3 + | |
self.pos[1][ | |
2] + ' ' * 3) | |
print('_' * 7 + '|' + '_' * 7 + '|' + '_' * 7) | |
print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7) | |
print('2' + ' ' * 2 + self.pos[2][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[2][1] + ' ' * 3 + '|' + ' ' * 3 + | |
self.pos[2][ | |
2] + ' ' * 3) | |
print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7) | |
print('') | |
# make sure that a move is legal | |
def update_pos(self, x, y): | |
if self.pos[x][y] == '-': | |
self.pos[x][y] = self.turn | |
self.turn = 'O' if self.turn == 'X' else 'X' | |
else: | |
print('invalid position. try again') | |
# look for a winner. assign winner to self.winner | |
def check_for_winner(self): | |
for y in range(0, 3): | |
tmp_hor = [] | |
tmp_ver = [] | |
# checks all horizontal and vertical sequences | |
for x in range(0, 3): | |
tmp_hor.append(self.pos[x][y]) | |
tmp_ver.append(self.pos[y][x]) | |
if len(tmp_ver) == 3: | |
self.check_groups(tmp_ver) | |
if len(tmp_hor) == 3: | |
self.check_groups(tmp_hor) | |
# hardcoding in diagonal sequences | |
self.check_groups([self.pos[0][0], self.pos[1][1], self.pos[2][2]]) | |
self.check_groups([self.pos[2][0], self.pos[1][1], self.pos[0][2]]) | |
def check_groups(self, seq): | |
'''we group together all recurring characters then check if there is more than one group. | |
One group in a sequence means that there are 3 of the same character in a row. | |
If there are 2 groups or more, there is no winner yet''' | |
groups = [] | |
for k, g in groupby(seq): | |
groups.append(list(g)) | |
if len(groups) == 1 and k != '-': | |
self.winner = k | |
def getavailablemoves(self): | |
self.valid_moves = [] | |
for row in enumerate(self.pos): | |
for col in enumerate(row[1]): | |
if col[1] == '-': | |
self.valid_moves.append([row[0], col[0]]) | |
def validate_input(self, move): | |
print("validating input...") | |
self.getavailablemoves() | |
xmove, ymove = map(int, move.split(',')) | |
return xmove, ymove | |
# generate a list of all possible moves, and assign a score to each end | |
# state. | |
def find_next_move(self, allmoves, moves=[], levl=0): | |
for i in range(len(allmoves)): | |
# initialize a new Board() instance with potential move | |
newboard = Board(deepcopy(self.pos), self.turn) | |
newboard.update_pos(allmoves[i][0], allmoves[i][1]) | |
newboard.check_for_winner() | |
newboard.getavailablemoves() | |
moves.append(allmoves[i]) | |
if newboard.winner: | |
if self.turn == self.cpu: | |
self.score.append([10 - levl, levl, copy(moves)]) | |
else: | |
self.score.append([levl - 10, levl, copy(moves)]) | |
moves.pop() | |
elif len(newboard.valid_moves) < 1: | |
# this means no winner - store this list too for stalemates | |
self.score.append([0, levl, copy(moves)]) | |
moves.pop() | |
else: | |
newboard.find_next_move(newboard.valid_moves, levl=levl + 1) | |
moves.pop() | |
# from the list of all moves, with their scores, find the path that leads | |
# to the highest score | |
def find_best_move(self): | |
levels = set([l[0] for l in groupby(self.score, key=lambda x: x[1])]) | |
total = {} | |
for move in self.score: | |
firstmv = str(move[2][0]) | |
if firstmv not in total.keys(): | |
total.update({firstmv: {move[1]: move[0]}}) | |
else: | |
if move[1] not in total[firstmv].keys(): | |
total[firstmv].update({move[1]: move[0]}) | |
else: | |
total[firstmv][move[1]] += move[0] | |
for k, v in total.items(): | |
print(k + ' ' + str(v)) | |
for level in levels: | |
prev_value = None | |
tmp = 0 | |
move = 0 | |
for k, v in total.items(): | |
if not level in v.keys(): | |
v[level] = 0 | |
if prev_value is not None: | |
if v[level] > prev_value: | |
move = k | |
tmp = v[level] | |
prev_value = v[level] | |
else: | |
prev_value = v[level] | |
tmp = v[level] | |
move = k | |
if [v[level] for k, v in total.items()].count(tmp) == 1: | |
return move | |
return move | |
def start_game(): | |
match = Board() | |
print("Would you like to play first? y/n") | |
firstplayer = input() | |
if firstplayer == 'y': | |
Board.cpu = 'O' | |
else: | |
Board.cpu = 'X' | |
match.draw_board() | |
print("make your move, i.e. '2,0' marks the third tile down in the first column.") | |
while True: | |
if match.turn == match.cpu: | |
Board.score = [] | |
match.getavailablemoves() | |
match.find_next_move(match.valid_moves) | |
print('best move:') | |
bestmove = match.find_best_move() | |
move = str(bestmove[1]) + ', ' + str(bestmove[4]) | |
else: | |
move = input() | |
xmove, ymove = match.validate_input(move) | |
result = match.update_pos(xmove, ymove) | |
match.draw_board() | |
match.check_for_winner() | |
if match.winner: | |
print("Player " + match.winner + " wins!") | |
playagain = input("play again? y/n: ") | |
break | |
return playagain | |
while True: | |
# Start a new game. | |
response = start_game() | |
# After the game has ended, check if the player wants to play another round | |
if response == 'y' or response == 'Y': | |
continue | |
else: | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment