CONNECT FOUR. Leela Ghaemmaghami and Clara Duffy. May 2015.
import copy
import Queue
class Board:
def __init__(self, rows, cols):
"""
Initializes board state for a given number of rows and columns
(forms a matrix of dimension rows x columns, filled with 0s)
rows: desired number of rows for this board
cols: desired number of cols for this board
"""
self.rows = rows
self.cols = cols
self.state = [[' ' for i in xrange(cols)] for i in xrange(rows)] # addressed as: [row][col]
def display(self):
"""
Displays the current internal board state in the console
"""
for i in range(self.rows): # number of rows
for j in range(self.cols): # number of columns
print self.state[i][j], "|", #prints blanks and lines
print ""
for j in range(self.cols):
print (j+1), "|",
print ""
def makeMove(self, column, piece):
"""
Finds first unoccupied space in a column and place player's piece there
Returns True if successful, False if error occurred
column: column in which to insert piece
piece: specific value to insert into specified column
"""
jeremy = 0
if self.state[self.rows-1][column] == ' ':
# if the last spot of the column is empty
self.state[self.rows-1][column] = piece
# puts the piece in the last spot
jeremy = 1
return True
for j in range(self.rows):
spot = self.state[j][column]
if spot != ' ':
# put the piece in the spot above the other peice
self.state[j-1][column] = piece
jeremy = 1
return True
if jeremy != 1:
return False # if no spots
def getPossibleMoves(self):
"""
Returns a list of integers (0-6) representing which columns
still have open slots (can have pieces put in them)
"""
possible = [] #list of possible columns
for j in range(self.cols):
# j is the number of the column
if not j in possible:
i = self.state[0][j]
if i == ' ':
possible.append(j)
# adds the column number to the list
if len(possible) != 0:
return possible # prints list of columns w spaces
elif len(possible) == 0:
return False
def isGameOver(self):
"""
Checks if game is over (if any player has won)
Returns which player has won if applicable
Returns 0 if no player has won yet
"""
# VERTICAL
for j in range(self.cols):
for i in range(self.rows-3):
if (self.state[i][j] == self.state[i+1][j]) and (self.state[i][j] == self.state[i+2][j]) and (self.state[i][j] == self.state[i+3][j]) and (self.state[i][j] != ' '):
# if there are 4 in a row vertically # troll the respawn jeremy!
return self.state[i][j]
# HORIZONTAL
for j in range(self.cols-3):
for i in range(self.rows):
if (self.state[i][j] == self.state[i][j+1]) and (self.state[i][j] == self.state[i][j+2]) and (self.state[i][j] == self.state[i][j+3]) and (self.state[i][j] != ' '):
# if there are 4 in a row horizontally # troll the respawn jeremy!
return self.state[i][j]
# SOUTHEAST
for j in range(self.cols-3):
for i in range(3, self.rows):
if (self.state[i][j] == self.state[i-1][j+1]) and (self.state[i][j] == self.state[i-2][j+2]) and (self.state[i][j] == self.state[i-3][j+3]) and (self.state[i][j] != ' '):
# if there are 4 in a row diagonally # troll the respawn jeremy!
return self.state[i][j]
# NORTHEAST
for j in range(self.cols-3):
for i in range(3, self.rows):
if (self.state[i][j+3] == self.state[i-1][j+2]) and (self.state[i][j+3] == self.state[i-2][j+1]) and (self.state[i][j+3] == self.state[i-3][j]) and (self.state[i][j+3] != ' '):
# if there are 4 in a row diagonally # troll the respawn jeremy!
return self.state[i][j+3]
if self.getPossibleMoves() == False: # if board is full
return None
return 0
class HumanPlayer:
def __init__(self, number):
self.number = number
def getNextMove(self, board):
"""
Returns the integer (0-6) row index of the player's next move
board: A board state (as defined in your Board class)
"""
while True:
try:
print "Choose your column!"
choice = (int(raw_input())-1)
# player chooses column
break
except ValueError:
print "Thats not a number. *_*"
while choice in range(board.cols): # while valid choice
if choice in board.getPossibleMoves():
return choice
else:
print "You are an idiot. Choose a valid column."
while not choice in board.getPossibleMoves(): # not valid choice
try:
choice = (int(raw_input())-1)
# lets player try again
except ValueError:
print "Your choice is invalid, choose again."
while (choice not in range(board.cols)): # if choice invalid
print "You are a failure. Choose a valid column."
choice = (int(raw_input())-1)
if choice in board.getPossibleMoves():
return choice
# player tries again
#AI Player
class AIPlayer:
def __init__(self, number):
self.number = number
def getNextMove(self, board):
"""
Returns the integer (0-6) row of the player's next move
board: A board state (as defined in your Board class)
"""
scores = []
move = 0
curr_score = 0
for x in board.getPossibleMoves():
boardcopy = copy.deepcopy(board)
boardcopy.makeMove(x, "O")
curr_score = self.minimax(boardcopy, 3, True)
scores.append((curr_score, x))
move = max(scores)[1] #return max
print "AI has chosen:"
print (move + 1)
return (move)
def scorer(self, list):
score = 0
#3 in a row and space at end
if (list[0] == list[1]) and (list[0] == list[2]) and (list[3] == ' ') and (list[0] != ' '):
if list[0] == "X":
score = (score - 830)
elif list[0] == "O":
score = (score + 710)
#3 in a row with space third
elif (list[0] == list[1]) and (' ' == list[2]) and (list[3] == list[0]) and (list[0] != ' '):
if list[0] == "X":
score = (score - 830)
elif list[0] == "O":
score = (score + 710)
#3 in a row with space first
if (list[1] == list[2]) and (list[1] == list[3]) and (" " == list[0]) and (list[1] != ' '):
if list[1] == "X":
score = (score - 830)
elif list[1] == "O":
score = (score + 710)
#incentivizes blocking opponent's 3 in a row
if (list[1] == list[2]) and (list[1] == list[3]) and (list[1] != list[0]) and (list[1] != ' ') and (list[0] != ' '):
if list[1] == "X":
score = (score + 3000)
elif list[1] == "O":
score = (score - 100)
#3 in a row with space second
if (list[0] == list[2]) and (' ' == list[1]) and (list[0] == list[3]) and (list[0] != ' '):
if list[0] == "X":
score = (score - 830)
elif list[0] == "O":
score = (score + 710)
#2 in a row with space after
if (list[0] == list[1]) and (' ' == list[2]) and (list[0] != ' '):
if list[0] == "X":
score = (score - 20)
elif list[0] == "O":
score = (score + 15)
#2 with space in middle
if (list[0] == list[2]) and (' ' == list[1]) and (list[0] != ' '):
if list[0] == "X":
score = (score - 20)
elif list[0] == "O":
score = (score + 15)
#2 with space first
if (list[1] == list[2]) and (' ' == list[0]) and (list[1] != ' '):
if list[1] == "X":
score = (score - 20)
elif list[1] == "O":
score = (score + 15)
#2 with space before and after
if (list[1] == list[2]) and (' ' == list[0]) and (list[3] == " ") and (list[1] != ' '):
if list[1] == "X":
score = (score - 100)
elif list[1] == "O":
score = (score + 50)
#2 with 2 spaces after
if (list[0] == list[1]) and (' ' == list[2]) and (list[3] == " ") and (list[0] != ' '):
if list[0] == "X":
score = (score - 105)
elif list[0] == "O":
score = (score + 55)
# horizontal 2 with 2 spaces before
if (list[2] == list[3]) and (' ' == list[0]) and (list[1] == " ") and (list[2] != ' '):
if list[2] == "X":
score = (score - 105)
elif list[2] == "O":
score = (score + 55)
#1 with spaces
if (' ' == list[0]) and (' ' == list[2]) and (list[1] != ' '):
if list[1] == "X":
score = (score - 15)
elif list[1] == "O":
score = (score + 5)
#1 with spaces
if (' ' == list[1]) and (' ' == list[3]) and (list[2] != ' '):
if list[2] == "X":
score = (score - 15)
elif list[2] == "O":
score = (score + 5)
#4 Os
if list.count("O") == 4:
score = score + 3900
#4 Xs
if list.count("X") == 4:
score = score - 4000
return score
def getBoardScore(self, board):
"""
Returns a score value ranking the utility of board to the AI player
board: A board state (as defined in your Board class)
"""
score = 0
#collect all of the lists
#rate the lists
totallist = [0,0,0,0]
#VERTICAL
for j in range(board.cols):
for i in range(board.rows-3):
totallist = [(board.state[i][j]), (board.state[i+1][j]), (board.state[i+2][j]), (board.state[i+3][j])]
score += self.scorer(totallist)
#4 vertically # troll the respawn jeremy!
# HORIZONTAL
for j in range(board.cols-3):
for i in range(board.rows):
totallist = [(board.state[i][j]), (board.state[i][j+1]), (board.state[i][j+2]), (board.state[i][j+3])]
score += self.scorer(totallist)
#4 horizontally # troll the respawn jeremy!
# SOUTHEAST
for j in range(board.cols-3):
for i in range(3, board.rows):
totallist = [(board.state[i][j]), (board.state[i-1][j+1]), (board.state[i-2][j+2]), (board.state[i-3][j+3])]
score += self.scorer(totallist)
#4 diagonally # troll the respawn jeremy!
# NORTHEAST
for j in range(board.cols-3):
for i in range(3, board.rows):
totallist = [(board.state[i][j+3]), (board.state[i-1][j+2]), (board.state[i-2][j+1]), (board.state[i-3][j])]
score += self.scorer(totallist)
#4 diagonally # troll the respawn jeremy!
return score
def minimax(self, board, depth, minnie):
"""
Returns a utility score value resulting from exploring board to specified depth
board: a board state (as defined in your Board class)
depth: integer representing number of additional times to recur before returning
minnie: indicates current player turn (True = opponent's turn, False otherwise)
"""
if (board.isGameOver()!= 0) or (depth == 0):
# if the game is over
return self.getBoardScore(board)
else:
succs = [] # list of successors
for n in board.getPossibleMoves():
boardcopy = copy.deepcopy(board)
# make a copy of the board
if minnie == True:
succs.append(boardcopy.makeMove(n, "O"))
elif minnie == False:
succs.append(boardcopy.makeMove(n, "X"))
# add options to list
scores = [] #list of scores
for succ in succs: # every successor
scores.append(self.minimax(boardcopy, depth-1, not minnie))
if minnie == False:
return max(scores)
# returns the maximum
elif minnie == True:
return min(scores)
# returns the minimum
def main():
while True:
try:
print "Choose number of rows: "
rows = int(raw_input("-->"))
# player inputs number of rows
print "Choose number of columns: "
cols = int(raw_input("-->"))
# number of columns
print "Press 1 for one player, 2 for two players, or 0 for no players"
players = int(raw_input("-->"))
# number of players
break
except ValueError:
print "Thats not a number. . . "
board = Board(rows, cols)
board.display()
# board is now the inputed values
if players == 1:
p1 = HumanPlayer(1)
p2 = AIPlayer(1)
elif players == 2:
p1 = HumanPlayer(1)
p2 = HumanPlayer(2)
elif players == 0:
p1 = AIPlayer(1)
p2 = AIPlayer(2)
while board.isGameOver() == 0: # while the game is not over
print "Player Xs Turn"
board.makeMove((p1.getNextMove(board)), "X")
# puts the piece in the column
board.display()
board.isGameOver()
# checks to see if game is over
if board.isGameOver() == 0: # if the game is not over
print "Player Os Turn"
board.makeMove((p2.getNextMove(board)), "O")
# puts piece in column
board.display()
board.isGameOver()
# checks if game is over
if (board.isGameOver() != 0) and (board.isGameOver() != None): # if someone wins
print "Yay Player", board.isGameOver(), "Won!!!!"
elif board.isGameOver() == None: # if board is full
print "There is no winner, the board is full. You both stink."
# Initialization code: No need to change
print "Welcome to Connect Four!"
main()
print "lol bye =D"