Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- """
- Tic Tac Toe
- Author: @kevtimova
- Usage:
- # To Test
- python -m unittest tic_tac_toe.TestTicTacToe
- # To Play
- python tic_tac_toe.py
- Dependencies:
- numpy
- """
- import numpy as np
- import unittest
- import sys
- class Board:
- def __init__(self, size):
- # Board size
- self.size = size
- # Initialize board array to 0s
- self.board = np.zeros((self.size, self.size))
- # Track players' turns: 0 (first) or 1 (second)
- self.turn = 0
- # Store number of empty cells
- self.empty = self.size*self.size
- # Arrays storing sum across all rows, columns, and diagonals
- self.sum_rows = np.zeros(self.size)
- self.sum_cols = np.zeros(self.size)
- self.sum_diag = np.zeros(2)
- def is_full(self):
- return not(self.empty)
- def add_move(self, i, j, player):
- # Check if move is valid
- if i < self.size and j < self.size and self.board[i][j] == 0:
- # Record player's move on the board
- self.board[i][j] = 1 if player.first else -1
- # Update sums across row and column associated with the move
- self.sum_rows[i] += self.board[i][j]
- self.sum_cols[j] += self.board[i][j]
- # Update sum of diagonal(s) if the move lies on any of them
- if i == j:
- self.sum_diag[0] += self.board[i][j]
- if i + j == self.size - 1:
- self.sum_diag[1] += self.board[i][j]
- # Decrease number of empty cells by 1
- self.empty += -1
- # Switch turns
- self.turn = 1 - self.turn
- return True
- # Return False if move is not valid
- return False
- def __repr__(self):
- rows = []
- for i in range(self.size):
- row = []
- for j in range(self.size):
- if self.board[i][j] == 0:
- row.append("_")
- elif self.board[i][j] == 1:
- row.append("X")
- else:
- row.append("O")
- row = " ".join(row)
- rows.append(row)
- return "\n".join(rows)
- class Player(object):
- def __init__(self):
- self.name = None
- @property
- def first(self):
- raise NotImplementedError
- def __repr__(self):
- return "Player {}".format(self.name)
- class PlayerOne(Player):
- def __init__(self):
- super(PlayerOne, self).__init__()
- self.name = "A"
- @property
- def first(self):
- return True
- class PlayerTwo(Player):
- def __init__(self):
- super(PlayerTwo, self).__init__()
- self.name = "B"
- @property
- def first(self):
- return False
- class Game:
- def __init__(self, size):
- self.size = size
- self.board = Board(self.size)
- self.players = [PlayerOne(), PlayerTwo()]
- self.turn = 0
- self.over = False
- def winning_move(self, i, j):
- # Game is over if any column, row, or diagonal sums to +/- board size
- over_scores = [-self.size, self.size]
- if self.board.sum_rows[i] in over_scores:
- return True
- elif self.board.sum_cols[j] in over_scores:
- return True
- elif i == j and self.board.sum_diag[0] in over_scores:
- return True
- elif i+j == self.size - 1 and self.board.sum_diag[1] in over_scores:
- return True
- return False
- def play(self):
- # Play while the game is not over
- while not(self.board.is_full()) and not(self.over):
- current_player = self.players[self.board.turn]
- print "Player {}'s move: (please enter comma-separated integer indices, e.g. 0,0)".format(current_player.name)
- # Obtain player's input
- try:
- i, j = [int(num) for num in raw_input().strip().split(",")]
- except:
- print "Invalid move.\nInput format: comma-separated integer indices, e.g. 0,0."
- continue
- # Check if move was valid
- success = self.board.add_move(i, j, current_player)
- if not success:
- print "Invalid move. Indices outside of allowed range or field already claimed."
- continue
- # Print board
- print self.board
- # Check if game is over
- self.over = self.winning_move(i, j)
- if self.over:
- print "Player {} is the winner. Game over!".format(current_player.name)
- else:
- if self.board.is_full():
- print "No winner. Game over!"
- class TestTicTacToe(unittest.TestCase):
- # Verify that initial board is empty
- def test_board_empty(self):
- test_board = Board(3)
- self.assertTrue(test_board.empty == test_board.size**2)
- # Verify that a full board is full
- def test_board_full(self):
- test_board = Board(3)
- test_player = PlayerOne()
- for i in range(test_board.size):
- for j in range(test_board.size):
- test_board.add_move(i, j, test_player)
- self.assertTrue(test_board.is_full())
- # Verify that winning moves are detected
- def test_winning_move(self):
- # Test game and players
- test_game = Game(3)
- test_player_one = test_game.players[0]
- test_player_two = test_game.players[1]
- # Winning configuration
- test_game.board.add_move(1, 1, test_player_one)
- self.assertFalse(test_game.winning_move(1, 1))
- test_game.board.add_move(0, 0, test_player_two)
- self.assertFalse(test_game.winning_move(0, 0))
- test_game.board.add_move(2, 0, test_player_one)
- self.assertFalse(test_game.winning_move(2, 0))
- test_game.board.add_move(1, 0, test_player_two)
- self.assertFalse(test_game.winning_move(1, 0))
- test_game.board.add_move(0, 2, test_player_one)
- self.assertTrue(test_game.winning_move(0, 2))
- # No winner
- test_game = Game(3)
- test_game.board.add_move(1, 1, test_player_one)
- self.assertFalse(test_game.winning_move(1, 1))
- test_game.board.add_move(0, 0, test_player_two)
- self.assertFalse(test_game.winning_move(0, 0))
- test_game.board.add_move(2, 0, test_player_one)
- self.assertFalse(test_game.winning_move(2, 0))
- test_game.board.add_move(0, 2, test_player_two)
- self.assertFalse(test_game.winning_move(0, 2))
- test_game.board.add_move(0, 1, test_player_one)
- self.assertFalse(test_game.winning_move(0, 1))
- test_game.board.add_move(2, 1, test_player_two)
- self.assertFalse(test_game.winning_move(2, 1))
- test_game.board.add_move(1, 0, test_player_one)
- self.assertFalse(test_game.winning_move(1, 0))
- test_game.board.add_move(1, 2, test_player_two)
- self.assertFalse(test_game.winning_move(1, 2))
- test_game.board.add_move(2, 2, test_player_one)
- self.assertFalse(test_game.winning_move(2, 2))
- self.assertTrue(test_game.board.is_full())
- # Verify that players are interchaged
- def test_next_turn(self):
- # Test game and players
- test_game = Game(3)
- test_player_one = test_game.players[0]
- test_player_two = test_game.players[1]
- # First player's initial turn
- current_turn = test_game.board.turn
- self.assertTrue(current_turn == 0)
- # Second player's turn
- test_game.board.add_move(1, 1, test_player_one)
- current_turn = test_game.board.turn
- self.assertTrue(current_turn == 1)
- # First player's next turn
- test_game.board.add_move(0, 0, test_player_two)
- current_turn = test_game.board.turn
- self.assertTrue(current_turn == 0)
- def run_game():
- while True:
- # Get integer board size
- print "Board size (please enter a positive integer):"
- try:
- n = int(raw_input())
- except:
- print "Invalid board size."
- continue
- # Play if board size is positive
- if n > 0:
- game = Game(n)
- game.play()
- break
- else:
- print "Invalid board size."
- if __name__ == "__main__":
- run_game()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement