I'm thinking about an assignment based around the game 2048. This would be towards the end of a first programming course at the university level and assumes the students have been introduced to at least two dimensional arrays, loops, conditionals and methods. The purpose is to have the students practice these skills.

I would represent the board as a two dimensional array of int.

I would require a method (e.g. merge(int[] row)) which does the work on an individual row of the puzzle when the move is to slide left. The idea is to encapsulate the work of a slide in a single, testable method. This also emphasizes that a two dimensional array is an array of array, and that one can work on a single row at a time.

What are your opinions on the following ideas:

  1. Should a single move in the game be implemented as (move(direction)) or as individual methods (up() down() left() right())?

  2. One idea for completing the game involves rotating the board by multiples of 90 degrees. Then a move can be implemented as a three step process:

    1. Rotate board to one orientation
    2. Slide all rows left (merge(board[row]) for each row
    3. Rotate board back to original orientation
  3. Another idea is to show them that move right can be implemented by the process

    1. reverse a row
    2. slide it left (merge(board[row])
    3. reverse the row again
    4. For columns, copy the values from a column into a one dimensional temp array, then perform the merge(temp) and then copy back to the board. This may also require reverses, depending on the up/down selection.
  4. Require a method to produce a list of empty cells for use in picking the location of the next "insertion". This could be a list of int to demonstrate how a single index can map to a row/col using div and mod operations. Alternatively, it could be a list of pairs of row/column.

  5. The last wrinkle is to decide what to actually stored in each board location. The simple answer is just the value. However, an interesting variation might be to store the log2 of the value. By doing this, you get an index that can be used to select, for example, the background color of each square of the board. This changes the logic of merge(), so that combining adjacent cells ups the value by 1, rather than adding two values together.

Am I trying to put too many different concepts into this assignment? Your opinions and suggestions are appreciated.

Thanks

  • About #5: $0{\text{ }}log_{2} = \infty$, while $0=1{\text{ }}log_{2}$, so there ends up being some kind of special test for the empty square value or the stored value of zero. The conditionals around that problem makes it likely that choosing color based on cell value would be just as fast, and less error-prone. – Gypsy Spellweaver Oct 18 at 1:05
  • Yes your are correct, the empty cell being 0 is a special case, but as an index works very nicely. I would hope that the students would see that array[cellValue] is cleaner than a switch on the the cellValue with all the possible cases (2 4 8 15 ...). Perhaps you are correct that the students would be more comfortable with the switch approach. I had thought of this an a way to demonstrate allocating an arrray of constant values. – Fritz Sieker Oct 18 at 18:45

First of all, it seems like rather nice bones of a 2-dimensional array lab. It does not strike me as being too many things at all.

  1. The interface is cleaner with move(direction), but the internal logic is cleaner divided into methods. Why not the best of both words? Provide them with starter code that will utilize private methods:

    public void move(direction){
      switch(direction){
        case UP:
           slideUp();
        case DOWN:
           slideDown();
       case LEFT:
           slideLeft();
       case RIGHT:
           slideRight();
      }
    }
    
  2. That is another way, also valid.

  3. It seems excessively finicky to require implementation details down to this level.

  4. Once again, I would provide the interface, but not these sorts of implementation details. You can provide both ways as suggestions, but leave it up to them. They may also choose a third option that you have not mentioned.

  5. This fits very nicely if you are providing some sort of GUI starter code, which would justify an implementation detail like this one.

  • I think having 4 public move methods is cleaner than using a single one with a string parameter. At least use an Enum! – Solomon Ucko Oct 24 at 1:53
  • @SolomonUcko I don't agree about the cleanliness in the public facing methods, though I am referencing private methods, so we're not far off there. I absolutely agree about the Enum. Thanks for pointing that out! Fixed that, and I also noticed when I went in that my code wasn't formatting. Apparently we now need 8 spaces to start a code block?? In any case, that's fixed now, too. – Ben I. Oct 24 at 12:45
  • I'd say that using an Enum parameter is better than using multiple methods, which is better than using a String. Sorry for the tone of my previous comment, I was in a bit of a hurry. – Solomon Ucko Oct 24 at 13:26

This will be mostly just caveats and things to think about.

One problem you will have is that the original code and some other versions are all online as open source. It will be hard to keep your students "in the dark" so that they can grow themselves (mushroom principle of education, I guess).

Second, for a 4 by 4 square array, the standard array operations (scanning, collecting, ...) using loops are a bit inefficient. For example, your rotate method for an array can be done with about 20 assignments, just keeping track of subscripts. That might be a very valuable exercise in itself, of course. But loop overhead in such a small array starts to overwhelm the operations.

Third, I haven't thought in detail about this yet, but you might want to consider a program that uses more space than the minimum, keeping, for example, two versions of the matrix simultaneously, with one rotated 90 degrees from the other. This will achieve adjacency of both rows (in one copy) and columns (in the other).

An object based version might also be possible, so that the "cells" of the matrix can be simultaneously row and column adjacent. This might move some of the logic into those cell objects as well.

  • Thanks Buffy, The rotation solution is particularly inefficient. However, in this case the problem size is small and is a don't care because it need only be fast enough for the human player not to be kept waiting. . Premature optimization is rarely a good thing. - Fritz – Fritz Sieker Oct 17 at 19:01
  • I actually encourage students to look on line for help and/or solutions. I ask only that they cite the link where they found the code. And, in doing so, that they assume responsibility for the code. Tough luck if it doesn't work. I also tend to introduce some structure not found in the online solutions so that students must, at least, adapt what they find to the requirements. – Fritz Sieker Oct 17 at 21:01
  • Actually, I wasn't optimizing. I just think that brute force assignment is the simplest solution here. – Buffy Oct 17 at 22:44

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.