Rowan University
Rowan University Programming Contest
hosted by the Computer Science Department
our 35th annual contest is Friday, 26th April 2024
Lessons From the 2017 RUPC
Structural Problems
Repeated Blocks of Code

Many entries had four separate methods for when the robot moved north, south, east, or west. The code in these sections was nearly identical, except for a few +1 and -1 operations.

This became a major issue for teams which wrote the first one, copied and pasted it several times, and then modified the copies. A few teams didn't get all the changes right, and were left with both south() and north() adding one to the Y coordinate, and no way of subtracting one. Other teams had a bug in the initial function, which bug they copied several times. On discovering the bug when testing against the sample data, they fixed it in one or two places but not all four.

Handling this sort of problem is best done by having one function, move() or similar, which is given the new location as an argument. That lets you separate out find the new spot from go to the new spot. And if move() has a bug, you only have to fix it once.

Encapsulation Problems

Some entries declared a class with a name such as grid, which is fine, but then crammed all the data into the grid, including specs about the robot. Some also had robot classes, with more or less information in them. One had the robot's strength and agility stored in the grid, and the robot stored only its location. Another reversed those, the robot storing its strength and agility and the grid storing the robot's location.

The robot class should store facts about the robot, and the grid class should store facts about the map. This separation makes code less likely to be buggy, and makes the bugs easier to find and fix when they occur.

Methodology Problems
Lack of Flexibility

Some programs just hardcoded values from the sample data into the code, setting the width of the grid to 7 and the height to 4. Testing such programs on other data is essentially impossible.

Better, but still troublesome, was hardcoding in a filename to read data from. That name can be changed and the program rerun.

If your program is going to read from a file by name, you should ask for the filename as input, which simplifies testing the code.

The problem assumes that all input will come from the keyboard; during judging, the easiest way to test is to just copy the input data from one window and paste it into another window where the program is running. This is likely also the quickest way for competitors to test their code.

Off-by-One Errors

The problem spec says that rows and columns are numbered starting at zero, which means that if the width is 5, the valid column numbers are from the set {0, 1, 2, 3, 4}. Several entries check position with code such as:

    if (  startx > width || startx < 0 || starty < 0 || starty > height )
            

which means that a position of (5, 5) would be accepted on a 5x5 board, when that position is actually off the grid.

Readability Problems
Mystifying Variable and Function Names

One program used both o (letter oh) and l (letter ell) as variable names. This left judges reading code that looked like 1 = x + 1, which is a confusing thing to see in a program.

Many entries used single-character names for both objects and methods, such as w and a to store width and agility, or E() for a method meaning go East.

Others had names such as canPositive(), which sounds like a true/false test and so should return a boolean, but their code included statements such as

    if ( canPositive() == 1 )
            

which made no sense at all. Why 1?

One entry had a variable named sk, and we never did figure out what that was supposed to stand for. Another had a method named karl(), which we also never figured out.

Magic Numbers

As in the canPositive() example above, many programs had numbers in them whose origin was mystifying. Judges were looking at lines such as these:

    if(a > 2 || a < 0)

    if ( state == 4 )

    if (t == -1)
          

Much better is to use named constants, or enumerated types, or even just use strings. Any of those will let you write code that uses words to mean what you're interested in. Your function Can_Move() can say return "too_deep";, and your if statements will read like this:

    if Robot.Can_Move(New_Spot) == "too_deep":
            
which makes it perfectly clear what you're testing for.

Incorrect Indentation

Indentation should reflect program structure. In Python, this is required, but in many languages whitespace is ignored. As a result, programs can be indented in ways that are misleading. For example:

    if ( x == 9 )
        if ( y == 7 )
            printf("Zipzap\n");
    else
        printf("Zop\n");
          

This may look like it will print Zop if x is 8, but it won't. It will print nothing if x is 8. The else goes with the closest if, so this only prints Zop if x is 9 and y is not 7. The incorrect indentation is misleading. This makes programs difficult to read, which makes them hard to debug.

Poor Use of Whitespace

Some programs had a wall of text quality to them, which slows down comprehension. A line like this:

    if(cordy<0||cordy>length||cordx<0||cordx>width){
          

should be spaced out:

    if (cordy < 0 || cordy > length || cordx < 0 || cordx > width) {
          

And blank lines can be used to separate code into paragraphs, where the chunk that reads input is separated from the chunk that does processing, and that from the chunk that generates output.

Names Too Similar

One program had both temp and tempp in the same method.

Other Mistakes
Mixing Up Coordinates

One disastrous mistake made by several teams was getting the X and Y coordinates backwards.

One program looked very good, and might have finished in the top five, but for this line of code:

    row, column = self.position
          

By putting the robot in the wrong location, there was no way to recover.

Input Problems

One program in Java did this:

    instructionsChars = (reader.nextInt()*2)-1;
    String[] instructionArray = new String[(instructionsChars+1)/2];

    instructions = reader.nextLine();
          

However, nextInt() reads an integer and then leaves the newline on the input. So when they call nextLine(), intending to read in the line with instructions, they get an empty line with nothing on it, because they read only the newline at the end of the line with the number of instructions.

This input:

    5
    hello
          

is "5\nhello\n". nextInt() gets the 5, and leaves the input pointer at the first "\n". nextLine() gets only up to that "\n", leaving the input pointer at the "h". So with that kind of input, nextInt() returns 5 and nextLine() returns an empty string. Another nextLine() would return "hello".

Postincrement/Postdecrement Operator

One mistake that showed up in a Java program reflected a fundamental misunderstanding of the postincrement operators. In Java, one can write x++; to add one to x. But that doesn't happen until after whatever other operation is present. This code, for example:

    x = 5;
    y = x++;
            

leaves y equal to 5, and x equal to six.

In the affected program, what the students probably meant to write was if the square to the left is okay, lower x by one. That would look like this:

    if ( inBounds(x-1) )
        x--;
          

But what they wrote was this:

    if(inBounds(x--))
        x--;
          

What that code does (remember the postdecrement operator doesn't take effect until after the function call) is if the current square is okay, lower x by two.