108

Assignment 1 | The Slide Game

In Assignment 1, you write Python code that will be used by a game called "The Slide Game". You can complete the whole assignment with only the concepts from Weeks 1, 2, and 3 of the course. This handout explains the problem being solved, and the tasks to complete, for the assignment. Please read it carefully and in its entirety.

Goals of this assignment:

The main goal of this assignment is that students will use the Function Design Recipe to plan, document, implement, and test functions. This entails the following sub-goals:

  • Students will describe what a function does appropriately using docstrings.
  • Students will show how to use a function appropriately with docstring examples.
  • Students will write function bodies using variables, numeric types, strings, and conditional statements.
  • Students will learn to use: Python 3, IDLE or Wing 101, the provided starter code, a checker module, and other tools.

Logistics:

  • Due Date: Thursday, February 11 by 11:59 PM.
  • Late Policy: There is a 1-hour grace period after the due date. Any submissions past the grace period will NOT be accepted.

The Slide Game

The Slide Game is a two player game played on a two-dimensional game board. The game board has a number of rows and columns specified at the beginning of the game and starts off filled with black squares. The following image is an example of a game board with 4 rows and 5 columns:

    Each player has an infinite number of squares in one colour (e.g., one player has red squares; the other player has yellow squares). There are two moves that a player can make to get their squares onto the game board: The first player to place NUM_MATCH of their squares in a straight line wins the game. Here, NUM_MATCH is a number like 3. The "straight line" can be a horizontal, vertical, or diagonal line of squares on the game board.

Slide Right

The "Slide Right" move allows a player to slide one of their squares onto the game board. The player must indicate which row they would like to slide their square onto. Because the size of the game board is fixed, the right-most square in the game board will "fall off" to make room for the player's own square.

Slide Left

The "Slide Left" move allows a player to slide one of their squares onto the game board. The player must indicate which row they would like to slide their square onto. Because the size of the game board is fixed, the left-most square in the game board will "fall off" to make room for the player's own square.

Winning the game

The objective of the game is to be the first player to connect NUM_MATCH squares in a row either horizontally (or simply "across"), vertically (or simply "down"), or diagonally. Diagonal lines come in two orientations: downward to the right and downdward to the left. Assuming NUM_MATCH is 3, here are some examples of where squares should be placed in order to win the game:



The Slide Game in Python

