Created
April 19, 2018 14:54
-
-
Save ma0c/795604aeed2badac82b9445345eed3ae to your computer and use it in GitHub Desktop.
Tic Tac Toe Minimal Python
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 requests | |
import time | |
BOARD_WIDTH = 3 | |
BOARD_HEIGHT = 3 | |
WELCOME_MESSAGE = "Welcome to tictactoe press: \n1. To play\n2. For Instructions\n3. Exit:\n" | |
UNAVAILABLE_OPTION = "Unavailable option" | |
GETTING_GAME_MESSAGE = "We're checking for game availability" | |
INSTRUCTION_MESSAGE = "To play put a number between 1 and 9 that represent the grid" | |
MAKE_MOVE_MESSAGE = "Make your move [1-9]:\n" | |
INVALID_MOVE_MESSAGE = "Invalid move, try again" | |
SLOT_OCCUPIED = "Slot Occupied" | |
WAITING_FOR_PLAYER = "Waiting for player" | |
EXIT_MESSAGE = "Have a nice day" | |
INSTRUCTION_GRID = [str(x) for x in range(1, 10)] | |
UNAVAILABLE_SERVER = "Unavailable server, please try later" | |
BACKEND_URL = "http://localhost:8000" | |
CREATE_OR_JOIN_ENDPOINT = "/create-or-join/" | |
MAKE_MOVE_ENDPOINT = "/make-move/" | |
GAME_STATUS_ENDPOINT = "/game-status/" | |
GET_BOARD_ENDPOINT = "/get-board/" | |
def print_board(board_str): | |
""" | |
from client import print_board | |
print_board("XO XO X ") | |
:param board_str: | |
:return: | |
""" | |
# separated_board = "|".join(board_str) | |
print("|".join(board_str[0:3])) | |
print("|".join(board_str[3:6])) | |
print("|".join(board_str[6:9])) | |
def read_valid_move(board): | |
user_input = input(MAKE_MOVE_MESSAGE) | |
try: | |
int_move = int(user_input.strip()) | |
if 1 <= int_move <= 9: | |
if board[int_move-1] == " ": | |
return int_move | |
else: | |
print(SLOT_OCCUPIED) | |
except ValueError: | |
pass | |
print(INVALID_MOVE_MESSAGE) | |
return read_valid_move(board) | |
def main_menu(): | |
user_input = input(WELCOME_MESSAGE) | |
try: | |
int_value = int(user_input.strip()) | |
if int_value == 1: | |
loop() | |
elif int_value == 2: | |
instructions() | |
elif int_value == 3: | |
exit_game() | |
else: | |
print("Main main Not main main option", int_value) | |
print(UNAVAILABLE_OPTION) | |
except ValueError: | |
print("Main main Not menu option") | |
print(UNAVAILABLE_OPTION) | |
def make_move(game_id, move_index, player): | |
payload = { | |
'id': game_id, | |
'move_index': move_index, | |
'player': player | |
} | |
make_move_response = requests.post( | |
"{}{}".format(BACKEND_URL, MAKE_MOVE_ENDPOINT), | |
json=payload | |
) | |
if make_move_response.status_code == 200: | |
return make_move_response.json() | |
else: | |
print(UNAVAILABLE_SERVER) | |
exit(0) | |
def check_status(game_id): | |
check_status_response = requests.get( | |
"{}{}?id={}".format(BACKEND_URL, GAME_STATUS_ENDPOINT, game_id) | |
) | |
if check_status_response.status_code == 200: | |
response_json = check_status_response.json() | |
return response_json.get('turn', game_id) | |
else: | |
return game_id | |
def get_board(game_id): | |
check_status_response = requests.get( | |
"{}{}?id={}".format(BACKEND_URL, GET_BOARD_ENDPOINT, game_id) | |
) | |
if check_status_response.status_code == 200: | |
response_json = check_status_response.json() | |
return response_json | |
else: | |
return dict() | |
def wait_for_play(current_game, turn): | |
while check_status(current_game) <= turn: | |
print(WAITING_FOR_PLAYER) | |
time.sleep(1) | |
def loop(): | |
print(WELCOME_MESSAGE) | |
create_or_join_request = requests.get("{}{}".format(BACKEND_URL, CREATE_OR_JOIN_ENDPOINT)) | |
if create_or_join_request.status_code == 200: | |
response_json = create_or_join_request.json() | |
current_game = response_json.get('id', 0) | |
last_player = response_json.get('last_player', 'O') | |
player = "X" if last_player == "O" else "O" | |
print("Game number {}".format(current_game)) | |
board = [' ' for _ in range(9)] | |
turn = response_json.get('turn', 0) | |
print("You are {}".format(player)) | |
if player == "O": | |
wait_for_play(current_game, turn) | |
response_json = get_board(current_game) | |
while not response_json.get("finished", True): | |
board = response_json.get('board', board) | |
print_board(board) | |
response_json = make_move( | |
current_game, | |
read_valid_move(board), | |
player | |
) | |
turn = response_json.get('turn', turn) | |
board = response_json.get('board', board) | |
print_board(board) | |
print(response_json.get('message', '')) | |
if not response_json.get("finished", True): | |
# The game is not ended | |
wait_for_play(current_game, turn) | |
response_json = get_board(current_game) | |
if response_json.get("finished", False): | |
# The opponent won the game | |
print_board(response_json.get('board', board)) | |
print(response_json.get('message', '')) | |
else: | |
# The game ended because I win | |
pass | |
main_menu() | |
else: | |
print(UNAVAILABLE_SERVER) | |
def instructions(): | |
print(INSTRUCTION_MESSAGE) | |
print(print_board(INSTRUCTION_GRID)) | |
main_menu() | |
def exit_game(): | |
print(EXIT_MESSAGE) | |
exit(0) | |
if __name__ == '__main__': | |
main_menu() |
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
certifi==2018.1.18 | |
chardet==3.0.4 | |
Django==2.0.4 | |
idna==2.6 | |
pytz==2018.4 | |
requests==2.18.4 | |
urllib3==1.22 |
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 sys | |
import json | |
from django.conf import settings | |
from django.conf.urls import url | |
from django.core.management import execute_from_command_line | |
from django.http import JsonResponse | |
from django.views.generic import View | |
from django.views.decorators.csrf import csrf_exempt | |
settings.configure( | |
DEBUG=True, | |
SECRET_KEY='A-random-secret-key!', | |
ROOT_URLCONF=sys.modules[__name__], | |
) | |
INVALID_PAYLOAD = "Invalid Payload" | |
INVALID_MOVE_MESSAGE = "Invalid Move" | |
GAME_NOT_FOUND_MESSAGE = "Game not found" | |
TURN_INVALID = "Turn invalid please wait for opponent" | |
GAME_ENDED_WITH_WINNER = "The game is ended and the winner is {}" | |
GAME_ENDED_WITH_DRAW = "The game is ended there is a draw" | |
GAMES = list() | |
# This matrix could be calculated automatically | |
WINNER_MATRIX = [ | |
[0, 1, 2], # 0 | |
[3, 4, 5], # 1 | |
[6, 7, 8], # 2 | |
[0, 3, 6], # 3 | |
[1, 4, 7], # 4 | |
[2, 5, 8], # 5 | |
[0, 4, 8], # 6 | |
[2, 4, 6] # 7 | |
] | |
# Also this dict but for AI this should be a better option | |
CHECK_FOR_WIN = { | |
0: [0, 3, 6], | |
1: [0, 4], | |
2: [0, 5, 7], | |
3: [1, 3], | |
4: [1, 4, 6, 7], | |
5: [1, 5], | |
6: [2, 3, 7], | |
7: [2, 4], | |
8: [2, 5, 6], | |
} | |
class CreateOrJoin(View): | |
def get(self, request, *args, **kwargs): | |
if not GAMES or GAMES[-1]['turn'] > 1: | |
# There are no games | |
json_response = self.create_new_game() | |
json_response['id'] = len(GAMES) | |
GAMES.append(json_response) | |
json_response['last_player'] = "O" | |
else: | |
# Return last game | |
current_game = GAMES[-1] | |
json_response = current_game.copy() | |
json_response['last_player'] = "X" | |
print(GAMES) | |
return JsonResponse(json_response) | |
def create_new_game(self): | |
new_game = dict() | |
new_game['board'] = [' ' for _ in range(9)] | |
new_game['turn'] = 0 | |
new_game['finished'] = False | |
new_game['winner'] = '' | |
return new_game | |
class MakeMove(View): | |
@csrf_exempt | |
def dispatch(self, request, *args, **kwargs): | |
return super(MakeMove, self).dispatch(request, *args, **kwargs) | |
@staticmethod | |
def check_winner(board, current_player, play): | |
for possible_win in CHECK_FOR_WIN[play]: | |
is_a_win = True | |
for slot in WINNER_MATRIX[possible_win]: | |
if board[slot] != current_player: | |
is_a_win = False | |
break | |
if is_a_win: | |
return True | |
return False | |
@staticmethod | |
def check_game_end(turn): | |
return turn >= 9 | |
def post(self, request, *args, **kwargs): | |
body = json.loads(request.body) | |
print(body) | |
response = dict() | |
game_index = body.get('id', None) | |
move_index = body.get('move_index', None) | |
player = body.get('player', None) | |
if game_index is None or move_index is None or player is None: | |
response['status'] = 400 | |
response['message'] = INVALID_PAYLOAD | |
else: | |
try: | |
game_index = int(game_index) | |
current_game = GAMES[game_index] | |
if current_game['board'][move_index - 1] == " ": | |
if current_game['last_player'] == player: | |
response['status'] = 400 | |
response['message'] = TURN_INVALID | |
else: | |
current_game['board'][move_index - 1] = player | |
current_game['last_player'] = player | |
current_game['turn'] += 1 | |
if self.check_winner( | |
current_game['board'], | |
player, | |
move_index -1 | |
): | |
current_game['finished'] = True | |
current_game['winner'] = player | |
response['finished'] = True | |
response['message'] = GAME_ENDED_WITH_WINNER.format(player) | |
elif self.check_game_end(current_game['turn']): | |
current_game['finished'] = True | |
response['finished'] = True | |
response['message'] = GAME_ENDED_WITH_DRAW | |
response['status'] = 200 | |
response['message'] = response.get('message', '') | |
response['board'] = current_game['board'] | |
response['last_player'] = current_game['last_player'] | |
response['turn'] = current_game['turn'] | |
else: | |
response['status'] = 400 | |
response['message'] = INVALID_MOVE_MESSAGE | |
except ValueError: | |
response['status'] = 400 | |
response['message'] = INVALID_MOVE_MESSAGE | |
except IndexError: | |
response['status'] = 400 | |
response['message'] = GAME_NOT_FOUND_MESSAGE | |
response['finished'] = response.get('finished', False) | |
return JsonResponse(response) | |
class GameStatus(View): | |
def get(self, request, *args, **kwargs): | |
response = dict() | |
game_index = request.GET.get('id', None) | |
if game_index is None: | |
response['status'] = 400 | |
response['message'] = INVALID_PAYLOAD | |
else: | |
try: | |
game_index = int(game_index) | |
game = GAMES[game_index] | |
response['status'] = 200 | |
response['turn'] = game['turn'] | |
except ValueError: | |
response['status'] = 400 | |
response['message'] = INVALID_PAYLOAD | |
except IndexError: | |
response['status'] = 400 | |
response['message'] = GAME_NOT_FOUND_MESSAGE | |
return JsonResponse(response) | |
class GetBoard(View): | |
def get(self, request, *args, **kwargs): | |
response = dict() | |
game_index = request.GET.get('id', None) | |
if game_index is None: | |
response['status'] = 400 | |
response['message'] = INVALID_PAYLOAD | |
else: | |
try: | |
game_index = int(game_index) | |
game = GAMES[game_index] | |
response['status'] = 200 | |
response['turn'] = game['turn'] | |
response['board'] = game['board'] | |
response['finished'] = game['finished'] | |
if game['finished']: | |
if game['winner']: | |
response['message'] = GAME_ENDED_WITH_WINNER.format(game['winner']) | |
else: | |
response['message'] = GAME_ENDED_WITH_DRAW | |
response['message'] = response.get('message', '') | |
except ValueError: | |
response['status'] = 400 | |
response['message'] = INVALID_PAYLOAD | |
except IndexError: | |
response['status'] = 400 | |
response['message'] = GAME_NOT_FOUND_MESSAGE | |
return JsonResponse(response) | |
urlpatterns = [ | |
url(r'^create-or-join/$', CreateOrJoin.as_view()), | |
url(r'^make-move/$', MakeMove.as_view()), | |
url(r'^game-status/$', GameStatus.as_view()), | |
url(r'^get-board/$', GetBoard.as_view()), | |
] | |
if __name__ == '__main__': | |
execute_from_command_line(sys.argv) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment