Created
December 25, 2024 22:24
-
-
Save RashikShahjahan/47af34bd66c7eaf971238fa6f6d2aa94 to your computer and use it in GitHub Desktop.
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
import random | |
class OutOfBoundsError(Exception): | |
"""Raised when a move is out of bounds.""" | |
pass | |
class PositionOccupiedError(Exception): | |
"""Raised when a position is already occupied.""" | |
pass | |
class TicTacToe: | |
"""A class representing a Tic-tac-toe game. | |
The game board is represented as a 3x3 grid where players take turns | |
marking spaces with 'X' or 'O'. Positions are numbered 1-9 from left | |
to right, top to bottom. | |
""" | |
def __init__(self): | |
"""Initialize an empty Tic-tac-toe board with player X starting.""" | |
self.board = [[None for _ in range(3)] for _ in range(3)] | |
self.curr_player = 'X' | |
self.is_game_over = False | |
def _position_to_coordinates(self, position: int) -> tuple[int, int]: | |
"""Convert a board position (1-9) to grid coordinates (x,y). | |
Args: | |
position: Integer from 1-9 representing the board position | |
Returns: | |
tuple: (row, column) coordinates on the board | |
""" | |
return ((position - 1) // 3, (position - 1) % 3) | |
def _coordinates_to_position(self, x: int, y: int) -> int: | |
"""Convert grid coordinates to board position number. | |
Args: | |
x: Row number (0-2) | |
y: Column number (0-2) | |
Returns: | |
int: Board position number (1-9) | |
""" | |
return x * 3 + y + 1 | |
def make_move(self, position: int): | |
"""Make a move on the board. | |
Args: | |
position: Integer from 1-9 representing the board position | |
Raises: | |
OutOfBoundsError: If position is not between 1 and 9 | |
PositionOccupiedError: If the chosen position is already taken | |
""" | |
if position < 1 or position > 9: | |
raise OutOfBoundsError(f"Invalid position: {position}. Please enter a number between 1 and 9.") | |
x_pos, y_pos = self._position_to_coordinates(position) | |
if self.board[x_pos][y_pos] is not None: | |
raise PositionOccupiedError(f"Position {position} is already occupied.") | |
self.board[x_pos][y_pos] = self.curr_player | |
if self._is_win(): | |
self._display_board() | |
self.is_game_over = True | |
print(f"{self.curr_player} is the winner!") | |
elif self._is_draw(): | |
self._display_board() | |
self.is_game_over = True | |
print("\nGame is a draw!") | |
else: | |
self._switch_player() | |
def _switch_player(self): | |
"""Switch the current player between X and O.""" | |
self.curr_player = 'O' if self.curr_player == 'X' else 'X' | |
def _is_win(self): | |
"""Check if the current player has won the game. | |
Returns: | |
bool: True if current player has won, False otherwise | |
""" | |
for row in self.board: | |
if row[0] == row[1] == row[2] and row[0] is not None: | |
return True | |
for col in range(3): | |
if self.board[0][col] == self.board[1][col] == self.board[2][col] and self.board[0][col] is not None: | |
return True | |
if self.board[0][0] == self.board[1][1] == self.board[2][2] and self.board[0][0] is not None: | |
return True | |
if self.board[0][2] == self.board[1][1] == self.board[2][0] and self.board[0][2] is not None: | |
return True | |
return False | |
def _is_draw(self): | |
"""Check if the game is a draw (all positions filled). | |
Returns: | |
bool: True if game is a draw, False otherwise | |
""" | |
return all(cell is not None for row in self.board for cell in row) | |
def _computer_move(self): | |
"""Make a random move for the computer player. | |
Chooses a random available position on the board. | |
""" | |
available_positions = [] | |
for i in range(3): | |
for j in range(3): | |
if self.board[i][j] is None: | |
position = self._coordinates_to_position(i, j) | |
available_positions.append(position) | |
if available_positions: | |
position = random.choice(available_positions) | |
print(f"\nComputer chooses position {position}") | |
self.make_move(position) | |
def play(self): | |
"""Start and manage the game loop. | |
Handles player input, displays the game board, and manages turns | |
between human and computer players. | |
""" | |
print("Welcome to Tic-tac-toe!") | |
print("Enter a single number (1-9) to make your move:") | |
print("1 | 2 | 3") | |
print("---------") | |
print("4 | 5 | 6") | |
print("---------") | |
print("7 | 8 | 9") | |
while True: | |
opponent = input("\nPlay against computer or human? (c/h): ").lower() | |
if opponent in ['c', 'h']: | |
break | |
print("Please enter 'c' for computer or 'h' for human.") | |
is_computer_opponent = opponent == 'c' | |
if is_computer_opponent: | |
print("\nYou are X, computer is O") | |
else: | |
print("\nPlayer X goes first") | |
while not self.is_game_over: | |
self._display_board() | |
if is_computer_opponent and self.curr_player == 'O': | |
self._computer_move() | |
else: | |
print(f"\nPlayer {self.curr_player}'s turn") | |
try: | |
position = int(input("Enter your move (1-9): ")) | |
self.make_move(position) | |
except ValueError: | |
print("Invalid input! Please enter a single number between 1 and 9.") | |
except (OutOfBoundsError, PositionOccupiedError) as e: | |
print(e) | |
continue | |
def _display_board(self): | |
"""Display the current state of the game board.""" | |
print("\n") | |
for row in self.board: | |
print(" | ".join(cell if cell is not None else " " for cell in row)) | |
print("-" * 9) | |
game = TicTacToe() | |
game.play() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment