Skip to content

Instantly share code, notes, and snippets.

@RashikShahjahan
Created December 25, 2024 22:24
Show Gist options
  • Save RashikShahjahan/47af34bd66c7eaf971238fa6f6d2aa94 to your computer and use it in GitHub Desktop.
Save RashikShahjahan/47af34bd66c7eaf971238fa6f6d2aa94 to your computer and use it in GitHub Desktop.
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