Skip to content

Instantly share code, notes, and snippets.

@AlexanderPease
Created May 7, 2019 04:59
Show Gist options
  • Save AlexanderPease/ff88bd8cf359b4224b33510a7dcbdab0 to your computer and use it in GitHub Desktop.
Save AlexanderPease/ff88bd8cf359b4224b33510a7dcbdab0 to your computer and use it in GitHub Desktop.
Tic-tac-toe implementation for Recurse pair programming interview - Zander Pease
"""
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
@anas2432
Copy link

#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;
}

static const char body_texture, head_texture;
Snake(): head(body[0]), dir(Right), size(1), hasGrown(false) {
	for (int c = 0; c < 1000; ++c) {
		body[c] = { -1, -1 };
	}
	head = { 5, 5 };
}

void turn(Dir dir) {
	this->dir = dir;
}

const coord & getHead() {
	return head;
}

bool inside(coord pos) {
	for (int c = 0; c < size; ++c) {
		if (pos == body[c]) {
			return true;
		}
	}
	return false;
}

void step() {
	coord newPos = head;
	switch (dir) {
	case Up: newPos.y -= 1; break;
	case Left: newPos.x -= 1; break;
	case Down: newPos.y += 1; break;
	case Right: newPos.x += 1; break;
	}

	if (newPos == body[1]) {
		return;
	}

	if (hasGrown) {
		++size;
		hasGrown = false;
	}

	for (int c = size - 1; c > 0; --c) {
		body[c] = body[c - 1];
	}

	head = newPos;
}

void grow() {
	hasGrown = true;
}

};

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();
}

void spawnFood() {
	do {
		food.pos.x = rand() % board.x;
		food.pos.y = rand() % board.y;
	} while (snake.inside(food.pos));
}

bool step() {
	snake.step();
	coord head = snake.getHead();
	if (food.pos == head) {
		snake.grow();
		spawnFood();
	}
	
	if (head.x < 0 || head.y < 0
		|| head.x > board.x || head.y > board.y) {
		return false;
	}

	return true;
}

void read_input() {
	if (GetAsyncKeyState(VK_LEFT)) {
		snake.turn(Left);
	}
	else if (GetAsyncKeyState(VK_RIGHT)) {
		snake.turn(Right);
	}
	else if (GetAsyncKeyState(VK_UP)) {
		snake.turn(Up);
	}
	else if (GetAsyncKeyState(VK_DOWN)) {
		snake.turn(Down);
	}
}

};

class Drawer {
Game & game;
public:
Drawer(Game & g) : game(g) {}

void draw() {
	for (int row = 0; row < game.board.y; ++row) {
		for (int col = 0; col < game.board.x; ++col) {
			coord current = { col, row };
			if (game.food.pos == current) {
				std::cout << game.food.texture;
			}
			else if (game.snake.inside(current)) {
				std::cout <<
					(game.snake.getHead() == current ?
					game.snake.head_texture :
					game.snake.body_texture);
			}
			else {
				std::cout << ' ';
			}
		}
	}
}

void clear() {
	system("cls");
}

private:

};

int main() {
Game g({ 80, 21 });

Drawer d(g);
while (true) {
	if (!g.step()) {
		cout << "You lost";
	}
	g.read_input();

	d.draw();
	
	Sleep(100);
	d.clear();

}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment