Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Connect 4 assigment
#1
Hello team,

I have this assignment to do:
Quote:
Have you ever played "Connect 4"? It's a popular kid's game by the Hasbro company. In this project, your task is create a Connect 4 game in Python. Before you get started, please watch this video on the rules of Connect 4:

https://youtu.be/utXzIFEVPjA

Once you've got the rules down, your assignment should be fairly straightforward. You'll want to draw the board, and allow two players to take turns placing their pieces on the board (but as you learned above, they can only do so by choosing a column, not a row). The first player to get 4 across or diagonal should win!

Normally the pieces would be red and black, but you can use X and O instead.
I broke it down into three steps:

1. Draw the board
2. Update the board with user input
3. Verify if there is 4 across or diagonal to end the game

So far I just manage to come up with the first step, it looks like that:

'''

012345678
 | | | |  0
--------- 1
 | | | |  2
--------- 3
 | | | |  4
--------- 5
 | | | |  6
--------- 7
 | | | |  8

'''

def drawField():
    for row in range(9):
        if row % 2 == 0:
            for column in range(9):
                if column % 2 == 0:
                    if column != 8:
                        print(' ', end='')
                    else:
                        print(' ')
                else:
                    print('|', end='')
        else:
            print('-'*9)

drawField()

player = 1
currentField = [' ', ' ', ' ', ' ', ' ']
drawField()
while(True):
    print(f'Players {player} turn')
    move = int(input('Column: '))
    if player == 1:
        currentField[move] == 'X'
        player = 2
    else:
        currentField[move] == 'O'
    drawField(currentField)
What I am struggling with is to update the board with the user's input.

Firstly I want to make the function add an 'X' or 'O' to the bottom row , or the 8th row, of the column the user input. Then check if it is not ' ', and add to the 7th row and so on.

I have it clear in my mind but I cannot code it.

Any thoughts are most welcome.
Reply
#2
I would use a list of lists to make your 4x4 grid and separate the logic from the display. I would also define a function to display the grid, doing input->logic->display with the logic part also checking to see if there is a winner.
Reply
#3
Hi,

By coincidence I wrote a "Connect 4" game the other day,
a "textual" version and a GUI Tkinter version.
I agree with post #2. Define a 2D matrix, play the game in memory,
and print the whole matrix after each move.

The "hardest" part is to validate if there is a winner,
vertically, horizontally (and oblique?)

Paul
Reply
#4
Team,

I managed to add user input as I wanted, by stacking them according to the column the users choose. So far the solution looks like that:

def drawField(field):
    for row in range(9): #0,1,2,3,4...
                         #0,.,1,.,2...
        if row % 2 == 0: #0.2.4 ...
            practicalRow = int(row/2) 
            for column in range(9): #0,1,2,3,4...
                                    #0,.,1,.,2...
                if column % 2 == 0:#0.2.4
                    practicalColumn = int(column/2) #0,1,2...
                    if column != 8:
                        print(field[practicalRow][practicalColumn], end='')
                    else:
                        print(field[practicalRow][practicalColumn])
                else:
                    print('|', end='')
        else:
            print('-'*9)

player = 1
currentField = [[' ', ' ', ' ', ' ', ' '], [' ', ' ', ' ', ' ', ' '], [' ', ' ', ' ', ' ', ' '], [' ', ' ', ' ', ' ', ' '], [' ', ' ', ' ', ' ', ' ']]
rowCount = [4, 4, 4, 4, 4]
drawField(currentField)
while(True):
    print(f'Players {player} turn')
    moveColumn = int(input('Column: '))
    if player == 1:
        if currentField[rowCount[moveColumn]][moveColumn] == ' ':
            currentField[rowCount[moveColumn]][moveColumn] = 'X'
            rowCount[moveColumn] -= 1
            player = 2
    else:
        if currentField[rowCount[moveColumn]][moveColumn] == ' ':
            currentField[rowCount[moveColumn]][moveColumn] = 'O'
            rowCount[moveColumn] -= 1
            player = 1
    drawField(currentField)
Now I have not idea how to check if there are four 'X' or 'O' horizontally, vertically or diagonally.

Any thoughts are most welcome.
Reply
#5
You seem to have a 5x5 board.
I like gravity to play it's role,
coins drop to the lowest position possible vertically (like in a real game)
So the user only needs to enter a column of choice (x-axis), the y value is automatically
determined by how many coins are already in that column.
A user entry is an x,y element in your 2D list (matrix) that turns into an X or an O.
Then you need 3 validation routines that loop over the matrix:
-one that counts how many of the same are on each row (adjacent)
-in each column
-and diagonally (a bit more tricky index-wise)
If one of the counts gets to 4, you have a winner.
Paul
Reply
#6
Do not think about the board as a visual thing. For the purposes of your program it is a list of columns. Each column has 8 spots. A spot may be empty or it may be held by player 1 or player 2. How hard it will be to write your program is going to depend a lot on how you represent the board and players.

I wrote this game just to see where the tricky parts are. Luckily there really aren't any. I decided to represent the board as a list of columns. A column being a list of numbers. Each element in the list is initialized to 2. I chose two because I want X to b zero, O to be one, and empty to be something else. By making them 0, 1, 2 I can make a list of markers to use when drawing the board. markers = 'X0 '. By making X == 1 and O == 2 it is really easy for me to toggle between players. Next player = (current player + 1) % 2. I wrote a simple function to print the board. After a few plays it looks something like this:
Output:
O X O 1 2 3 4 5 6 7 8 X play [1..8]:
When X enters '2' as her play, the board will look like this:
Output:
X O X O 1 2 3 4 5 6 7 8 O play [1..8]:
Finding the winner is the most difficult part of the program, but there is an obvious, brute force method that is easy to write and fairly straight forward. You have to think about how you would find the winner in connect 4 if you were limited to only looking at one cell at a time. Remember that only the last player can win, so you only have to check for 4 X's or 4 O's in a row, not both. Let's say X just played. You might start at board[0][0] and see if that is a X. If so you have a sequence of 1 X's. If board[0][1] is an X there is a sequence of 1 X's. If board[0][2] is empty or an O, my streak ends at 2, which is not a winning streak. I then check for a horizontal streak, an upward diagonal streak, and a downward diagonal streak. As soon as I find a streak of 4 or more the game is over.
Reply
#7
Hello team,

Ok this verification step is being a pain in the back. I am trying to brute force it before making anything more sophisticated.

I reduced the field to facilitate the tests:

def checkWinner(field, player):
    for items in field:
        print(items)
        if items[2] == 'X':
            print(f'Player {player} won')
        if items[1] == 'X':
            print(f'Player {player} won')
            
player = 1
currentField = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
rowCount = [2, 2, 2]

while(True):
    print(f'Players {player} turn')
    #moveRow = int(input('Row: '))
    moveColumn = int(input('Column: '))
    if player == 1:
        if currentField[rowCount[moveColumn]][moveColumn] == ' ':
            currentField[rowCount[moveColumn]][moveColumn] = 'X'
            rowCount[moveColumn] -= 1
            checkWinner(currentField, player)
            player = 2
    else:
        if currentField[rowCount[moveColumn]][moveColumn] == ' ':
            currentField[rowCount[moveColumn]][moveColumn] = 'O'
            rowCount[moveColumn] -= 1
            checkWinner(currentField, player)
            player = 1
The function checkWinner() does the verification, it receives the currentField, and the current player.

So far it checks only the rows.

What buggers me is between line 4 and 7. This if statement fires correctly.
if items[2] == 'X':
But this if statement, which is supposed to check whether the second line has all 'X's, fires when there is two 'X's.
if items[1] == 'X':


I already tried if items[2] == ['X', 'X', 'X'] but it does not work as I want.
Reply
#8
Stop worrying about finding a winner and work on code that finds series.

You call your players 'X' and 'O'. I call may players 0 and 1. It doesn't matter much because the code is pretty much the same.

If I have a board[columns][rows] and I want to find the length of a row of 'X' starting at column c, row r I can do this:
def row_count(r, c):
    cnt = 0
    while c < 8 and board[c][r] == 'X':
        cnt+= 1
        c += 1
    return cnt
If I want to find the length of a column of 'X' starting at c, r I can do this:
def column_count(r, c):
    cnt = 0
    while r < 8 and board[c][r] == 'X':
        cnt+= 1
        r += 1
    return cnt
