Python Forum
Please help me with my connect 4 game im beyond despair and I have it due in 4 days
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Please help me with my connect 4 game im beyond despair and I have it due in 4 days
#1
Exclamation 
THIS IS NOT PART OF THE CODE ONLY SOME GUIDING TOWARDS WHERE MY PROBLEMS ARE
The function that checks for diagonal lines wont detect them.
------------------------------------------------------------------------------------------------------------------------
code starts here
BLANK = "*"  # sets the character for the BLANK positions
##################################################################
# DECLARE the variables used in the program as required by CIE ###
##################################################################
# Board(0:6, 0:7) : str (but ignore row 0 and column 0)
# ThisPlayer : str[1]
# GameFinished, WinnerFound : bool
# ColumnNumber : int
# ValidColumn, ValidRow : int


def InitialiseBoard():
    global Board
    Board = [[BLANK] * 7 for x in range(7)]  # Creates the array of blank values


def SetUpGame():  # Set up the parameters for the beginning of the game
    global ThisPlayer
    global GameFinished
    ThisPlayer = "O"  # Player O always starts
    GameFinished = False


def OutputBoard():
    print("The board as it currently stands...\n")
    print("Choose your column")
    print("1 2 3 4 5 6 7\n")

    for Row in range(6, 0, -1):
        for Column in range(7):
            print(Board[Row][Column], end=" ")
        print()
    print()


def ColumnNumberValid():  # returns whether or not the column number is valid
    global ColumnNumber
    Valid = False
    if (ColumnNumber >= 0) and (ColumnNumber <= 6):
        if Board[6][ColumnNumber] == BLANK:  # at least 1 empty space in column
            Valid = True
    return Valid


def ThisPlayerChoosesColumn():  # returns a valid column number
    global ColumnNumber
    print("Player ", ThisPlayer, "'s turn.")
    ColumnNumber = int(input("Enter a valid column number: ")) - 1
    while ColumnNumberValid() == False:  # check whether the column number is valid
        ColumnNumber = int(input("Enter a valid column number: ")) - 1
    return ColumnNumber


def FindNextFreePositionInColumn():  # returns the next free position
    ThisRow = 1
    while Board[ThisRow][ValidColumn] != BLANK:  # find first empty cell
        ThisRow += 1
    return ThisRow


def ThisPlayerMakesMove():
    global Board
    global ValidColumn
    global ValidRow
    ValidColumn = (
        ThisPlayerChoosesColumn()
    )  # use a module to return valid column number

    ValidRow = FindNextFreePositionInColumn()  # use a module to return row number
    Board[ValidRow][ValidColumn] = ThisPlayer


def CheckHorizontalLineInValidRow():
    global WinnerFound
    for i in range(5):
        if i < 5:
            if (
                (Board[ValidRow][i - 1] == ThisPlayer)
                and (Board[ValidRow][i] == ThisPlayer)
                and (Board[ValidRow][i + 1] == ThisPlayer)
                and (Board[ValidRow][i + 2] == ThisPlayer)
            ):
                WinnerFound = True


def CheckVerticalLineInValidColumn():
    global WinnerFound
    if (ValidRow == 4) or (ValidRow == 5) or (ValidRow == 6):
        if (
            (Board[ValidRow][ValidColumn] == ThisPlayer)
            and (Board[ValidRow - 1][ValidColumn] == ThisPlayer)
            and (Board[ValidRow - 2][ValidColumn] == ThisPlayer)
            and (Board[ValidRow - 3][ValidColumn] == ThisPlayer)
        ):
            WinnerFound = True


def CheckDiagonalRight():
    global WinnerFound
    for i in range(5):
        if (
            (Board[ValidRow][i] == ThisPlayer)
            and (Board[ValidRow + 1][i + 1] == ThisPlayer)
            and (Board[ValidRow + 2][i + 2] == ThisPlayer)
            and (Board[ValidRow + 3][i + 3] == ThisPlayer)
        ):
            WinnerFound = True


def CheckDiagonalLeft():
    global WinnerFound
    for i in range(7):
        if i > 4:
            if (
                (Board[ValidRow][i] == ThisPlayer)
                and (Board[ValidRow + 1][i - 1] == ThisPlayer)
                and (Board[ValidRow + 2][i - 2] == ThisPlayer)
                and (Board[ValidRow + 3][ValidColumn - 3] == ThisPlayer)
            ):
                WinnerFound = True


def CheckDiagonalLine():
    global WinnerFound
    if ValidRow < 4:
        CheckDiagonalRight()
    elif ValidRow == 4:
        CheckDiagonalLeft()
        CheckDiagonalRight()
    elif ValidRow > 4:
        CheckDiagonalLeft()


def CheckForFullBoard():
    global GameFinished
    BlankFound = False
    ThisRow = 0
    while (ThisRow != 6) and (BlankFound == False):
        ThisColumn = 0
        ThisRow += 1
    while (ThisColumn != 7) and (BlankFound == False):
        ThisColumn += 1
        if Board[ThisRow][ThisColumn] == BLANK:
            BlankFound = True

    if BlankFound == False:
        print("It is a draw")
        GameFinished = True


def CheckIfThisPlayerHasWon():
    global WinnerFound
    global GameFinished
    WinnerFound = False
    CheckHorizontalLineInValidRow()
    if WinnerFound == False:
        CheckVerticalLineInValidColumn()
        if WinnerFound == False:
            CheckDiagonalLine()
    if WinnerFound == True:
        GameFinished = True
        print(ThisPlayer, " is the winner")
    else:
        CheckForFullBoard()


