-
-
Save TalhaProgrammer92/6b5f2ae1e740b6080b75d304ae34cd19 to your computer and use it in GitHub Desktop.
Code for the tic tac toe game we created in this video: https://youtu.be/E8fmDDtaHLU on CodeWithHarry channel on YouTube
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 random import randint, shuffle | |
| from os import system, mkdir, name | |
| from os.path import join, exists | |
| import time | |
| import sqlite3 as sq | |
| # * Check if folder 'save' exists or not | |
| if not exists('save'): | |
| mkdir('save') | |
| # * Function - Clear screen | |
| def clrscr() -> None: | |
| system('cls' if name == 'nt' else 'clear') | |
| ################################## | |
| # Player - Handle player data | |
| ################################## | |
| class Player: | |
| def __init__(self, **kwargs): | |
| self.__name: str = kwargs.get('name', 'unknown') | |
| self.__score: int = kwargs.get('score', 0) | |
| self.__symbol: str = kwargs.get('symbol', self.__name[randint(0, len(self.__name) - 1)]) | |
| # * Getters | |
| @property | |
| def name(self) -> str: | |
| return self.__name | |
| @property | |
| def score(self) -> int: | |
| return self.__score | |
| @property | |
| def symbol(self) -> str: | |
| return self.__symbol | |
| @property | |
| def get_data(self) -> list: | |
| return [self.__name, self.__symbol, str(self.__score)] | |
| @property | |
| def get_title(self) -> list[str]: | |
| return ['name', 'symbol', 'score'] | |
| # * Method - Increase score | |
| def increment_score(self) -> None: | |
| self.__score += 1 | |
| # * Method - Reset score | |
| def reset_score(self) -> None: | |
| self.__score = 0 | |
| # * Method - Representation | |
| def __repr__(self) -> str: | |
| return f'Name:\t{self.name}\nScore:\t{self.score}\nSymbol:\t{self.symbol}' | |
| ####################################### | |
| # Cell - Handle cell of game board | |
| ####################################### | |
| class Cell: | |
| def __init__(self, **kwargs): | |
| self.__index: int = kwargs.get('index', 0) | |
| self.symbol: str = kwargs.get('symbol', str(self.__index + 1)) | |
| # * Getter | |
| @property | |
| def index(self) -> int: | |
| return self.__index | |
| @property | |
| def is_empty(self) -> bool: | |
| return str(self.index + 1) == self.symbol | |
| # * Method - Equal comparision operator | |
| def __eq__(self, value: 'Cell') -> 'Cell': | |
| return self.symbol == value.symbol | |
| # * Method - Representation | |
| def __repr__(self) -> str: | |
| return self.symbol | |
| ################################ | |
| # Board - Handle game board | |
| ################################ | |
| class Board: | |
| def __init__(self): | |
| self.grid: list[list[Cell]] = [] | |
| self.__available: list[int] = [0, 1, 2, 3, 4, 5, 6, 7, 8] | |
| # * Getters | |
| @property | |
| def available_cells(self) -> list[int]: | |
| return self.__available | |
| @property | |
| def get_data(self) -> list: | |
| data: list = [] | |
| for i in range(3): | |
| for j in range(3): | |
| data.append([i, j, self.grid[i][j].symbol]) | |
| return data | |
| @property | |
| def get_title(self) -> list[str]: | |
| return ['row', 'column', 'symbol'] | |
| # * Method - Remove available | |
| def remove_from_available(self, index: int) -> None: | |
| self.__available.remove(index) | |
| # * Method - Reset grid | |
| def reset(self) -> None: | |
| # ? Empty case | |
| if len(self.grid) == 0: | |
| count: int = 0 | |
| for i in range(3): | |
| l: list[Cell] = [] | |
| for j in range(3): | |
| l.append(Cell(index=count)) | |
| count += 1 | |
| self.grid.append(l) | |
| # ? Filled case | |
| else: | |
| for row in self.grid: | |
| for cell in row: | |
| cell = Cell(index=cell.index) | |
| # * Method - Fill grid | |
| def fill(self, grid: list[list[Cell]]) -> None: | |
| for i in range(3): | |
| for j in range(3): | |
| cell: Cell = grid[i][j] | |
| # ? If the cell is not empty | |
| if not cell.is_empty: | |
| self.remove_from_available(cell.index) | |
| self.grid[i][j] = cell | |
| # * Method - Check if the board is filled | |
| def is_filled(self) -> bool: | |
| for row in self.grid: | |
| for cell in row: | |
| # ? If not empty | |
| if not cell.is_empty: | |
| return False | |
| # ? If empty | |
| return True | |
| # * Method - Display grid | |
| def display(self) -> None: | |
| for i in range(3): | |
| for j in range(3): | |
| print('', self.grid[i][j], end=' |' if j < 2 else '') | |
| print('\n---+---+---' if i < 2 else '\n') | |
| ######################################################################## | |
| # Data Handler - Handles game state data for save/load feature | |
| ######################################################################## | |
| class DataHandler: | |
| def __init__(self, **kwargs): | |
| self.__connection: sq.Connection = sq.connect(join('save', kwargs.get('file', time.strftime('%Y-%m-%d %H:%M:%S')) + '.db')) | |
| self.__cursor: sq.Cursor = self.__connection.cursor() | |
| # * Method - Execute query | |
| def execute(self, query: str) -> None: | |
| self.__cursor.execute(query) | |
| self.__connection.commit() | |
| # * Method - Create tables | |
| def create_tables(self) -> None: | |
| # ? Player table | |
| self.execute(""" | |
| CREATE TABLE IF NOT EXISTS Player( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| name TEXT NOT NULL, | |
| symbol TEXT NOT NULL, | |
| score INT NOT NULL | |
| ); | |
| """) | |
| # ? Board table | |
| self.execute(""" | |
| CREATE TABLE IF NOT EXISTS Board( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| row INT NOT NULL, | |
| column INT NOT NULL, | |
| symbol TEXT NOT NULL | |
| ); | |
| """) | |
| # ? Game table | |
| self.execute(""" | |
| CREATE TABLE IF NOT EXISTS Game( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| turn INT NOT NULL, | |
| game_over INT NOT NULL, | |
| matches INT NOT NULL | |
| ); | |
| """) | |
| # * Method - Insert data | |
| def insert_into(self, table: str, titles: list[str], data: list) -> None: | |
| self.execute(f""" | |
| INSERT INTO {table}({','.join(titles)}) VALUES({','.join(data)}); | |
| """) | |
| # * Method - Reterieve data | |
| def reterieve_data(self, table: str) -> list: | |
| self.execute(f"SELECT * FROM {table}") | |
| return self.__cursor.fetchall() | |
| # * Method - Close connection | |
| def close(self) -> None: | |
| self.__connection.close() | |
| ######################### | |
| # Game - Handle game | |
| ######################### | |
| class Game: | |
| def __init__(self, **kwargs): | |
| self.__board: Board = kwargs.get('board', Board()) | |
| self.__players: list[Player] = kwargs.get('players', [Player(), Player()]) | |
| self.__turn: int = kwargs.get('turn', 0) | |
| self.__matches: int = kwargs.get('matches', 0) | |
| self.__game_over: bool = kwargs.get('game_over', False) | |
| # * Getters | |
| @property | |
| def get_data(self) -> list: | |
| return [str(self.__turn), str(int(self.__game_over)), str(self.__matches)] | |
| @property | |
| def get_title(self) -> list[str]: | |
| return ['turn', 'game_over', 'matches'] | |
| # * Method - Reset game | |
| def reset(self) -> None: | |
| self.__board.reset() | |
| self.__matches = 0 | |
| self.__game_over = False | |
| for player in self.__players: | |
| player.reset_score() | |
| # * Method - Check winner (0-none, 1-winner, 2-draw) | |
| def __check_winner(self) -> int: | |
| # ? Check row & column | |
| for i in range(3): | |
| if (self.__board.grid[i][0].symbol == self.__board.grid[i][1].symbol and | |
| self.__board.grid[i][1].symbol == self.__board.grid[i][2].symbol and | |
| not self.__board.grid[i][1].is_empty): | |
| return 1 | |
| if (self.__board.grid[0][i].symbol == self.__board.grid[1][i].symbol and | |
| self.__board.grid[1][i].symbol == self.__board.grid[2][i].symbol and | |
| not self.__board.grid[1][i].is_empty): | |
| return 1 | |
| # ? Check Diagonal | |
| if (self.__board.grid[0][0].symbol == self.__board.grid[1][1].symbol and | |
| self.__board.grid[1][1].symbol == self.__board.grid[2][2].symbol and | |
| not self.__board.grid[1][1].is_empty): | |
| return 1 | |
| elif (self.__board.grid[0][2].symbol == self.__board.grid[1][1].symbol and | |
| self.__board.grid[1][1].symbol == self.__board.grid[2][0].symbol and | |
| not self.__board.grid[1][1].is_empty): | |
| return 1 | |
| # ? Check if draw | |
| if self.__board.is_filled(): | |
| return 2 | |
| # ? No winner found | |
| return 0 | |
| # * Method - Update game state | |
| def __update_turn(self) -> None: | |
| self.__turn ^= 1 | |
| # * Method - Start game | |
| def start_game(self) -> None: | |
| while not self.__game_over: | |
| # ? Clear Screen | |
| clrscr() | |
| # ? Display board | |
| self.__board.display() | |
| # ? Display turn | |
| print(f'Turn of {self.__players[self.__turn].name}!') | |
| # ? Get input | |
| while True: | |
| # try: | |
| index = input('Enter index: ') | |
| if index == 'save': | |
| handler: DataHandler = DataHandler(file='Save01') | |
| handler.create_tables() | |
| # ! Player data | |
| for player in self.__players: | |
| handler.insert_into('Player', player.get_title, player.get_data) | |
| # ! Board data | |
| for bdata in self.__board.get_data(): | |
| handler.insert_into('Board', self.__board.get_title, bdata) | |
| # ! Game data | |
| handler.insert_into('Game', self.get_title, self.get_data) | |
| handler.close() | |
| print("Saved Successfull!") | |
| index = int(index) | |
| if index - 1 in self.__board.available_cells: | |
| break | |
| else: | |
| print('The selected cell is not empty') | |
| # except Exception as e: | |
| # print(f'Incorrect entry! {e}') | |
| # ? Place symbol | |
| index -= 1 | |
| i: int = int(index / 3) | |
| j: int = index % 3 | |
| self.__board.grid[i][j].symbol = self.__players[self.__turn].symbol | |
| self.__board.remove_from_available(index) | |
| # ? Check winner | |
| cw: int = self.__check_winner() | |
| # ? If a winner found | |
| if cw == 1: | |
| print(f'Winner is {self.__players[self.__turn].name}!') | |
| self.__players[self.__turn].increment_score() | |
| self.__game_over = True | |
| # ? If draw | |
| elif cw == 2: | |
| print('Game is Draw!') | |
| self.__game_over = True | |
| # ? Update game state | |
| self.__update_turn() | |
| # ? Increment the match variable | |
| self.__matches += 1 | |
| if __name__ == "__main__": | |
| board: Board = Board() | |
| board.reset() | |
| players: list[Player] = [ | |
| Player(name='Talha', symbol='T'), | |
| Player(name='Rayan', symbol='R') | |
| ] | |
| game: Game = Game(board=board, players=players) | |
| game.start_game() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment