Lab 1 Solution

"""Lab 1: Solution"""
from collections import Counter
import random


def get_positions(current_board, position_marker):
    """Helper fcn to get all locations on board of particular marker
       value, specified in position_marker.
    """
    return [key for (key, value) in current_board.items() if value == position_marker]

def visualize_board(game_board):
    """Helper function to visualize current state of board, to help check
       steps and conclusion of game.
    """
    print("Current board:")
    print(f"""{game_board["top-L"]} | {game_board["top-M"]} | {game_board["top-R"]}""")
    print(f"""{game_board["mid-L"]} | {game_board["mid-M"]} | {game_board["mid-R"]}""")
    print(f"""{game_board["low-L"]} | {game_board["low-M"]} | {game_board["low-R"]}""")
    print()

def end_game(game_board, marker, verbose=True):
    """Helper function to identify if there's a winner/tie based on current
       state of board or not.
    """
    # --- Step 1: List all positions of given player on board:
    player_positions = get_positions(game_board, marker)
    # --- Step 2: Parse positions to identify locations and directions:
    locations_positions = [x.split('-') for x in player_positions]
    locations = [l for [l, d] in locations_positions]
    directions = [d for [l, d] in locations_positions]
    # --- Step 3: Get counts of positions and locations, to help identify a
    #             potential winner:
    locations_dict = {v: k for (k, v) in Counter(locations).items()}
    directions_dict = {v: k for (k, v) in Counter(directions).items()}
    # --- Step 4: Check if end of game, based on stopping criteria:
    #             - no more empty locations OR
    #             - all 3 end in 'L'/'M'/'R'
    #             - all different 'top'/'mid'/'low' and 'L'/'M'/'R'
    if ((3 in locations_dict.keys()) | (3 in directions_dict.keys()) | (len(
            set(player_positions).intersection(
                    set(['top-R', 'mid-M', 'low-L']))) == len(
            set(player_positions).intersection(
                    set(['top-L', 'mid-M', 'low-R']))) == 3)):
        if verbose:
            print(f"{marker} is winner")
        return True
    elif not get_positions(game_board, " "):
        if verbose:
            print("tie")
        return True
    else:
        return False

if __name__ == '__main__':
    random.seed(2019)
    # --- Start with empty board:
    board = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
             'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
             'low-L': ' ', 'low-M': ' ', 'low-R': ' '
            }
    visualize_board(board)
    player_marker = "X"
    # --- Let each player take turns placing Xs and Os on board, until
    #     one is the winner or there's a tie:
    while not end_game(board, player_marker, verbose=True):
        for player_marker in {"X", "O"}:
            selected_position = random.choice(
                get_positions(board, position_marker=' '))
            board[selected_position] = player_marker
            visualize_board(board)
            if end_game(board, player_marker, verbose=False):
                break