In this assignment, you will be writing Python code for the Slide Game. Instead of using colourful pictures of squares on a game board (as we saw in the last section), in our Python code we will use text to represent the squares (i.e., Python's str type). In the rest of this section, we provide some details to help you understand how to approach the code you need to write.

Starter Code
Constants

Constants are special variables whose values should not change once assigned -- you saw constants in the Week 2 Prepare module. A different naming convention (uppercase pothole) is used for constants, so that programmers know to not change their values. For example, in the starter code, the constant SQUARE_BLACK is assigned the value '-' at the beginning of the module and the value of SQUARE_BLACK should never change in your code. When writing your code, if you need to use a black square, you should use SQUARE_BLACK. The same applies for the other constant values.

Using constants simplifies code modifications and improves readability and changeability. For example, if we later decide to use a different character to represent a black square, we would only have to make a change in one place (the SQUARE_BLACK assignment statement), rather than throughout the program. This also makes our code more readable — whether we use '-' or any other character to represent a black square, we write our code using the constant SQUARE_BLACK so it is clear to the reader what we mean.

Representing the game board

A game board can be thought of as a table with rows (see the constant NUM_ROW) and columns (NUM_COL). The size of a game board does not change during the course of a game. For example, here is an empty game board with 4 rows and 5 columns:

                           1   2   3   4   5
        1  - | - | - | - | -
        2  - | - | - | - | -
        3  - | - | - | - | -
        4  - | - | - | - | -
            

A location in the game board can be described by indicating the row and then the column. For example, using the empty game board above, (1, 1) indicates the location at the top-left of the game board. And (4, 5) indicates the bottom-right of the game board.

We will use strings (i.e., the str type) to represent the squares on a game board. The black squares will be: '-' (SQUARE_BLACK). The red squares will be: 'R' (SQUARE_RED). The yellow squares will be: 'Y' (SQUARE_YELLOW).

We have not yet studied how to store 2-dimensional information in Python programs, so we will need a different way to represent our game board. The approach that we will take for this assignment is to store the rows one after another, starting with the first row. Using our empty (all black squares) game board example, this is: '--------------------'. Using a slightly more interesting example:

                           1   2   3   4   5
        1  R | - | - | - | -
        2  - | - | - | - | Y
        3  R | - | - | - | -
        4  Y | - | - | - | -
                

Would be represented as 'R--------YR----Y----'.

Accessing a square in the game board

We have used row and column indices to describe the position of each square in the grid representation of a game board. But each character in a Python str is in a position that is described by a single index. How is the Python program going to translate between row and column indices and str indices? To answer this question, we will have to determine a formula!

Consider the following concrete example:

                           1   2   3   4   5
        1  A | B | C | D | E
        2  F | G | H | I | J
        3  K | L | M | N | O
        4  P | Q | R | S | T
                

Which, as a string, is: 'ABCDEFGHIJKLMNOPQRST'. Let us use the table below to derive a formula that calculates the index based on: the location (i.e., row and column) and game board size (i.e., NUM_ROW and/or NUM_COL).

location index character
(1, 1) 0 A
(1, 2) 1 B
(1, 3) 2 C
(1, 4) 3 D
(1, 5) 4 E
(2, 1) 5 F
(2, 2) 6 G
(2, 3) 7 H
(2, 4) 8 I
(2, 5) 9 J
(3, 1) 10 K
(3, 2) 11 L
(3, 3) 12 M
(3, 4) 13 N
(3, 5) 14 O
(4, 1) 15 P
(4, 2) 16 Q
(4, 3) 17 R
(4, 4) 18 S
(4, 5) 19 T

From the table above, we see that the character in the square with position (2,1) (i.e., the square at row 2 and column 1) has index 5. The other squares in that row have indices 6, 7, 8 and 9. We conclude that when moving one square across a row, the index increases by 1.

The squares in column 3 have indices 2, 7, 12 and 17. Moving one square down a column increases the index by 5, the number of columns of the game board. We conclude that when moving one square down a column, the index increases by the game board's number of columns.

Let us now introduce variables that will allow us to express the formula explicitly.

Let a variable named str_index refer to the position of a square in the str representation of a game board with size NUM_ROW rows and NUM_COL columns. Let variables row and col refer to the position of the same square in the grid representation of a game board. From what we have seen, we can conclude that str_index depends on row, col, and NUM_COL. That is, the following formula will compute str_index:

(row - 1) * NUM_COL + (col - 1)

The minus 1's are needed for the arithmetic to work out. This is because we chose to index our rows and columns starting at 1 (as a thought exercise, what would the formula be if we chose to index the rows and columns starting at 0?).

What to do

We have included the type contracts and a specification of every function below; please read through them carefully. We will be evaluating your docstrings in addition to your code; include two examples in your docstrings. You will need to paraphrase the specifications of the functions to get an appropriate docstring description. Make sure you review the CSC108 Python Style Guidelines for the rules on how to write a docstring description.


Using a1_checker.py

    We are providing a checker module (a1_checker.py) that tests two things:
    1. Whether your code follows the Python style guidelines, and
    2. Whether your functions are named correctly, have the correct number of parameters, and return the correct types.
    To run the checker, open a1_checker.py and run it. Note: the checker file should be in the same directory as slide_functions.py and pyta, as provided in the starter code zip file. Sample messages from the checker being run are below. After running the checker, be sure to scroll up to the top of the shell and read all the messages!
The checker passes for both style and types
The checker did not pass
Reading Style/Convention errors
Example message from shell:
                    
W0613 (unused-argument)  Number of occurrences: 4.
  [Line 42] The parameter 'game_board' is unused. This may indicate you misspelled a parameter name in the function body. Otherwise, the parameter can be removed from the function without altering the program.
   40
   41
   42  def is_board_full(game_board: str) -> bool:
   43      """Return True if and only if the game_board is full.
   44
                    
                
Reading the example:

The error message tells you exactly which line in the slide_functions.py file you should inspect. In our example above, that is line 42. The error unused_argument in this example means that your function body is not using the parameter called game_board.

Reading Type Contract errors
Example message from shell:
                    
======================================================================
FAIL: test_between (__main__.SanityTest)
Check the type contract of function between.
----------------------------------------------------------------------
Traceback (most recent call last):
    File "x-wingide-python-shell://119623232/2", line 99, in test_between
    File "x-wingide-python-shell://119623232/2", line 145, in _check
AssertionError: False is not true : between should return a bool, but instead it returned None.
                    
                
Reading the example:

The error message tells you which function in slide_functions.py you should inspect. In our example above, that is the between function. The error AssertionError in this example could have many different kinds of messages. If we read this message closely, we see that we are not returning the correct type (other errors may have a different message). That is, our implementation of between is returning None, but it should be returning a value with type bool.


Marking Scheme

This section describes the aspects of your work that may be marked for A1.

Coding Style (20%)

Make sure that you follow Python style guidelines that we have introduced and the Python coding conventions that we have been using throughout the semester. Although we don't provide an exhaustive list of style rules, the checker tests for style are complete. So if your code passes the checker, then it will earn full marks for coding style with one exception: docstrings may be evaluated separately. Make sure you review the CSC108 Python Style Guidelines for the rules on how to write a docstring description.

For each occurrence of a PyTA error, one mark (out of 20) deduction will be applied. For example, if a C0301 (line-too-long) error occurs 3 times, then 3 marks will be deducted. All functions, including helper functions, should have complete docstrings including preconditions when you think they are necessary.

Correctness (80%)

Your functions should perform as specified. Correctness, as measured by our tests, will count for the largest single portion of your marks. Once your assignment is submitted, we will run additional tests not provided in the checker. Passing the checker does not mean that your code will earn full marks for correctness.

No Remark Requests

No remark requests will be accepted for code that didn't run and thus failed all the tests. This means a simple syntax error could result in a grade of 0 on the assignment. Before the deadline, you are responsible for running your code and the checker program to identify and resolve any errors that will prevent our tests from running.


What to Hand In

The very last thing you do before submitting should be to run the checker program one last time. Otherwise, you could make a small error in your final changes before submitting that causes your code to receive zero for correctness! Submit slide_functions.py to Assignment 1 (i.e., a1) on MarkUs. Remember that spelling of filenames, including case, counts: your file must be named exactly as above.