Last active
August 29, 2015 14:01
-
-
Save Samuel-Retter/7109ebb265bd2044a00a to your computer and use it in GitHub Desktop.
Plays the game of Connect Four against a human opponent.
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
__author__ = 'Samuel' | |
# Python 3.3 | |
# Plays the game of Connect 4 against a human opponent | |
import random | |
# global constants | |
X = 'X' | |
O = 'O' | |
EMPTY = " " | |
TIE = 'TIE' | |
A = [0,1,2,3,4,5] | |
B = [6,7,8,9,10,11] | |
C = [12,13,14,15,16,17] | |
D = [18,19,20,21,22,23] | |
E = [24,25,26,27,28,29] | |
F = [30,31,32,33,34,35] | |
G = [36,37,38,39,40,41] | |
COLUMNS = [A, B, C, D, E, F, G] | |
COLUMNSNAMES = ['A', 'B', 'C', 'D', 'E', 'F', 'G'] | |
WAYS_TO_WIN = [] | |
for i in range(24): | |
WAYS_TO_WIN.append((i,i+6,i+12,i+18)) | |
for i in range(42): | |
if i % 6 <= 2: | |
WAYS_TO_WIN.append((i,i+1,i+2,i+3)) | |
for i in range(42): | |
if i <= 23 and i % 6 <= 2: | |
WAYS_TO_WIN.append((i,i+7,i+14,i+21)) | |
for i in range(42): | |
if i <= 23 and i % 6 > 2: | |
WAYS_TO_WIN.append((i,i+5,i+10,i+15)) | |
def display_instruct(): | |
print( | |
""" | |
Welcome to Connect 4. | |
You will make your move known by entering a letter, A-G. The letter will | |
correspond to a column as follows: | |
A B C D E F G | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
| | | | | | | | | |
+---+---+---+---+---+---+---+ | |
""" | |
) | |
def ask_yes_no(question): | |
"""Ask a yes or no question.""" | |
response = None | |
while response not in ("1", "2"): | |
response = input(question).lower() | |
return response | |
def ask_letter(question): | |
"""Ask for a letter""" | |
response = 'nothingYet' | |
while response.upper() not in COLUMNSNAMES: | |
response = input(question) | |
return response.upper() | |
def pieces(): | |
"""Determine if player or computer goes first.""" | |
go_first = ask_yes_no("Enter 1 to go first, 2 to go second: ") | |
if go_first == "1": | |
print("\nOk. Make a move.") | |
human = X | |
computer = O | |
else: | |
print("\nI'll go first, then.") | |
computer = X | |
human = O | |
return computer, human | |
def new_board(): | |
"""Create new game board.""" | |
board = [] | |
for square in range(42): | |
board.append(EMPTY) | |
return board | |
def display_board(board): | |
"""Display game board on screen.""" | |
print("\n A B C D E F G") | |
print(" +"+"---+"*7) | |
print(" |", board[5], "|", board[11], "|", board[17], "|", board[23], "|", board[29], "|", board[35], "|", board[41], "|") | |
print(" +"+"---+"*7) | |
print(" |", board[4], "|", board[10], "|", board[16], "|", board[22], "|", board[28], "|", board[34], "|", board[40], "|") | |
print(" +"+"---+"*7) | |
print(" |", board[3], "|", board[9], "|", board[15], "|", board[21], "|", board[27], "|", board[33], "|", board[39], "|") | |
print(" +"+"---+"*7) | |
print(" |", board[2], "|", board[8], "|", board[14], "|", board[20], "|", board[26], "|", board[32], "|", board[38], "|") | |
print(" +"+"---+"*7) | |
print(" |", board[1], "|", board[7], "|", board[13], "|", board[19], "|", board[25], "|", board[31], "|", board[37], "|") | |
print(" +"+"---+"*7) | |
print(" |", board[0], "|", board[6], "|", board[12], "|", board[18], "|", board[24], "|", board[30], "|", board[36], "|") | |
print(" +"+"---+"*7) | |
print() | |
def legal_moves(board): | |
"""Create a list of legal moves.""" | |
moves = [] | |
for column in COLUMNS: | |
if column: | |
moves.append(column) | |
return moves | |
def winner(board): | |
"""Determine the game winner.""" | |
for row in WAYS_TO_WIN: | |
if board[row[0]] == board[row[1]] == board[row[2]] == board[row[3]] != EMPTY: | |
winner = board[row[0]] | |
return winner | |
if EMPTY not in board: | |
return TIE | |
return None | |
def human_move(board, human): | |
"""Get human move.""" | |
legal = legal_moves(board) | |
move = 'nothingYet' | |
while move.upper() not in COLUMNSNAMES: | |
move = ask_letter("In which column would you like to drop your piece? (A - G): ") | |
if move.upper() not in COLUMNSNAMES: | |
print("\nThat column is already full. Choose another.\n") | |
return move | |
def moveprinter(moveX): | |
for i in 'DCEBFAG': | |
if moveX == globals()[i]: | |
print(i) | |
return | |
def test(move, human, board): | |
board = board[:] | |
if len(move) == 1: | |
return 'safe' | |
board[move[1]] = human | |
if winner(board) == human: | |
return 'unsafe' | |
return 'safe' | |
board[move[1]] = EMPTY | |
def test2(move, computer, board): | |
board = board[:] | |
if len(move) == 1: | |
return 'safe' | |
board[move[1]] = computer | |
if winner(board) == computer: | |
return 'unsafe' | |
return 'safe' | |
board[move[1]] = EMPTY | |
def computer_move(board, computer, human): | |
print("\nI will drop my piece in column", end=" ") | |
# win if possible | |
board = board[:] | |
for move in legal_moves(board): | |
board[move[0]] = computer | |
if winner(board) == computer: | |
moveprinter(move) | |
return move | |
board[move[0]] = EMPTY | |
# block human's win | |
for move in legal_moves(board): | |
board[move[0]] = human | |
if winner(board) == human: | |
moveprinter(move) | |
return move | |
board[move[0]] = EMPTY | |
# create double trap | |
for move in legal_moves(board): | |
board[move[0]] = computer | |
rows_with_at_least_three_computers = [] | |
for row in WAYS_TO_WIN: | |
computersinrow = [] | |
for square in row: | |
if board[square] == computer: | |
computersinrow.append(square) | |
availablesquares = [] | |
for column in COLUMNS: | |
if column: | |
availablesquares.append(column[0]) | |
if row[0] in availablesquares or row[1] in availablesquares or row[2] in availablesquares or row[3] in availablesquares: | |
bool1 = True | |
else: | |
bool1 = False | |
if len(computersinrow) == 3 and EMPTY in [board[row[0]], board[row[1]], board[row[2]], board[row[3]]] and bool1: | |
rows_with_at_least_three_computers.append(row) | |
if len(rows_with_at_least_three_computers) == 2 and test(move, human, board) == 'safe': | |
moveprinter(move) | |
return move | |
# undo move | |
board[move[0]] = EMPTY | |
# if human can create a double trap, block it | |
for move in legal_moves(board): | |
board[move[0]] = human | |
rows_with_at_least_three_humans = [] | |
for row in WAYS_TO_WIN: | |
humansinrow = [] | |
for square in row: | |
if board[square] == human: | |
humansinrow.append(square) | |
availablesquares = [] | |
for column in COLUMNS: | |
if column: | |
availablesquares.append(column[0]) | |
if row[0] in availablesquares or row[1] in availablesquares or row[2] in availablesquares or row[3] in availablesquares: | |
bool2 = True | |
else: | |
bool2 = False | |
if len(humansinrow) == 3 and EMPTY in [board[row[0]], board[row[1]], board[row[2]], board[row[3]]] and bool2: | |
rows_with_at_least_three_humans.append(row) | |
if len(rows_with_at_least_three_humans) == 2 and test(move, human, board) == 'safe' and test2(move, computer, board) == 'safe': | |
moveprinter(move) | |
return move | |
# undo move | |
board[move[0]] = EMPTY | |
# horizontal trios | |
midtrios = [] | |
for i in range(6,24): | |
midtrios.append((i,i+6,i+12)) | |
if i % 6 <= 2: | |
midtrios.append((i,i+7,i+14)) | |
elif i % 6 > 2: | |
midtrios.append((i,i+5,i+10)) | |
begtrios = [] | |
for i in range(0,6): | |
begtrios.append((i,i+6,i+12)) | |
if i % 6 <= 2: | |
begtrios.append((i,i+7,i+14)) | |
elif i % 6 > 2: | |
begtrios.append((i,i+5,i+10)) | |
endtrios = [] | |
for i in range(24,30): | |
endtrios.append((i,i+6,i+12)) | |
if i % 6 <= 2: | |
endtrios.append((i,i+7,i+14)) | |
elif i % 6 > 2: | |
endtrios.append((i,i+5,i+10)) | |
best_moves = [D,C,E,B,F,A,G] | |
r1 = random.randint(0,1) | |
r2 = random.randint(0,1) | |
r3 = random.randint(0,1) | |
if r1: | |
best_moves[1],best_moves[2] = best_moves[2],best_moves[1] | |
if r2: | |
best_moves[3],best_moves[4] = best_moves[4],best_moves[3] | |
if r3: | |
best_moves[5],best_moves[6] = best_moves[6],best_moves[5] | |
# if human can complete a trio next turn, block it | |
for move in best_moves: | |
if move and test(move, human, board) == 'safe' and test2(move, computer, board) == 'safe': | |
board[move[0]] = human | |
for trio in midtrios: | |
if board[trio[0]-(trio[1]-trio[0])] == EMPTY or board[trio[2]+(trio[2]-trio[1])] == EMPTY: | |
bool3 = True | |
else: | |
bool3 = False | |
if board[trio[0]] == board[trio[1]] == board[trio[2]] == human and bool3: | |
moveprinter(move) | |
return move | |
board[move[0]] = human | |
for trio in begtrios: | |
if board[trio[2]+(trio[2]-trio[1])] == EMPTY: | |
bool4 = True | |
else: | |
bool4 = False | |
if trio[0]==trio[1]==trio[2]==human and bool4: | |
moveprinter(move) | |
return move | |
board[move[0]] = human | |
for trio in endtrios: | |
if board[trio[0]-(trio[1]-trio[0])] == EMPTY: | |
bool5 = True | |
else: | |
bool5 = False | |
if trio[0]==trio[1]==trio[2]==human and bool5: | |
moveprinter(move) | |
return move | |
board[move[0]] = EMPTY | |
# if computer can complete a trio next turn, go there | |
for move in best_moves: | |
if move and test(move, human, board) == 'safe' and test2(move, computer, board) == 'safe': | |
board[move[0]] = computer | |
for trio in midtrios: | |
if board[trio[0]-(trio[1]-trio[0])] == EMPTY or board[trio[2]+(trio[2]-trio[1])] == EMPTY: | |
bool3 = True | |
else: | |
bool3 = False | |
if board[trio[0]] == board[trio[1]] == board[trio[2]] == computer and bool3: | |
moveprinter(move) | |
return move | |
board[move[0]] = computer | |
for trio in begtrios: | |
if board[trio[2]+(trio[2]-trio[1])] == EMPTY: | |
bool4 = True | |
else: | |
bool4 = False | |
if trio[0]==trio[1]==trio[2]==computer and bool4: | |
moveprinter(move) | |
return move | |
board[move[0]] = computer | |
for trio in endtrios: | |
if board[trio[0]-(trio[1]-trio[0])] == EMPTY: | |
bool5 = True | |
else: | |
bool5 = False | |
if trio[0]==trio[1]==trio[2]==computer and bool5: | |
moveprinter(move) | |
return move | |
board[move[0]] = EMPTY | |
for move in best_moves: | |
if move: | |
if move in legal_moves(board) and test(move, human, board) == 'safe' and test2(move, computer, board) == 'safe': | |
moveprinter(move) | |
return move | |
for move in best_moves: | |
if move: | |
if move in legal_moves(board) and test(move, human, board) == 'safe': | |
moveprinter(move) | |
return move | |
for move in best_moves: | |
if move in legal_moves(board): | |
moveprinter(move) | |
return move | |
def next_turn(turn): | |
"""Switch turns.""" | |
if turn == X: | |
return O | |
else: | |
return X | |
def congrat_winner(the_winner, computer, human): | |
"""Congratulate the winner.""" | |
if the_winner == computer: | |
print("I win!") | |
elif the_winner == human: | |
print("You win!") | |
elif the_winner == TIE: | |
print("It's a tie!") | |
def main(): | |
print('\n\n\n\n') | |
display_instruct() | |
computer, human = pieces() | |
turn = X | |
board = new_board() | |
display_board(board) | |
while not winner(board): | |
if turn == human: | |
done0 = False | |
while not done0: | |
move = human_move(board, human) | |
moverow = globals()[move] | |
try: | |
board[moverow[0]] = human | |
moverow.remove(moverow[0]) | |
done0 = True | |
print("\nFine...") | |
except IndexError: | |
print("\nThat column is already full. Choose another.\n") | |
else: | |
move = computer_move(board, computer, human) | |
board[move[0]] = computer | |
move.remove(move[0]) | |
display_board(board) | |
turn = next_turn(turn) | |
the_winner = winner(board) | |
congrat_winner(the_winner, computer, human) | |
# for human_move, "move" is a str and "moverow" is the actual list | |
# but for computer_move, "move" itself is the list (no need for moverow) | |
# now start the program | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment