Skip to content

Instantly share code, notes, and snippets.

@dutchLuck
Last active September 11, 2023 09:58
Show Gist options
  • Save dutchLuck/460e8d4f0b9dbb37b0273ecb39aab17e to your computer and use it in GitHub Desktop.
Save dutchLuck/460e8d4f0b9dbb37b0273ecb39aab17e to your computer and use it in GitHub Desktop.
Retro text version of Connect 4 game.
#! /usr/bin/python3
#
# C 4
#
# This code attempts to play the game of Connect 4
# using: text mode input and output.
#
# Last edited Mon Apr 3 20:58:53 2023
#
# c4 doesn't incorporate much game awareness in
# its play, so it should be relatively easy to
# beat.
#
# 0v2 Look ahead 1 token for a win or to stop human player potential win
# 0v1 Added code for repeated games
# 0v0 original version
#
# Roadmap for additional capability
# 0v3 Add more aggressive strategy to help computer win
#
"""
Board of 6 rows of 7 columns is mapped to a cell number
as shown below; -
|   5  |  11  |  17  |  23  |  29  |  35  |  41  |
|   4  |  10  |  16  |  22  |  28  |  34  |  40  |
|   3  |   9  |  15  |  21  |  27  |  33  |  39  |
|   2  |   8  |  14  |  20  |  26  |  32  |  38  |
|   1  |   7  |  13  |  19  |  25  |  31  |  37  |
|   0  |   6  |  12  |  18  |  24  |  30  |  36  |
====1======2======3======4======5======6======7===
"""
import random # computer only plays random at this stage
import time # provides a time delay through sleep()
board = 42 * [0] # The playing board is a list
def clearGameBoard( board ):
for cnt in range( 42 ):
board[ cnt ] = 0
def columnIsntFull( board, column ):
return board[( 6 * column ) - 1 ] == 0 # As long as the top cell is empty the column can be considered available
def printTokenInSpace( player ):
if player == 1:
print( ' C ', end = '' ) # Indicate a computer token in the cell
elif player == 101:
print( 'wCn', end = '' ) # Indicate a winning computer token in the cell
elif player == 2:
print( ' P ', end = '' ) # Indicate a human player token in the cell
elif player == 102:
print( 'wPn', end = '' ) # Indicate a winning human player token in the cell
else:
print( ' ? ', end = '' ) # Indicate something has gone wrong
def printBoard( board ):
print() # a blank line before the board
for row in range( 5, -1, -1): # move from upper to lower rows
for col in range( row, row + 41, 6): # move across each row
print( '|', end = '' )
if col >= 36: # test for right hand end of board
if board[ col ] == 0: # test for unpopulated cell
print(' |')
else:
printTokenInSpace( board[ col ] )
print( '|' )
else:
if board[ col ] == 0: # test for unpopulated cell
print(' ', end = '' )
else:
printTokenInSpace( board[ col ] )
print( '==1===2===3===4===5===6===7==' )
def testForVerticalWin( index, player ):
fourInA_Row = (board[ index] == player) and (board[ index + 1] == player) and (board[ index + 2] == player) and (board[ index + 3] == player)
return fourInA_Row
def testForHorizontalWin( index, player ):
fourInA_Row = (board[ index] == player) and (board[ index + 6] == player) and (board[ index + 12] == player) and (board[ index + 18] == player)
return fourInA_Row
def testForDiagRightWin( index, player ):
fourInA_Row = (board[ index] == player) and (board[ index + 7] == player) and (board[ index + 14] == player) and (board[ index + 21] == player)
return fourInA_Row
def testForDiagLeftWin( index, player ):
fourInA_Row = (board[ index] == player) and (board[ index - 5] == player) and (board[ index - 10] == player) and (board[ index - 15] == player)
return fourInA_Row
def testAllForVerticalWin( player ):
vertWin = False
for col in range( 0, 42, 6 ):
for index in range( col, col + 3, 1 ):
vertWin = vertWin or testForVerticalWin( index, player )
return vertWin
def testAllForHorizontalWin( player ):
horizWin = False
for row in range( 0, 6, 1 ):
for index in range( row, row + 24, 6 ):
horizWin = horizWin or testForHorizontalWin( index, player )
return horizWin
def testAllForDiagRightWin( player ):
diagWin = False
for row in range( 0, 3, 1):
for index in range( row, row + 24, 6 ):
diagWin = diagWin or testForDiagRightWin( index, player )
return diagWin
def testAllForDiagLeftWin( player ):
diagWin = False
for row in range( 0, 3, 1 ):
for index in range( row + 18, row + 42, 6 ):
diagWin = diagWin or testForDiagLeftWin( index, player )
return diagWin
def testAllForPlayerWin( player ):
return testAllForVerticalWin( player ) or testAllForHorizontalWin( player ) or testAllForDiagRightWin( player ) or testAllForDiagLeftWin( player )
def testAllForComputerWin():
return testAllForPlayerWin( 1 )
def testAllForHumanPlayerWin():
return testAllForPlayerWin( 2 )
def findIndexOfBottomMostEmptyLevelInColumn( column, board ):
if board[( 6 * column ) - 1 ] != 0:
print( '?? Column {} is already full'.format( column ))
return -1
else:
lowestIndex = 6 * ( column - 1 )
for index in range( 6 * ( column - 1 ), 6 * column, 1 ):
if board[ index ] != 0:
lowestIndex = index + 1
return lowestIndex
def columnToCompleteA_PossibleWin( player, board ):
col = 0
result = False
index = -1
while col < 7 and not result:
col = col + 1
if columnIsntFull( board, col ):
index = findIndexOfBottomMostEmptyLevelInColumn( col, board )
board[ index ] = player
result = testAllForPlayerWin( player )
board[ index ] = 0 # restore board
if not result: # ensure index gets reset if no Win found
index = -1
if index == -1:
col = -1
return( col )
def columnToCompleteA_PossibleComputerWin( board ):
return( columnToCompleteA_PossibleWin( 1, board ))
def columnToBlockA_PossibleHumanPlayerWin( board ):
return( columnToCompleteA_PossibleWin( 2, board ))
def computerDropToken( board, token ):
index = -1 # Ensure the while loop runs at least once
while index == -1:
col = columnToCompleteA_PossibleComputerWin( board )
## print( 'column returned by columnToCompleteA_PossibleComputerWin() is {}'.format( col ))
if col == -1:
col = columnToBlockA_PossibleHumanPlayerWin( board )
## print( 'column returned by columnToBlockA_PossibleHumanPlayerWin() is {}'.format( col ))
if col == -1: # if no column chosen yet then chose a random column
col = random.randint( 1, 7 )
if columnIsntFull( board, col ):
print( '\nComputer has chosen column {}'.format( col ))
index = findIndexOfBottomMostEmptyLevelInColumn( col, board )
## print( 'Index for column {} is {}'.format( col, index ))
if index > -1:
board[ index ] = token
return index
def humanDropToken( board, token ):
yetToGetValidInput = True
while yetToGetValidInput:
print( '\nWhich column (1 to 7) do you want to drop a token into ? (0 to exit) : ', end = '' )
response = input()
if response == '':
print( "?? No column number was specified, Please try again" )
else:
# If human input is 0 then exit
if response == '0':
return -2
elif response in [ '1', '2', '3', '4', '5', '6', '7' ]:
col = int( response )
# Error check the human input and limit it to something sensible
if col > 7 or col < 1:
print( "?? There is no column {}, Please try again".format( col ))
elif columnIsntFull( board, col ):
yetToGetValidInput = False
else:
print( '?? Column {} is already full, Please try again.'.format( col ))
else:
print( '?? {} is not a number between 1 and 7, Please try again.'.format( response ))
# Drop the token into the lowest available cell
index = findIndexOfBottomMostEmptyLevelInColumn( col, board )
## print( 'Bottom Index for column {} is {}'.format( col, index ))
# If the column is already full exit the game
if index > -1:
board[ index ] = token
return index
def emptyCellCount( build ):
count = 0
for index in range( 0, 42, 1 ):
if board[ index ] == 0:
count = count + 1
return count
def printInstructions():
print()
print( 'The aim of this game is to get four tokens in a row,' )
print( 'either vertically, horizontally or on a diagonal.' )
print( 'This is acheived by dropping each one in 1 of 7 columns' )
print( 'when it is your turn to play on the following board.' )
printBoard( board )
print( 'While you are working on arranging for four tokens in' )
print( 'a row the computer will also take turns to drop its' )
print( 'tokens into the columns, also aiming for four in a row.' )
print( 'The winner is the first player to get four tokens in a row.' )
print()
def doComputerTurn( board ):
index = computerDropToken( board, 1 )
printBoard( board )
if testAllForComputerWin():
print( '\nCommiserations the computer has four in a row, so it Wins' )
index = -1 # Flag Game finished
return index
def doHumanPlayerTurn( board ):
index = humanDropToken( board, 2 )
if index > -1:
printBoard( board )
if testAllForHumanPlayerWin():
print( '\nCongratulations you have four in a row, so you Win!' )
index = -1 # Flag Game finished
return index
# Start of main code
random.seed(a=None, version=2)
#
print( 'Do you want instructions? [ y or n ]: ', end = '' )
response = input()
if response == 'y' or response == 'Y':
printInstructions()
#
gameNumber = 0 # Keep track of number of games played
computerWins = 0 # Keep track of number of games won by the computer
humanPlayerWins = 0 # Keep track of number of games won by the human player
startNewGame = True
while startNewGame:
clearGameBoard( board ) # Clear board just in case game is played repeatedly
gameNumber = gameNumber + 1
print( '\nStarting Game Number {}'.format( gameNumber ))
print( '\nDo you wish to play first? [ y or n ]: ', end = '' )
response = input()
## print( 'response was "{}"'.format( response ))
for loopCnt in range( 1, 22, 1 ):
print( '\n\n>>> Starting round {} <<<'.format( loopCnt ))
if response == 'y' or response == 'Y':
index = doHumanPlayerTurn( board ) # Get the column, drop the token and check for a win (returns -1)
if index > -1: # If index is -1 then Human Player has already won
index = doComputerTurn( board ) # Determine the column, drop the token and check for a win (returns -1)
if index == -1:
computerWins += 1
elif index == -1:
humanPlayerWins += 1
else:
index = doComputerTurn(board ) # If index is set to -1 then computer has won
if index > -1:
index = doHumanPlayerTurn( board )
if index == -1:
humanPlayerWins += 1
elif index == -1:
computerWins += 1
# Exit if end-of-game flag is set
if index < 0: # exit if a win or the human player has entered 0
print( 'Exiting game {}.'.format( gameNumber ))
break
print( '\n\n>>> Game {} board after round {} is; -'.format( gameNumber, loopCnt ))
printBoard( board )
time.sleep( 1 )
if emptyCellCount( board ) <= 0:
print( 'No further play is possible as all columns are full - Exiting drawn game.' )
break
print( '\nDo you wish to play again? [ y or n ]: ', end = '' )
rspns = input()
startNewGame = rspns == 'y' or rspns == 'Y'
print( 'Bye, you played {} game(s) and won {} time(s) - thanks for playing.'.format( gameNumber, humanPlayerWins ))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment