CS 111
Spring 2018

Old version

This is the CS 111 site as it appeared on May 10, 2018.

Problem Set 9 FAQ

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.

General questions

  1. Why does code I copied from the problem set give an indent error?

    Sometimes IDLE does not format code properly when you copy and paste it into your editor. Be sure that there is an indent between the function header and the code inside of it. Make sure that the doc string is indented as well.

    # this is incorrect
    def foo():
    """ A simple function
    """
    
    # this is correct
    def foo():
        """ A simple function
        """
    

Part I

  1. When writing my __repr__, I’m confused about how to get the column numbers at the bottom of the string. Do you have any suggestions?

    First, you will need a loop that gives you the column numbers one by one. Inside the body of that loop, you should concatenate the appropriate string for each column number to the string s that you are building up. You will need to include both a space and a string for the digit for that column.

    Two other notes:

    • It’s possible that some of the numbers could be 10 or greater,
      so you will need to use the mod operator (%) to get just the units digit of the number.

    • You can turn the units digit into a string by using the str() function. This is necessary in order for you to concatenate the digit to the string s.

  2. What should I do if my add_checker function isn’t working?

    It’s a good idea 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. The while loop should accomplish this by starting from the top (row = 0) and checking 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.

  3. 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?

  4. 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?

  5. 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
    
  6. 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.

Part II, General Questions

  1. 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.

  2. 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.

  3. 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.

Problem 3

  1. 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.

Problem 4

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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?