Skip to content

Commit f154b6c

Browse files
author
Thomas Renn
committed
Initial commit
0 parents  commit f154b6c

File tree

4 files changed

+506
-0
lines changed

4 files changed

+506
-0
lines changed

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Git Ignore files #
2+
3+
# Compiled files #
4+
##########################
5+
*.pyc
6+
7+
# OS files #
8+
##########################
9+
.DS_Store
10+

connect4.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#
2+
# Overall Connect-4 game playing module
3+
# Author: Tom Renn
4+
#
5+
import gameboard as gb
6+
import nodeModule as nm
7+
import re
8+
import sys
9+
import cProfile
10+
11+
# MiniMax algorithm
12+
#
13+
# node: Root node to calculate minimax on
14+
# depth: How far down into the node to search
15+
# returnNode: Optional parameter, True if this call is to return the resulting child node to pick
16+
# otherwise miniMax returns the heuristic value of the node it is given
17+
def miniMax(node, depth, returnNode=False):
18+
isLeaf = False
19+
children = node.generateChildren()
20+
availableMoves = node.getAvailableMoves()
21+
result = 0;
22+
resultNode = node
23+
24+
if len(availableMoves) == 0: # we're at a leaf
25+
result = node.heuristic2()
26+
elif depth < 1: # reached max depth
27+
result = node.heuristic2()
28+
else:
29+
# look at each child for the moves available
30+
for nextMove in availableMoves:
31+
child = nm.Node(node, nextMove)
32+
# value of child node
33+
result = miniMax(child, depth-1)
34+
35+
if node.player == nm.PLAYER_MAX: # player is MAX
36+
if result >= node.alpha:
37+
node.alpha = result
38+
resultNode = child
39+
else: # player is MIN
40+
if result <= node.beta:
41+
node.beta = result
42+
resultNode = child
43+
44+
# determine if we need to stop searching children
45+
if node.alpha >= node.beta:
46+
break
47+
48+
if (returnNode):
49+
# clear alpha-beta values from this traversal
50+
resultNode.resetAlphaBeta()
51+
return resultNode
52+
else:
53+
return result
54+
55+
# Overall method that plays connect4
56+
def playGame():
57+
state = nm.Node()
58+
print 'Welcome to connect4. You are P, the player. C is the computer opponent'
59+
print "Type 'quit' or 'q' to exit the game at any time"
60+
61+
if askPlayerPreference(): # player moves first if desired
62+
move = getPlayerMove(state)
63+
state = state.placeChipAt(move)
64+
else: # always have computer act as MAX player
65+
state.setPlayerType(nm.PLAYER_MAX)
66+
67+
winningPlayer = 'initalization'
68+
# Game continues until someone wins
69+
while True:
70+
depth = 4
71+
state = miniMax(state, depth, True)
72+
state.gameboard.fancyPrint()
73+
74+
if state.isGameOver():
75+
winningPlayer = 'Computer wins!'
76+
break
77+
78+
move = getPlayerMove(state)
79+
if move == -1:
80+
winningPlayer = 'You quit, loser!'
81+
break
82+
83+
state = state.placeChipAt(move)
84+
85+
if state.isGameOver():
86+
# print winning gameboard
87+
state.gameboard.fancyPrint()
88+
winningPlayer = 'You win!'
89+
break
90+
91+
print winningPlayer
92+
93+
# get the players next move for a particular node
94+
# continues until player enters an available move
95+
# or until player types quit, which case returns -1
96+
def getPlayerMove(node):
97+
availableMoves = node.getAvailableMoves()
98+
move = -1
99+
while move == -1 or move not in availableMoves:
100+
move = raw_input("Choose your next move (0-6): ")
101+
if re.match('\d', move): # move is a digit
102+
move = int(move)
103+
elif re.match('quit|q', move):
104+
move = -1
105+
break
106+
return move
107+
108+
# ask the player if they would like to move first
109+
# returns True if player moves first
110+
def askPlayerPreference():
111+
yes = ["yes", "y", "Y"]
112+
no = ["no", "n", "N"]
113+
userInput = raw_input("Would you like to move first? (y/n): ")
114+
if userInput in yes:
115+
return True
116+
elif userInput in no:
117+
return False
118+
else:
119+
print "Sorry that is not an acceptable answer"
120+
return askPlayerPreference()
121+
122+
123+
playGame()
124+
125+

gameboard.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#
2+
# GameBoard module
3+
# -
4+
# Represent a board to play on using a 2d array from NumPy
5+
# NumPy : http://sourceforge.net/projects/numpy/files/NumPy/1.7.1/
6+
#
7+
# Author: Tom Renn
8+
#
9+
import numpy as np
10+
import nodeModule
11+
12+
BOARD_WIDTH = 7
13+
BOARD_HEIGHT = 6
14+
EMPTY_SPOT = 0
15+
16+
class GameBoard:
17+
18+
# Constructor
19+
# If no board is given, create one containing all 0s
20+
def __init__(self, baseBoard=None):
21+
if baseBoard is None:
22+
self.board = np.zeros( (BOARD_HEIGHT, BOARD_WIDTH), dtype=np.int16)
23+
else:
24+
self.board = baseBoard
25+
26+
# place chip in a given column index
27+
# return (row, col) of newly placed chip, or (-1, -1) if unable to be placed
28+
def moveAt(self, index, player):
29+
if index not in range(BOARD_WIDTH):
30+
return (-1, -1)
31+
32+
# go from bottom of the board up
33+
for i in reversed(range(BOARD_HEIGHT)):
34+
row = self.board[i]
35+
if row[index] == EMPTY_SPOT: # found empty row
36+
row[index] = player
37+
return (i, index)
38+
else: # row occupied
39+
continue
40+
41+
# if we got this far, the move is not able to be made
42+
return (-1, -1)
43+
44+
# return the chip at the given row and column
45+
# returns EMPTY_SPOT if board bounds are exceeded
46+
def getChipAt(self, row, col):
47+
if row >= 0 and row < BOARD_HEIGHT:
48+
if col >= 0 and col < BOARD_WIDTH:
49+
return self.board[row][col]
50+
return EMPTY_SPOT
51+
52+
# return indexes of available moves
53+
def getAvailableMoves(self):
54+
movesAvailable = []
55+
for i in range(BOARD_WIDTH):
56+
if self.board[0][i] == EMPTY_SPOT:
57+
movesAvailable.append(i)
58+
return movesAvailable
59+
60+
# prints the gameboard in a nice format
61+
def fancyPrint(self):
62+
for row in range(BOARD_HEIGHT):
63+
rowString = '|'
64+
for col in range(BOARD_WIDTH):
65+
pos = self.board[row][col]
66+
if pos == EMPTY_SPOT:
67+
# if bottom row and empty show _
68+
if row == (BOARD_HEIGHT - 1):
69+
pos = '_'
70+
else:
71+
pos = ' '
72+
elif pos == nodeModule.PLAYER_MIN:
73+
pos = 'P'
74+
else:
75+
pos = 'C'
76+
rowString = rowString + pos + '|'
77+
print rowString
78+
# print index on bottom
79+
rowString = '|'
80+
for row in range(BOARD_WIDTH):
81+
rowString = rowString + str(row) + '|'
82+
print rowString
83+
84+
# number of available moves
85+
def availableMoves(self):
86+
count = 0
87+
for i in range(BOARD_WIDTH):
88+
if self.board[0][i] == EMPTY_SPOT:
89+
count = count + 1
90+
return count

0 commit comments

Comments
 (0)