Created
May 7, 2019 04:59
-
-
Save AlexanderPease/ff88bd8cf359b4224b33510a7dcbdab0 to your computer and use it in GitHub Desktop.
Tic-tac-toe implementation for Recurse pair programming interview - Zander Pease
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
""" | |
Tic Tac Toe game submitted for Recurse Center pair programming interview. | |
""" | |
from copy import copy | |
import unittest | |
EMPTY_BOARD = [ | |
False, False, False, False, False, False, False, False, False | |
] | |
WINNING_BOARDS = [ | |
# Horizontal | |
'123', | |
'456', | |
'789', | |
# Vertical | |
'147', | |
'258', | |
'369', | |
# Diagonal | |
'159', | |
'357' | |
] | |
class TicTacToe(object): | |
"""A single Tac-Tac-Toe game. | |
A board is represented as a list of available moves 1-9. | |
An empty space evaluates to False. | |
""" | |
def __init__(self, **kwargs): | |
self.board = copy(kwargs.get('board', EMPTY_BOARD)) | |
if len(self.board) is not 9: | |
raise TypeError('Board is incorrect length') | |
self.symbols = 'XO' | |
def __repr__(self): | |
"""Example of string representation: | |
O | X | | |
| X | O | |
X | O | O | |
""" | |
output = '' | |
for idx, space in enumerate(self.board): | |
if space: | |
output += f'{space} ' | |
else: | |
output += ' ' | |
if (idx + 1) % 3 == 0: | |
output += '\n' | |
else: | |
output += '| ' | |
return output | |
def position(self, position): | |
"""Returns value of space at position.""" | |
return self.board[position - 1] | |
def move(self, player, position): | |
"""Makes a move on position for player. Returns True if successful. | |
Kwargs: | |
player is in self.symbols, ex. 'X'. | |
position is a 1-9 for the position on the board. | |
Possible exceptions: | |
TypeError if player not recognized. | |
IndexError if space not recognized. | |
ValueError if space already occupied. | |
NB: self.board[0] corresponds to position 1 on the board.""" | |
input_err = 'Input must be an integer 1-9, try again...' | |
# Input error checks | |
if player not in self.symbols: | |
raise IndexError(input_err) | |
try: | |
board_idx = int(position) - 1 | |
except ValueError: | |
raise ValueError(input_err) | |
if board_idx < 0: | |
raise IndexError(input_err) | |
elif self.board[board_idx]: | |
raise ValueError('That space is already taken! Try again...') | |
self.board[board_idx] = player | |
return True | |
def won_by(self, player): | |
"""Returns True if player has won.""" | |
for board in WINNING_BOARDS: | |
in_a_row = 0 | |
for space in board: | |
board_idx = int(space) - 1 | |
if self.board[board_idx] != player: | |
break | |
else: | |
in_a_row += 1 | |
if in_a_row == 3: | |
return True | |
TEST_BOARD = ['X', 'X', False, False, 'O', 'O', 'X', 'O', False] | |
class TestTicTacToe(unittest.TestCase): | |
def test_init(self): | |
"""Test initializing board.""" | |
# Base init | |
self.assertTrue(TicTacToe().board == EMPTY_BOARD) | |
# Custom init | |
self.assertTrue( | |
TicTacToe(board=TEST_BOARD).board == TEST_BOARD | |
) | |
# Illegal init | |
tic = None | |
short_board = ['X', 'X', False, False] | |
try: | |
tic = TicTacToe(board=short_board) | |
except TypeError: | |
pass | |
except Exception: | |
raise Exception('Unexpected exception thrown on TicTacToe.init') | |
if tic: | |
raise Exception('Board was illegally created') | |
def test_win(self): | |
"""Test winning conditions.""" | |
horizontal = TicTacToe(board=[ | |
'X', 'X', 'X', | |
False, False, False, | |
False, False, False | |
]) | |
self.assertTrue(horizontal.won_by('X')) | |
self.assertFalse(horizontal.won_by('O')) | |
diagonal = TicTacToe(board=[ | |
'O', 'X', 'X', | |
False, 'O', False, | |
'X', False, 'O' | |
]) | |
self.assertTrue(diagonal.won_by('O')) | |
self.assertFalse(diagonal.won_by('X')) | |
def test_move(self): | |
"""Test inputting a move.""" | |
tic = TicTacToe(board=TEST_BOARD) | |
# Legal move | |
self.assertTrue(tic.move('X', 3)) | |
# Illegal move, space is already occupied | |
try: | |
tic.move('X', 3) | |
raise Exception('Move overwrote a value') | |
except ValueError: | |
pass | |
try: | |
tic.move('X', 100) # Illegal move, space DNE | |
tic.move('X', 0) # Illegal move, array vulnerability | |
raise Exception('Move outside the board was allowed') | |
except IndexError: | |
pass | |
# Illegal player | |
try: | |
tic.move('Z', 4) | |
raise Exception('Illegal player was allowed') | |
except IndexError: | |
pass | |
def test_print(self): | |
"""Test output of the board's state.""" | |
tic = TicTacToe(board=TEST_BOARD) | |
printout = 'X | X | \n | O | O \nX | O | \n' | |
self.assertTrue( | |
str(tic) == printout | |
) | |
if __name__ == "__main__": | |
# Run a two-player game of tic-tac-toe | |
print('\nWelcome to Tic-Tac-Toe!') | |
print('Spaces are labeled 1-9, just like a keypad.') | |
print('Player 1 is X, Player 2 is O\n') | |
tic = TicTacToe() | |
counter = 1 | |
while True: | |
# Identify player | |
if counter % 2: | |
player = 1 | |
symbol = 'X' | |
else: | |
player = 2 | |
symbol = 'O' | |
print(f'\nCurrent board:\n{tic}') | |
choice = input(f"Player {player}'s move: ") | |
try: | |
tic.move(symbol, choice) | |
except Exception as e: | |
print(e) | |
continue | |
if tic.won_by(symbol): | |
print(f"\nPlayer {player} wins in {counter} moves!\n") | |
print(f'Final board:\n{tic}') | |
break | |
elif counter == 9: | |
print('\nGame ends in a tie!') | |
print(f'Final board:\n{tic}') | |
break | |
# Increment to next move | |
counter += 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
#include "stdafx.h"
#include <Windows.h>
#include
using namespace std;
enum Dir {
Up = 0, Left = 1, Down = 2, Right = 3
};
struct coord {
int x, y;
bool operator==(const coord & o) const {
return x == o.x && y == o.y;
}
};
struct Food {
coord pos;
char texture = '@';
};
class Snake {
coord body[1000];
coord & head;
int size;
bool hasGrown;
Dir dir;
public:
Dir getDir() const {
return dir;
}
};
const char Snake::body_texture = 'x';
const char Snake::head_texture = 'X';
class Game {
Snake snake;
Food food;
coord board;
friend class Drawer;
public:
Game(coord board) : board(board) {
spawnFood();
}
};
class Drawer {
Game & game;
public:
Drawer(Game & g) : game(g) {}
private:
};
int main() {
Game g({ 80, 21 });
}