I wrote a connect four game for two players, where each player uses the same keyboard. I programmed this solely in hopes of improving my skills.
I'm open to and looking for any sort of feedback. Do the comments contain sufficient detail to understand the code? Are the functions separated into their individual mechanisms/purposes properly? Is it, overall, a decently written algorithm?
I'm especially skeptical of the check_winner function. I feel like it could be optimized, just not sure how.
import math
def board_setup():
"""Setup game board dimension size; returns game board as list"""
board_size = -1
while board_size < 4 or board_size > 9:
try:
board_size = int(input("Enter board size dimension (minimum=4, maximum=9): "))
except ValueError:
print("Error: Enter an integer")
return [" "]*board_size**2
def player_setup():
"""Setup two players and their corresponding symbol/piece to represent them on the game board; returns list of player symbols/pieces"""
players = ["too many"]*2
for i in range(2):
while len(players[i]) > 1:
players[i] = input("Player " + str(i+1) + ": Enter your symbol/piece (1 character in length): ")
if players[0] == players[1] or players[i].isspace() or players[i] == "":
print("Error: Duplicate or unsupported player symbol provided")
players[i] = "too many"
return players
def line_break(lines):
"""Prints specified number of blank lines
lines: number of lines"""
print("\n"*lines)
def print_board_pieces(board):
"""Prints board's vertical and horizontal barrier and player symbols/pieces set in board
board: board array"""
board_width = int(math.sqrt(len(board)))
barrier_symbol = "▋"
#print player pieces or blank space and vertical barriers
for s in range(0, len(board), board_width):
print(barrier_symbol, *board[s:s+board_width], barrier_symbol, sep="|")
if s < len(board)-board_width:
print(barrier_symbol, "+", "-+"*board_width, barrier_symbol, sep="")
#horizontal floor of board
print(barrier_symbol, barrier_symbol*board_width*2, barrier_symbol*2, sep="")
def print_board(board, numbered):
"""Print game board to screen
board: board array
numbered: number top of columns?; bool"""
line_break(3) #padding
#number rows
if numbered:
print(" ", end="")
for i in range(int(math.sqrt(len(board)))):
print(" ", str(i+1), sep="", end="")
print(" ")
else:
print()
#barrier and player pieces
print_board_pieces(board)
line_break(3) #padding
def player_move(board, player):
"""Prompt and apply player input to board; returns index of player's movement in the board array as int
board: board array to modify
player: player symbol/piece to apply"""
board_width = int(math.sqrt(len(board)))
board_size = len(board)
print("It is " + player + "'s turn!")
move = -1
succeeded = False
while not succeeded:
while move < 0 or move >= board_width:
try:
move = int(input("Where would you like to drop your symbol/piece (enter the integer representing the column)?: "))-1
except ValueError:
print("Error: Enter an integer")
move = board_size - (board_width - move) #move symbol/piece to bottommost row of board
#move player piece up vertically on board until spot is free
while 0 <= move < board_size and board[move] != " ":
move -= board_width
#if piece is in free and valid spot on board, end root loop
if 0 <= move < board_size:
succeeded = True
else:
print("Error: No space left in column")
board[move] = player #apply player piece/symbol to board
return move
def switch_player_turn(player):
"""Switches current player turn; returns other player index in array as int
player: player index in players list as int"""
return 0 if player == 1 else 1
def check_winner(board, player, index):
"""Check if player has won vertically, diagonally, horizontally, if it's a tie, or if the game should continue
board: board array
player: player symbol/piece as string
index: newly placecd player piece's/symbol's index in board array to check from"""
board_width = board_height = int(math.sqrt(len(board)))
board_size = len(board)
#horizontal check
row_start = board_width * math.floor(moved/board_width)
if board[row_start:row_start+board_width].count(player) >= 4:
connected = 1
l, r = moved-1, moved+1
while connected < 4 and r < row_start+board_width and board[r] == player:
connected += 1
r += 1
while connected < 4 and l >= row_start and board[l] == player:
connected += 1
l -= 1
if connected == 4:
return player
#vertical check
column_start = moved
while column_start >= board_height:
column_start -= board_height
if board[column_start:board_size:board_height].count(player) >= 4:
connected = 1
l, r = moved-board_height, moved+board_height
while connected < 4 and r < board_size and board[r] == player:
connected += 1
r += board_height
while connected < 4 and l >= 0 and board[l] == player:
connected += 1
l -= board_height
if connected == 4:
return player
#diagonal check
_dir = -1
while _dir <= 1:
diag_start = diag_end = moved
while diag_start % board_height != 0 if _dir == 1 else (diag_start+1) % board_height != 0 and diag_start >= board_width:
diag_start -= board_height+_dir #move diag_start as far top as possible until wall collision
while diag_end % board_height != 0 if _dir == -1 else (diag_end+1) % board_height != 0 and diag_end < (board_size-board_width):
diag_end += board_height+_dir #move diag_end as far bottom as possible until wall collision
if board[diag_start:diag_end+1:board_height+_dir].count(player) >= 4:
connected = 1
l, r = moved-(board_height+_dir), moved+(board_height+_dir)
while connected < 4 and r <= diag_end and board[r] == player:
connected += 1
r += board_height+_dir
while connected < 4 and l >= diag_start and board[l] == player:
connected += 1
l -= board_height+_dir
if connected == 4:
return player
_dir += 2
#draw/tie check
if " " not in board:
return "No one"
else:
return None #no winner or draw yet
#board setup
board_arr = board_setup()
#player setup
players = player_setup()
current_player = 0
#main loop
winner = None
while winner == None:
print_board(board_arr, numbered=True)
moved = player_move(board_arr, players[current_player])
winner = check_winner(board_arr, players[current_player], moved)
if winner:
break
else:
current_player = switch_player_turn(current_player)
#winner
print("\n"*100)
print_board(board_arr, numbered=False)
if winner in players:
print(winner, "won!")
else:
print("The board is full, it's a draw!")