Skip to content

Instantly share code, notes, and snippets.

@dltacube
Last active May 20, 2017 22:13
Show Gist options
  • Save dltacube/15e29f3d60d0fe66d1a1577a65e13c8b to your computer and use it in GitHub Desktop.
Save dltacube/15e29f3d60d0fe66d1a1577a65e13c8b to your computer and use it in GitHub Desktop.

Revisions

  1. @heyoni heyoni revised this gist May 20, 2017. 1 changed file with 44 additions and 30 deletions.
    74 changes: 44 additions & 30 deletions tictactoe.py
    Original file line number Diff line number Diff line change
    @@ -1,20 +1,24 @@
    from itertools import groupby
    import os
    from copy import deepcopy, copy
    # Made for Python3.5


    default_pos = [['-', '-', '-'], ['-', '-', '-'], ['-', '-', '-']]


    class Board:
    score = []
    cpu = None
    def __init__(self, positions = default_pos, row_move = '', col_move = ''):

    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
    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':
    @@ -35,32 +39,34 @@ def draw_board(self):
    os.system('cls' if os.name == 'nt' else 'clear')
    # line numbers
    print('player ' + self.turn + "'s turn")
    print(' ' * 3 + '1' + ' ' * 7 + '2' + ' ' * 7 + '3')
    print(' ' * 3 + '0' + ' ' * 7 + '1' + ' ' * 7 + '2')
    # board
    print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7)
    print('1' + ' ' * 2 + self.pos[0][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[0][1] + ' ' * 3 + '|' + ' ' * 3 +
    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('2' + ' ' * 2 + self.pos[1][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[1][1] + ' ' * 3 + '|' + ' ' * 3 +
    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('3' + ' ' * 2 + self.pos[2][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[2][1] + ' ' * 3 + '|' + ' ' * 3 +
    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 = []
    @@ -69,15 +75,18 @@ def check_for_winner(self):
    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)
    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.
    If there are 2 groups or more, the sequence does not have 3 consecutives of any particular key'''
    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))
    @@ -97,28 +106,32 @@ def validate_input(self, move):
    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)):
    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)])
    # long term storage for movelist goes here
    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()
    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:
    newboard.find_next_move(newboard.valid_moves, levl=levl+1)
    moves.pop()
    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])])
    @@ -155,6 +168,7 @@ def find_best_move(self):
    return move
    return move


    def start_game():
    match = Board()
    print("Would you like to play first? y/n")
  2. @heyoni heyoni created this gist May 20, 2017.
    196 changes: 196 additions & 0 deletions tictactoe.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,196 @@
    from itertools import groupby
    import os
    from copy import deepcopy, copy
    # Made for Python3.5

    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
    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 + '1' + ' ' * 7 + '2' + ' ' * 7 + '3')
    # board
    print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7)
    print('1' + ' ' * 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('2' + ' ' * 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('3' + ' ' * 2 + self.pos[2][0] + ' ' * 3 + '|' + ' ' * 3 + self.pos[2][1] + ' ' * 3 + '|' + ' ' * 3 +
    self.pos[2][
    2] + ' ' * 3)
    print(' ' * 7 + '|' + ' ' * 7 + '|' + ' ' * 7)
    print('')

    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')

    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.
    If there are 2 groups or more, the sequence does not have 3 consecutives of any particular key'''
    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

    def find_next_move(self, allmoves, moves=[], levl=0):
    for i in range(len(allmoves)):
    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)])
    # long term storage for movelist goes here
    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()

    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