If you don’t see your question here, post it on Piazza or come to office hours! See the links in the navigation bar for both of those options.
What should I do if my add_checker
function isn’t working?
One option is to start with the buggy version of this method from
the lecture notes. It isn’t perfect, but it’s a great start. When
we add a checker to a column, we need to find the row in the slot
where it lands. If you follow our basic approach, the while
loop
should start from the top (row = 0
) and check every row until it
finds a checker.
When this loop finds a checker and stops, the value of row refers
to a row where there is a checker. Are there any checkers above
this row? Where could we put the new checker in terms of row
?
The other issue is that the while
loop doesn’t stop before it
goes beyond the bottom row of the board. This causes an index
error because we try to check if the slot at the specified row and
column contains a space, but the row doesn’t exist. One way to
avoid this problem is to check inside the loop if the row index
has become too large, and to break out of the loop as needed.
Alternatively, you could also use a completely different
approach. For example, to avoid the index errors that our buggy
version produced, you could use an index-based for
loop that
takes on the row indices, and use a break
statement to end the
loop early as needed.
Regardless of the approach that you take, use concrete examples
to help you figure out the logic – both for the loop itself and
for what should happen after the loop. For example, once your
loop is done, do you need to adjust the final value of the
variable that you are using for the row
index? If so, do you
always need to adjust it, or do you need to decide whether or not
to adjust it?
How should I remove a checker from a column?
The buggy code we gave you for add_checker
overwrites the
topmost checker in a column. How could you modify it so that it
replaces the first checker with a space, but does nothing if the
column is empty?
My code for can_add_to
doesn’t work in some cases.
What am I doing wrong?
Try to think about this problem without using any loops. Suppose we want to drop a checker into a column. Which row in that column can we look at to see if the column is full? If this spot was empty, what would that mean?
What row and column ranges do I need to check for an up-diagonal win?
To come up with the correct range, try to think about when it doesn’t make sense to even look for a win. For example, suppose there is a board of some height greater than 4 and width 5. Starting at row 3 and column 1, we could have an up diagonal win for X, as shown below. Would it be possible to have an up diagonal win starting at row 2? Why not? Could we have an up diagonal win starting at column 2?
0 | | | | |x| 1 | | | |x| | 2 | | |x| | | 3 | |x| | | | 4 | | | | | | ... 0 1 2 3 4
How do we count the number of moves a player makes?
Read over the requirements for the next_move
method in the
problem set. That method should be updating the player’s num_moves
attribute. If we ever want to know how many moves a player has
made, we can just check this attribute.
I found a bug in my code for Part I, and it’s too late to resubmit it. Will this hurt my grade for Part II?
No, we will use our own versions of the Board
and Player
classes
to grade Part II. Any bugs you have in Part I will not affect
your grade for Part II.
Something seems wrong in my Part II, but my code looks correct. What should I do?
It’s possible that there may be a bug in one of your Board
and
Player
methods that you wrote earlier. Use debugging
print
statements to make sure the results of your methods make
sense. If you think your work from Part I has bugs, come to office
hours and we can help you to fix them.
In addition, you should check for subtle bugs including:
Using ==
instead of =
. For example, let’s say that we want
to assign the result of the method call p.foo()
to the
variable x
. This line of code is incorrect:
x == p.foo() # incorrect!
Instead, you need to use the assignment operator:
x = p.foo() # correct!
Forgetting to include the empty parentheses when making a method call for a method that has no inputs. For example, we might forget to include the parentheses from the line above:
x = p.foo # incorrect! need parens as shown above
Without the parentheses, the method call doesn’t actually get made!
Passing in the wrong inputs for a method. For example, let’s
say that you’re trying to determine if the board b
is a win
for the current player so that you can figure out the player’s
score for the column col
. If you’re not careful, you might
make the following call:
if b.is_win_for(col) == True: # incorrect!
This doesn’t work, because is_win_for
needs you to pass in the
checker of the player whose win you are trying to check for –
not a column index like col
. Instead, the correct call
would look something like this:
if b.is_win_for(self.checker) == True: # correct!
Before you attempt to call a method, remind yourself of what inputs the method is supposed to take so that you can call it correctly!
When I use RandomPlayer
or AIPlayer
, why am I seeing 0
for the number of moves?
When you override next_move
in any class, make sure you are
incrementing the attribute that stores the number of moves that
the player has made so far. Look over your Player
class to see
which attribute this is.
Do we need to follow the guidelines for the RandomPlayer
class?
Yes, please create the RandomPlayer
class by inheriting from the
Player
class. This is an excellent exercise in seeing the
advantages of object-oriented programming. Using inheritance, we
do not need to duplicate code from the Player
class and we can
tweak its default behavior by making a RandomPlayer
always give a
random move. You will also use this technique of inheriting classes and
overriding methods in Problem 4.
If inheritance or overriding methods seems confusing, look over the lecture notes to see some examples.
Where should I begin on Problem 4?
Start by reading over the lecture notes on AI for Connect Four. Be sure you understand how the lookahead strategy works before you start coding. Once you go through some of the examples we gave and the worksheet, you can start with the pseudocode given in the lecture notes. If some part of it seems confusing, be sure to also look over the hints given in the problem set which go into more detail.
Does each line in the pseudocode represent one line of Python?
Not necessarily. Each statement in the pseudocode could be implemented with one or more statements. Some of the pseudocode could even be implemented using conditional statements.
Do I need to create a separate opponent class?
No, the opponent you construct in scores_for
will be an instance
of the AIPlayer
class.
How do I create an opponent?
You need to create an instance of the AIPlayer
class whose
checker is the opposite of the current player. In addition, they
should use the same tiebreaking strategy as the current player,
and have a lookahead that is one less than the lookahead used by
the current player.
How do I determine the player’s scores from the opponent’s?
You can determine this by answering the following questions.
Suppose we are determining the score for a column col
.
The opponent’s best/max score after the player moves into col
is 100, so the opponent wins. What should our player’s score be
in this case?
The opponent’s best/max score after the player moves into col
is 0,
so the opponent loses. What should our player’s score be in this case?
The opponent’s best/max score after the player moves into col
is 50, so neither the opponent nor the player wins. What should
our player’s score be in this case?
Last updated on April 8, 2024.