0

I'm trying to make a Connect 4 game. Here is my code:

board = {}

for i in range(1,8):
    board[f'{i}_column'] = {}

    for a in range(1,7):
        board[f'{i}_column'][f'{i}x{a}_position'] = 'Empty'

class Checker:
    def __init__(self,color):
        self.color = color

    def find_column_amount(self,column):
        self.column_num = 0
        self.column_full = False
        for i in range(0,6):
            if board[f'{column}_column'][f'{column}x{6-i}_position'] != 'Empty':
                self.column_num += 1
            else:
                pass
        if self.column_num >= 6:
            self.column_full = True

    def place(self,column):
        self.find_column_amount(column)
        if self.column_full == False:
            if column <= 7 and column > 0:
                board[f'{column}_column'][f'{column}x{6-self.column_num}_position'] = self.color.title()
                else:
                print('You\'re out of the range!')
        else:
            print(f'Column {column} is full!\nTry another one!')

    def check_win(self):
        for d in range(1,7):
            for c in range(1,5):
                for b in range(c,c+4):
                    vars()[f'value_{b-c+1}'] = board[f'{b}_column'][f'{b}x{d}_position']

                if value_1 == value_2 == value_3 == value_4 and (value_1 and value_2 and value_3 and value_4) != 'Empty':
                    self.win()

    def win(self):
        print('You won!')

To see if it worked I ran this:

p1 = Checker('red')
p1.place(1)
p1.place(2)
p1.place(3)
p1.place(4)
p1.check_win()

I tried this code and it didn't work. The part that was wrong is the check_win function. I tested the code outside the function, and changed the self.win() to print('You won!') and it worked.

for d in range(1,7):
    for c in range(1,5):
        for b in range(c,c+4):
            vars()[f'value_{b-c+1}'] = board[f'{b}_column'][f'{b}x{d}_position']

        if value_1 == value_2 == value_3 == value_4 and (value_1 and value_2 and value_3 and value_4) != 'Empty':
            print('You won!')

The result was this:

You won!

When I plug it into the function again, it didn't work. I don't know what I'm doing wrong. Can anyone tell me how to fix this?

  • 4
    This: `(value_1 and value_2 and value_3 and value_4) != 'Empty` is not how you compare multiple variables against a value. – khelwood May 08 '20 at 19:15
  • See [How to test multiple variables against a value?](https://stackoverflow.com/questions/15112125/how-to-test-multiple-variables-against-a-value) – khelwood May 08 '20 at 19:16
  • 2
    The dynamically generated dictionary keys and the `vars()` stuff is a REALLY unnecessarily complicated way of storing a 2d array. I'm going to go out on a limb and guess that something in there is causing a problem. – Samwise May 08 '20 at 19:16

1 Answers1

0

I just did a quick pass through this code and replaced all the stuff that made my head hurt with simpler code that does what it seems like the original code was trying to do. (This could definitely be simplified further, but I was just going for the low-hanging fruit in an effort to make it run.) Seems to work now and it was faster to just rewrite those bits of code than try to debug them.

board = [[None for a in range(7)] for i in range(8)]


class Checker:
    def __init__(self, color):
        self.color = color

    def find_column_amount(self, column):
        self.column_num = 0
        self.column_full = False
        for i in range(0, 6):
            if board[column][6-i] is not None:
                self.column_num += 1
            else:
                pass
        if self.column_num >= 6:
            self.column_full = True

    def place(self, column):
        self.find_column_amount(column)
        if not self.column_full:
            if column <= 7 and column > 0:
                board[column][6-self.column_num] = self.color.title()
            else:
                print('You\'re out of the range!')
        else:
            print(f'Column {column} is full!\nTry another one!')

    def check_win(self):
        for d in range(1, 7):
            for c in range(1, 5):
                values = {board[b][d] for b in range(c, c+4)}
                if len(values) == 1 and values.pop() is not None:
                    self.win()

    def win(self):
        print('You won!')


p1 = Checker('red')
p1.place(1)
p1.place(2)
p1.place(3)
p1.place(4)
p1.check_win()  # prints "You won!" now
Samwise
  • 68,105
  • 3
  • 30
  • 44
  • 1
    In a higher-hanging fruit context, `check_win` should be done every time a token is placed and it could check for neighbours in the 8 appropriate directions rather than checking all the board. Edit: this is the comment more for the OP than your answer actually. – Guimoute May 08 '20 at 19:31
  • 1
    also it'd make more sense for the board to be a self-contained object that handles the checking and the moves for both sides, rather than a `Checker` class that only operates for one player and accesses the board as a global. – Samwise May 08 '20 at 20:13
  • You can also make the concept of checking for N identical pieces in a row a lot more generic: https://stackoverflow.com/a/61395017/3799759 – Samwise May 08 '20 at 20:14
  • Also, the entire `find_column_amount` function could be a single call to `index` if the columns are lists instead of dicts. :) – Samwise May 08 '20 at 20:45