- 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
fromgo 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
(letter ell) as variable names. This left judges reading code that looked likel
, which is a confusing thing to see in a program.1 = x + 1
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
, and your if statements will read like this:return "too_deep";
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 printsZop
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
.