def SwapThisPlayer():
    global ThisPlayer
    if ThisPlayer == "O":
        ThisPlayer = "X"
    else:
        ThisPlayer = "O"


# ******************* This is the main program ******************
def main():
    InitialiseBoard()  # Call board set up routines
    SetUpGame()
    OutputBoard()

    # Below is the loop for players to make moves
    while GameFinished == False:
        ThisPlayerMakesMove()
        OutputBoard()
        CheckIfThisPlayerHasWon()
        if GameFinished == False:
            SwapThisPlayer()  # next player takes their turn


main()  # Call the main program
Larz60+ write Jan-21-2022, 12:23 PM:
Please post all code, output and errors (it it's entirety) between their respective tags. Refer to BBCode help topic on how to post. Use the "Preview Post" button to make sure the code is presented as you expect before hitting the "Post Reply/Thread" button.
Fixed for you this time. Please use bbcode tags on future posts.
Reply
#2
I think it is a great idea that you test for the winner based on the last move, but your tests are all wrong. You need to count the number of contiguous markers in a line. You are kind of doing that but forgetting that there might be markers before and after the recently played marker. If the last marker was placed at (2, 2) four consecutive markers could be in (0,2), (1,2), (2,2), (3,2). You look in only one direction.

My test for checking if there are 4 markers in a row would look like this:
def get(row, column, board):
    """Return marker in board[row][column] if row and column are valid, else return None"""
    if 0 <= row < ROWS and 0 <= column <= COLUMNS:
        return board[row][column]
    return None

def winningRow(row, column, board):
    """Return True if there are 4 or more matching markers in a row"""
    marker = get(row, column, board)
    count = 1
    c = column-1
    while get(row, c) == marker:
        count += 1
        c -= 1
    c = column+1
    while get(row, c) == marker:
        count += 1
        c += 1
    return count >= 4
Using a pattern like this you could write a generic tester that would work for rows, columns and diagonals.


A few comments about your code:

It is good that you are breaking up your code into functions, but you lose much of the benefits of functions by using global variables to pass information to and from the function. For example:
def ColumnNumberValid():  # returns whether or not the column number is valid
    global ColumnNumber
    Valid = False
    if (ColumnNumber >= 0) and (ColumnNumber <= 6):
        if Board[6][ColumnNumber] == BLANK:  # at least 1 empty space in column
            Valid = True
    return Valid
should look like this:
def columnNumberValid(column, board):
    """"Returns True if the column is in range and has an empty spot"""
    if 0 <= column < COLUMNS:  # Do not sprinkle code with numerical constants like 6.
        return board[ROWS-1][column] == BLANK
    return False
This might be even better:
def columnNumberValid(column, board):
    try:
        return board[ROWS-1][column] == BLANK
    except IndexError:
        return False
Other than BLANK (and ROWS and COLUMNS in my example) I don't think any of your variables should be global. Initialize the variables in main() and pass them as arguments to your functions.

You should avoid functions that don't provide any real benefit. I would get rid of these functions and move the code back into main.
def InitialiseBoard():
    global Board
    Board = [[BLANK] * 7 for x in range(7)]  # Creates the array of blank values
 
 
def SetUpGame():  # Set up the parameters for the beginning of the game
    global ThisPlayer
    global GameFinished
    ThisPlayer = "O"  # Player O always starts
    GameFinished = False
None of these functions are used more than once, they don't reduce the length of the code, and I don't think they make your code easier to understand.

Do not duplicate code. This loop:
    print("Player ", ThisPlayer, "'s turn.")
    ColumnNumber = int(input("Enter a valid column number: ")) - 1
    while ColumnNumberValid() == False:  # check whether the column number is valid
        ColumnNumber = int(input("Enter a valid column number: ")) - 1
    return ColumnNumber
is better written as this:
    print("Player ", ThisPlayer, "'s turn.")
    while True:
        ColumnNumber = int(input("Enter a valid column number: ")) - 1
        if ColumnNumberValid():
            return ColumnNumber
Each duplicate line of code is a potential maintenance nightmare.

I don't understand the logic of this function.
def CheckForFullBoard():
    global GameFinished
    BlankFound = False
    ThisRow = 0
    while (ThisRow != 6) and (BlankFound == False):
        ThisColumn = 0
        ThisRow += 1
    while (ThisColumn != 7) and (BlankFound == False):
        ThisColumn += 1
        if Board[ThisRow][ThisColumn] == BLANK:
            BlankFound = True
 
    if BlankFound == False:
        print("It is a draw")
        GameFinished = True
The first while loops sets ThisColumn = 0 and ThisRow = 6. Why not just set ThisColumn=0 and thisRow=6 instead of looping. Better yet, you know how many moves it takes to fill the board. Why not get rid of the full board check and do something more like this:
for _ in range(ROWS*COLUMNS):
    ThisPlayerMakesMove()
    OutputBoard()
    CheckIfThisPlayerHasWon()
    SwapThisPlyer()
    if GameFinished:
        break;
If you have 7 rows and 7 columns, the board is full after players make 49 moves. There is no reason to check the board.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  I need help with Connect-Four Game Board niiakoadjei 2 2,620 May-26-2021, 04:54 PM
Last Post: jefsummers
  Connect 4 Game DarksideMoses 11 10,692 May-15-2018, 02:02 PM
Last Post: buran

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020