Last active
June 21, 2023 00:46
-
-
Save ig0r-ferreira/0de8b2eff41372338f612398309fce90 to your computer and use it in GitHub Desktop.
Tic Tac Toe CLI game
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 time | |
import random | |
ALIGN = 30 | |
DIRECTIONS = [ | |
[(1, 1), (2, 2), (3, 3)], | |
[(1, 3), (2, 2), (3, 1)], | |
[(1, 1), (1, 2), (1, 3)], | |
[(2, 1), (2, 2), (2, 3)], | |
[(3, 1), (3, 2), (3, 3)], | |
[(1, 1), (2, 1), (3, 1)], | |
[(1, 2), (2, 2), (3, 2)], | |
[(1, 3), (2, 3), (3, 3)], | |
] | |
def clear_console(): | |
print("\033[H\033[J", end="") | |
def print_error(error): | |
print(f'\033[1;31m{error}\033[m') | |
def read_int(prompt): | |
while True: | |
try: | |
user_input = int(input(prompt).strip()) | |
except ValueError: | |
print_error('Invalid input. Enter an integer.') | |
else: | |
if 4 > user_input > 0: | |
return user_input | |
print_error('Invalid input. Choose only 1, 2 or 3.') | |
def has_winner(player, game_board): | |
hit_sequence = player * 3 | |
return any( | |
''.join(map(game_board.get, direction)) == hit_sequence | |
for direction in DIRECTIONS | |
) | |
def get_opponent(player): | |
return 'X' if player != 'X' else 'O' | |
def get_cpu_reaction(human_player, game_board): | |
cpu = get_opponent(human_player) | |
empty_positions = [] | |
for direction in DIRECTIONS: | |
areas = list(map(game_board.get, direction)) | |
if ' ' not in areas: | |
continue | |
if areas.count(cpu) == 2 or areas.count(human_player) == 2: | |
return direction[areas.index(' ')] | |
else: | |
empty_positions.append(direction[areas.index(' ')]) | |
else: | |
return random.choice(empty_positions) | |
def read_human_symbol(): | |
while True: | |
player = input('Will you be X or O? ').strip().upper() | |
if player in ('X', 'O'): | |
return player | |
print_error('You must choose X or O. Please try again.') | |
def read_player_move(game_board): | |
print() | |
while True: | |
row = read_int('Enter a row number: ') | |
column = read_int('Enter a columm number: ') | |
if game_board[row, column].isspace(): | |
return row, column | |
print_error( | |
'A move has already been made to this position. Try another.' | |
) | |
def make_game_board(): | |
return dict.fromkeys( | |
((x, y) for x in range(1, 4) for y in range(1, 4)), ' ' | |
) | |
def show_game_board(table): | |
game_board = f""" | |
| | | |
{table[1, 1]} | {table[1, 2]} | {table[1, 3]} | |
______|_______|_______ | |
| | | |
{table[2, 1]} | {table[2, 2]} | {table[2, 3]} | |
______|_______|_______ | |
| | | |
{table[3, 1]} | {table[3, 2]} | {table[3, 3]} | |
| | | |
""" | |
print(game_board) | |
def mainloop(human_player, game_board): | |
current_player = human_player | |
moves = [] | |
show_game_board(game_board) | |
while len(moves) < 9: | |
print(f'{current_player} plays now'.center(ALIGN)) | |
if current_player == human_player: | |
position = read_player_move(game_board) | |
else: | |
time.sleep(1.5) | |
position = get_cpu_reaction(human_player, game_board) | |
game_board[position] = current_player | |
moves.append(position) | |
clear_console() | |
show_game_board(game_board) | |
if has_winner(current_player, game_board): | |
print(f'Player {current_player} wins!'.center(ALIGN)) | |
break | |
current_player = get_opponent(current_player) | |
else: | |
print('Nobody won, game over.'.center(ALIGN)) | |
def start_game(): | |
clear_console() | |
print('Welcome a Tic Tack Toe game!\n') | |
mainloop(read_human_symbol(), make_game_board()) | |
if __name__ == '__main__': | |
try: | |
start_game() | |
except KeyboardInterrupt: | |
print('\nGame interrupted.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment