Skip to content

tests, small comments and modifications, and start of new strategy #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

.idea/vcs.xml
.idea/workspace.xml
191 changes: 176 additions & 15 deletions C4Game.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,49 @@
from termcolor import colored

class C4Game:

# List of shapes (characters) and colors that players will be assigned
colorMap = [
colored('O', 'blue'), # Gray
colored('O', 'red'), # Red
colored('O', 'blue'),
colored('O', 'red'),
]

def __init__(self, players=1, boardWidth=7, boardHeight=8, winLength=4, turn=0):
self.players = players
def __init__(self, players=1, boardWidth=7, boardHeight=8, winLength=4, turn=0, *names):
self.players = players # Number of players
if players > len(self.colorMap):
raise Exception("Too many players, not enough colors")
self.playerArray = [Player(i) for i in range(players)]
if len(names) != players:
raise Exception(f"{len(names)} player names provided, but {players} players")
self.playerArray = [Player(i, names[i]) for i in range(players)]
self.boardWidth = boardWidth
self.boardHeight = boardHeight
self.winLength = winLength
if winLength > max(boardWidth, boardHeight):
print(f"winLength {winLength} is too large for {boardWidth}x{boardHeight} board. No one will be able to win.", sys.stderr)
self.board = self.createBoard()
self.turn = turn
self.turn = turn # Whose turn it is, as an integer; takes values 0,...,players - 1

#
# def __repr__(self):
# return f"C4Node({self.player}, {self.state}, {repr(nodearray)})"
#
# def __str__(self):
# return f"Player: {self.player}, State: {self.state}"

def __repr__(self):
return f"C4Game(players = {self.players}, baordWidth = {self.boardWidth}, boardHeight = {self.boardHeight}, winLength = {self.winLength}, turn = {self.turn}, names = {self.names}"

def __str__(self):
return repr(self)

def createBoard(self):
# List of lists of blank spots ('-') that will be filled in with players' pieces
return [['-' for i in range(self.boardWidth)] for j in range(self.boardHeight)]

def printBoard(self):
# Prints the board in its current state
for row in range(self.boardHeight):
for col in range(self.boardWidth):
print(self.board[self.boardHeight - 1 - row][col], end=" ")
print(" ")
print("")

def play(self, column: int): # Columns are 1 through boardWidth
if(column > self.boardWidth or column == 0):
def play(self, column: int):
"""column is the column that was chosen for dropping a piece into. Can take values 1,...,boardWidth. Board will be modified and the turn will be changed."""
if (column > self.boardWidth or column == 0):
print(f"Column must be between 1 and {self.boardWidth}", sys.stderr)
return
if self.board[self.boardHeight-1][column-1] != '-':
Expand All @@ -47,6 +56,158 @@ def play(self, column: int): # Columns are 1 through boardWidth
break
self.turn = (self.turn + 1) % self.players

def allPossibleWins(self):
"""A set of all possible ways of winning, given the board dimensions. Each way of winning is itself represented as a frozenset (of size winLength) of tuples, where each tuple is the coordinates of one of the board spots included in the winning line.

For example, if we have a board of size 3x3, and winLength = 2, then the possible ways of winning are:

_______________________

horizontal lines:

* * *
* * *
O O *

* * *
* * *
* O O

* * *
O O *
* * *

* * *
* O O
* * *

O O *
* * *
* * *

* O O
* * *
* * *

which will be represented by the frozensets

{(0,0), (1,0)}
{(1,0), (2,0)}
{(0,1), (1,1)}
{(1,1), (2,1)}
{(0,2), (1,2)}
{(1,2), (2,2)}

_______________________

vertical lines:

* * *
O * *
O * *

O * *
O * *
* * *

* * *
* O *
* O *

* O *
* O *
* * *

* * *
* * O
* * O

* * O
* * O
* * *

which will be represented by the frozensets

{(0,0), (0,1)}
{(0,1), (0,2)}
{(1,0), (1,1)}
{(1,1), (1,2)}
{(2,0), (2,1)}
{(2,1), (2,2)}

_______________________

diagonal up lines:

* * *
* O *
O * *

* * *
* * O
* O *

* O *
O * *
* * *

* * O
* O *
* * *

which will be represented by the frozensets

{(0,0), (1,1)}
{(1,0), (2,1)}
{(0,1), (1,2)}
{(1,1), (2,2)}

_______________________

diagonal down lines:

* * *
O * *
* O *

* * *
* O *
* * O

O * *
* O *
* * *

* O *
* * O
* * *

which will be represented by the frozensets

{(0,1), (1,0)}
{(1,1), (2,0)}
{(0,2), (1,1)}
{(1,2), (2,1)}

_______________________

"""
horizontalWins = {}
# for each horizontalWinTuple: (must figure these out based on board size)
# horizontalWins.add(horizontalWinTuple)
verticalWins = {}
# for each verticalWinTuple: (must figure these out based on board size)
# verticalWins.add(verticalWinTuple)
diagonalUpWins = {}
# for each diagonalUpWinTuple: (must figure these out based on board size)
# diagonalUpWins.add(diagonalUpWinTuple)
diagonalDownWins = {}
# for each diagonalDownWinTuple: (must figure these out based on board size)
# diagonalDownWins.add(diagonalDownWinTuple)
allWins = {}
allWins = allWins.union(horizontalWins, verticalWins, diagonalUpWins, diagonalDownWins)
return allWins


class Player:

Expand Down
18 changes: 10 additions & 8 deletions C4Node.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
numberofslots = 7
numberOfSlots = 7

class C4Node:
"""A C4Node is a node in the tree of all possible game states. It keeps track of the state (one of the player has won, or no one has won yet), and which player's turn yielded that node. It also keeps track of all the nodes below it.
"""

def __init__(self, player: str, n = numberofslots, state="", nodearray = []):
def __init__(self, player: str, n = numberOfSlots, state="", nodeArray = []):
self.player = player
self.state = state
self.nodearray = nodearray
if self.nodearray == []:
self.nodearray = [None for i in range(n)]
self.nodeArray = nodeArray
if self.nodeArray == []:
self.nodeArray = [None for i in range(n)]

def __repr__(self):
return f"C4Node({self.player}, {self.state}, {repr(nodearray)})"
return f"C4Node({self.player}, {self.state}, {repr(nodeArray)})"

def __str__(self):
return f"Player: {self.player}, State: {self.state}"
Expand All @@ -22,9 +24,9 @@ def __str__(self):
print('test')
root = C4Node('red')
root.state = 'red wins'
root.nodearray[2] = C4Node('black')
root.nodeArray[2] = C4Node('black')

print(root.nodearray[2])
print(root.nodeArray[2])



Expand Down
106 changes: 106 additions & 0 deletions test_C4Game.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
"""Unit tests for C4Game.py"""

import unittest
from C4Game import *

class TestC4Game(unittest.TestCase):

def test_init(self):
"""Tests for C4Game.__init__ (therefore includes some testing of C4Game.createBoard and Player class"""
game = C4Game(2, 3, 2, 1, 0, "Maya", "Joey")
# Tests basic attributes
self.assertEqual(game.players, 2)
self.assertEqual(game.boardWidth, 3)
self.assertEqual(game.boardHeight, 2)
self.assertEqual(game.winLength, 1)
self.assertEqual(game.turn, 0)
self.assertEqual(len(game.playerArray), game.players)
# Tests that playerArray comes out as expected
player0Exp = Player(0, "Maya")
Copy link
Collaborator

@joenelsong joenelsong Jun 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Exp on the end of this variable name seems confusing, also would be better to Hard code the test values instead of running them through a Player constructor, just incase the error is int he Player constructor

Copy link
Collaborator

@joenelsong joenelsong Jun 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah I see, but the Color comes from the Game, so maybe something like this makes more sense.
testP0Name = "maya"
testP1Name = "joey"
player0Exp = Player(0, testP1Name)
...

and then reuse the testPXNames at the top too.

But not a big deal :P

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain to me why naming the strings like that is better? :)
Exp stood for expected in my mind. What did it evoke for you?

player1Exp = Player(1, "Joey")
self.assertEqual(game.playerArray[0].name, player0Exp.name)
self.assertEqual(game.playerArray[0].color, player0Exp.color)
self.assertEqual(game.playerArray[1].name, player1Exp.name)
self.assertEqual(game.playerArray[1].color, player1Exp.color)
# Tests that board comes out as expected
boardExp = [['-','-','-'],['-','-','-']]
self.assertEqual(game.board, boardExp)

def test_play_turns(self):
"""Tests that C4Game.play advances turns properly"""
game = C4Game(2, 3, 4, 2, 0, "Joey", "Maya")
self.assertEqual(game.turn, 0)
game.play(1)
self.assertEqual(game.turn, 1)
game.play(2)
self.assertEqual(game.turn, 0)
game.play(1)
self.assertEqual(game.turn, 1)
game.play(3)
self.assertEqual(game.turn, 0)

def test_play(self):
"""Tests that C4Game.play changes the board properly"""
game = C4Game(2, 3, 4, 2, 0, "Maya", "Joey")
b = colored('O', 'blue')
r = colored('O', 'red')
turn0Exp = [
['-','-','-'],
['-','-','-'],
['-','-','-'],
['-','-','-']
]
self.assertEqual(game.board, turn0Exp)
game.play(1)
turn1Exp = [
[b, '-', '-'],
['-', '-', '-'],
['-', '-', '-'],
['-', '-', '-']
]
self.assertEqual(game.board, turn1Exp)
game.play(1)
turn2Exp = [
[ b,'-','-'],
[ r,'-','-'],
['-','-','-'],
['-','-','-']
]
self.assertEqual(game.board, turn2Exp)
game.play(2)
turn3Exp = [
[b, b, '-'],
[r, '-', '-'],
['-', '-', '-'],
['-', '-', '-']
]
self.assertEqual(game.board, turn3Exp)
game.play(3)
turn4Exp = [
[ b, b, r],
[ r,'-','-'],
['-','-','-'],
['-','-','-']
]
self.assertEqual(game.board, turn4Exp)
game.play(2)
turn5Exp = [
[ b, b, r],
[ r, b,'-'],
['-','-','-'],
['-','-','-']
]
self.assertEqual(game.board, turn5Exp)
game.play(1)
turn6Exp = [
[b, b, r],
[r, b, '-'],
[r, '-', '-'],
['-', '-', '-']
]
self.assertEqual(game.board, turn6Exp)


if __name__ == "__main__":
unittest.main()