Can you come up with the logic to do an upward diagonal (/) or a downward diagonal (\)?

The code for all of these looks pretty much the same. I know so because I wrote one routine that knows how to search in any direction depending on the parameters are passed.
Reply
#9
Team,

I cannot come up with a solution because I am dumb.

Here is a connect 4 code I found online.

import numpy as np

ROW_COUNT = 6
COLUMN_COUNT = 7

def create_board():
	board = np.zeros((ROW_COUNT,COLUMN_COUNT))
	return board

def drop_piece(board, row, col, piece):
	board[row][col] = piece

def is_valid_location(board, col):
	return board[ROW_COUNT-1][col] == 0

def get_next_open_row(board, col):
	for r in range(ROW_COUNT):
		if board[r][col] == 0:
			return r
def print_board(board):
	print(np.flip(board, 0))

def winning_move(board, piece):
	# Check horizontal locations for win
	for c in range(COLUMN_COUNT-3):
		for r in range(ROW_COUNT):
			if board[r][c] == piece and board[r][c+1] == piece and board[r][c+2] == piece and board[r][c+3] == piece:
				return True 

	# Check vertical locations for win
	for c in range(COLUMN_COUNT):
		for r in range(ROW_COUNT-3):
			if board[r][c] == piece and board[r+1][c] == piece and board[r+2][col] == piece and board[r+3][c] == piece:
				return True

	# Check positively sloped diagonals
	for c in range(COLUMN_COUNT-3):
		for r in range(ROW_COUNT-3):
			if board[r][c] == piece and board[r+1][c+1] == piece and board[r+2][c+2] == piece and board[r+3][c+3] == piece:
				return True
	
	# Check negatively 
	for c in range(COLUMN_COUNT-3):
		for r in range(3, ROW_COUNT):
			if board[r][c] == piece and board[r-1][c+1] == piece and board[r-2][c+2] == piece and board[r-3][c+3] == piece:
				return True
            
board = create_board()
print_board(board)
game_over = False
turn = 0

while not game_over:
	# Ask for plaer 1 input
	if turn == 0:	
		col = int(input('Player 1 make your Selection (0-6)'))

		if is_valid_location(board, col):
			row = get_next_open_row(board, col)
			drop_piece(board, row, col, 1)
			
			if winning_move(board, 1):
				print('Player 1 win !!')
				game_over = True
	# Ask for player 2 input

	else:
		col = int(input('Player 2 make your Selection (0-6)'))

		if is_valid_location(board, col):
			row = get_next_open_row(board, col)
			drop_piece(board, row, col, 2)

			if winning_move(board, 1):
				print("Player 1 wins !!")
				game_over = True
	print_board(board)
	
	turn += 1
	turn = turn % 2
Reply
#10
This part of the code is pretty horrible:
    if turn == 0:   
        col = int(input('Player 1 make your Selection (0-6)'))
 
        if is_valid_location(board, col):
            row = get_next_open_row(board, col)
            drop_piece(board, row, col, 1)
             
            if winning_move(board, 1):
                print('Player 1 win !!')
                game_over = True
    # Ask for player 2 input
 
    else:
        col = int(input('Player 2 make your Selection (0-6)'))
 
        if is_valid_location(board, col):
            row = get_next_open_row(board, col)
            drop_piece(board, row, col, 2)
 
            if winning_move(board, 1):
                print("Player 1 wins !!")
                game_over = True
Do you notice anything about this code? Anything, repetitive? Like the code for the first player and the second player being identical except the first player is "1" and the second player is "2"? Do you think you could improve upon this code by removing all the duplicate code and make a more generic coding that works for both player 1 and player 2? At least then you wouldn't be blatantly plagiarizing if you turned this in.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Sorting list - Homework assigment ranbarr 1 2,227 May-16-2021, 04:45 PM
Last Post: Yoriz
  Python homework assigment makisha 3 3,279 Feb-28-2019, 10:21 PM
Last Post: Yoriz
  Display school assigment Vittya 2 3,485 Nov-09-2017, 02:13 PM
Last Post: sparkz_alot

Forum Jump:

User Panel Messages

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