15110 Fall 2011 [Cortina/von Ronne]

Programming Assignment 11 - due Tuesday, December 6

This assignment cannot be dropped. However, you may hand this assignment in late by Friday, December 9 with a 2-point penalty.

For this assignment, you will create a Ruby source file connect4.rb containing a Ruby function(s) implementing each of the problems described below. If you find it useful for any of the problems in this assignment, you may define additional helper functions that your primary function for that problem calls to solve a sub-problem. Definitions for these helper functions should be included in the same Ruby source file. You should store connect4.rb in a folder named pa11.

Note: You are responsible for protecting your solutions to these problems from being seen by other students either physically (e.g., by looking over your shoulder) or electronically. (More information can be found in the instructions for Programming Assignment 2.)

Overview

In this assignment, you will complete a program to allow two players to play a game of Connect Four.

The vertical game board consists of 7 columns that can hold up to 6 discs. Two players (Red and Black) alternate dropping a colored disc (red and black, respectively) into one of the columns, with each disc falling as far as it can go. The first player to get four of the same color in a row (vertically, horizontally or diagonally) wins the game. If neither player can do this after all disc locations are filled, then the game ends in a tie.

In this game, the players are represented with the numbers 0 (Red) and 1 (Black). The game board is represented using a two-dimensional array with 6 rows and 7 columns. In each cell of this array, we store a 0 if there is a red disc in that position, a 1 if there is a black disc in that position, or nil if there is no disc in that position. (Initially, this array consists of only nil values.) Rows are numbered from 0 to 5 from top to bottom, and columns are numbered from 0 to 6 from left to right.

To display the board, we will use the Canvas to create a window with a width of 175 pixels and a height of 150 pixels. The window is broken up into 42 square regions of size 25 X 25, forming 6 rows and 7 columns. In each square region, we draw a circle centered in the square region with a radius of 10 pixels, filled in with either red, black, or white, depending on the value stored in the equivalent position of the two-dimensional array for the game board.

To draw a circle with center (x,y) and radius of 10 pixels, you can use the following method call:

Canvas::Circle.new(x, y, 10, :fill => :red, :outline => :black, :width => 2)

As an example, here is the two-dimensional array for a game board in this game along with the corresponding display in the Canvas.

[ [nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, nil, nil],
  [nil, nil, nil, nil, nil, 1, nil],
  [nil, 1, nil, nil, 1, 0, nil],
  [nil, 0, nil, nil, 0, 0, nil],
  [0, 1, 1, nil, 1, 0, nil] ]

Complete the problems below to complete this game in Ruby.

Problems

In this assignment, you should start by downloading a copy of the file connect4.rb into your pa11 folder. Read through the first two functions we give you to see how they work. You are responsible for understanding all of the code we've given you. The main function that you run to play the game is called play().

You will see that the file we give you has all of the functions represented as "stubs". This means that each function is written so that it returns something so that the entire program will run without crashing if you haven't completed all of the functions. As you complete each function, replace the code we give you for the function in question with your own code, but leave the subsequent stubs in place until you have tested your current function completely.

  1. [1 point] Complete the function new_board() (in the file connect4.rb). This function should create and return a two-dimensional array with 6 rows and 7 columns with all array values set to nil.

  2. [2 points] Complete the function display_board(board) (in the file connect4.rb). This function has one parameter which is the two-dimensional array board that holds the position of each disc in the game as described above.

    General Algorithm:

    1. For each row and column, do the following:
      1. Compute the x and y location of the center of the corresponding square region of the Canvas for that row and column.
      2. If board[row][column] is equal to 0, then draw a circle with a center of (x,y), a radius of 10, and a fill color of red and a black outline of width 2 pixels.
      3. If board[row][column] is equal to 1, then draw a circle with a center of (x,y), a radius of 10, and a fill color of black and a black outline of width 2 pixels.
      4. If board[row][column] is equal to nil, then draw a circle with a center of (x,y), a radius of 10, and a fill color of white and a black outline of width 2 pixels.

    Testing your work: Once you finish this function, when you run the program by calling the function play() within irb, you should see an empty board with 42 white circles.

  3. [2 points] Complete the function add_piece(board, player, column) (in the file connect4.rb). This function has three parameters which are the two-dimensional array board that holds the position of each disc in the game, the number of the current player who is dropping a disc (0 or 1), and the column where the disc is being dropped (an integer between 0 and 6, inclusive). This function should update the board to the state it would be in after the disc has been dropped down the given column (if possible).

    The basic idea is to scan the column from the bottom to the top for the first empty (nil) cell. You will then "drop" the disc into that cell by storing the player's number in that location. If the entire column is full, then you will return nil and not change the game board.

    General algorithm:

    1. For each row in the given column from 5 down to 0, do the following:
      1. If board[row][column] is nil, then do the following:
        1. Store the player's number in board[row][column].
        2. Return the row where the disc stopped in that column.
    2. Return nil (since the entire column is full if you get to this step).

    Testing your work: Once you finish this function, you should be able to play the game, dropping discs (alternating red and black), but the game will not test to see if anyone wins.

  4. [1 point] Complete the function check_win_vertical(board, row, column) (in the file connect4.rb). This function has three parameters which are the two-dimensional array board that holds the position of each disc in the game, and the row and column where the most recent disc was placed. This function should return true if the player who placed this disc has won vertically, or false otherwise.

    To test if the player wins vertically, you only have to count the number of discs of the same player below the newly placed disc until you either find a different player, or the bottom of the game board. (None of the locations below the newly placed disc can be empty.)

    General algorithm:

    1. Set player equal to board[row][column].
    2. Set count equal to 1. (That is, count the newly dropped disc.)
    3. Set r equal to \(\textit{row} + 1\). (That is, start with the next row.)
    4. While r is less than or equal to 5 and board[r][column] is equal to player, do the following:
      1. Add 1 to count.
      2. Add 1 to r.
    5. If count is greater than or equal to 4, then return true. Otherwise, return false.

    Testing your work: Once you complete this function, you should be able to play and test for a player winning with 4-in-a-row vertically.

  5. [2 points] Complete the function check_win_horizontal(board, row, column) (in the file connect4.rb). This function has three parameters which are the two-dimensional array board that holds the position of each disc in the game, and the row and column where the most recent disc was placed. This function should return true if the player who placed this disc has won horizontally, or false otherwise.

    This function works similarly to the previous function, except that you have to count the number of adjacent discs of the same color both left and right to see if the complete total is at least 4.

    General algorithm:

    1. Set player equal to board[row][column].
    2. Set count equal to 1. (That is, count the newly dropped disc.)
    3. Set c equal to \(\textit{column} + 1\). (That is, start with the next column to the right.)
    4. While c is less than or equal to 6 and board[row][c] is equal to player, do the following:
      1. Add 1 to count.
      2. Add 1 to c.
    5. Set c equal to \(\textit{column} - 1\). (Now, start with the first column to the left of the newly placed disc.)
    6. While c is greater than or equal to 0 and board[row][c] is equal to player, do the following:
      1. Add 1 to count.
      2. Subtract 1 from c.
    7. If count is greater than or equal to 4, then return true. Otherwise, return false.

    Testing your work: Once you complete this function, you should be able to play and test for a player winning with 4-in-a-row vertically or horizontally.

  6. [2 points] Complete the functions check_win_diagonal1(board, row, column) and check_win_diagonal2(board, row, column) (in the file connect4.rb). These functions have three parameters which are the two-dimensional array board that holds the position of each disc in the game, and the row and column where the most recent disc was placed. These functions should return true if the player who placed this disc has won diagonally, or false otherwise. There are two functions since there are two diagonal lines to test.

    Hint: These functions are similar to the ones you wrote previously. Use those functions are a guide for these functions.

    Testing your work: Once you complete this function, you should be able to play a complete game of Connect Four.

Submission

You should now have a pa11 directory that contains the required file, connect4.rb containing the corresponding functions. Zip up your directory and upload it using the handin system. (The handin system will accept submissions beginning on Friday until the deadline Tuesday night. Submissions after the deadline but before the late deadline will be marked on our system as "late".)