Unknown's avatar

About Bruce Ryan

https://about.me/bruce.ryan

Completely puzzling

 

When Mood Music
2012-04-21 04:42:00 frustrated Heroes – Roni Size/Reprazent

Having achieved non-trivial, working JUnit tests, I thought I could spend some time improving the UI/UX of my game. Several frustrating hours later, I’m not sure this was a good idea.

Poor UI/UX
The ‘working’ version first asks the user if s/he wants to restart a saved game, and warns him/her that if there isn’t one, a brand-new game will be started. This is presented as a bog-standard yes-no dialog.

On choosing ‘No’, i.e. the user is presented with the first representation of the swamp in a very plain dialog. The user’s only choice is to click ‘OK’ (or force-quit).

Clicking ‘OK’ forces the program to redraw the swamp after the ogre has moved and potentially been joined by an enemy. The user is offered the choice of seeing the creatures move again (by clicking ‘Yes’) or saving the game (by clicking ‘No’). Again. it’s a standard yes-no dialog.

Clicking ‘Yes’ here does make the creatures move as they should. Clicking ‘No’ refreshes the view of the game-state that will be saved. Here’ the user’s only choice is again to click ‘OK’. Doing so terminates the program quietly, cleanly and boringly!

Should the user initially have chosen to restore a saved game, the saved state is displayed, again in a plain dialog with just an ‘OK’ button. Clicking ‘OK’ brings up the yes-no-dialog where the user can carry on moving the creatures.

Road to nowhere
This seemed poor – I wanted to offer the user some kind of menu. It’s easy to create a dialog with text offering numbered choices and a space for the user to type his or her choice. It’s fairly easy to trap invalid numeric input and force the user to re-enter a choice. Alternatively, if the user enters a non-numeric choice, it’s possible to use exceptions to trap this. However, trying to account for users entering random strings of differently incorrect choices led me into a merry hell of nested do-while loops.

So nearly!
Then inspiration struck – while looking for ways of replacing the standard icons in Java dialog boxes, I’d seen a way to ‘doctor’ standard ‘yes-no-cancel’ dialogs with my own text. So I could do away with needing to trap poor user input by offering buttons for ‘I don’t want to play at all’, ‘play a brand-new game’ and ‘restore saved game’. Setting this up took a fairly short time, and so I set about testing whether my better-UI/UX game worked.

The brand-new game worked as it should. Saving and recalling seemed to work, so I set about doing more rigorous testing: starting a game, saving it, recalling it, saving it again, terminating the app, restarting the app, recalling the game…

Hmm – not quite right. In any recalled game, regardless of whether or not the app was terminated and restarted, the ogre intermittently ignores 1 or 2 immediate enemies. Sometimes 2 immediate enemies will kill him, as should happen. Sometimes he kills one of them. If there are three immediate enemies, he may kill one of them or two of them may kill him while the third is ignored. If there are four, the ogre is definitely killed.

Bah encore
So I thought I could go back to the early, ‘working’ version of the game which didn’t have this fault. WRONG! The fault is there – I’d just not tested thoroughly enough at the time! It appears my choice is not ‘working but poor UI’ or ‘intermittently faulty but better UI’. Instead, it’s ‘intermittently faulty but poor UI’ or ‘intermittently faulty but better UI’. So the choice is easier – big hairy wow.

I have to start writing up tomorrow to have time to create a decent report for the end of Tuesday. We also have to demonstrate our programs working on Tuesday afternoon. The lecturer has to see 20 programs and interview their creators in 2 hours. So there’s a chance he might not see the fault. (I’m not relying on this!) I’m wondering whether I should point out the fault and/or mention it in my report.

Weird

When Mood Music
2012-04-20 16:58:00 boggled Shuttin’ The Ole Dirt Down – Rev Hammer

Even though 1 + 1 ≠ 2, the following test passes. (swampSize is set elsewhere to be 4)

public void testDrawSwampMap() {
//Set up
Swamp s1 = new Swamp(swampSize);
String actualMap;
String expectedMap;

//Get value. The swampMap will vary every time the Swamp is created because
//an Ogre is added IN A RANDOM PLACE each time a Swamp is created.
//So remove the ogre, then draw the map.
s1.removeCreature(s1.ogreXcoord(), s1.ogreYcoord(), “OGRE”);
actualMap = s1.drawSwampMap();

expectedMap =
“(0,0): t(1,0): t(2,0): t(3,0): tnn” +
“(0,1): t(1,1): t(2,1): t(3,1): tnn” +
“(0,2): t(1,2): t(2,2): t(3,2): tnn” +
“(0,3): t(1,3): t(2,3): t(3,3): tnn”;

//Test
assertEquals(expectedMap, actualMap);
}

So “=” ≠ “equals”!

1 + 1 ≠ 2

When Mood Music
2012-04-20 16:05:00 annoyed, puzzled In The City – Razorlight

Following on from here, take a look at this code:

package _06Swampless_JUnitTests;

public class testString {

public static void main(String[] args) {
String piece1 = “donkeyOGREsnake”;
String piece2 = “donkeysnake”;
String piece3 = piece1.replace(“OGRE”, “”);
if (piece3 == piece2) {
System.out.println(“yeehah”);
}
System.out.println(“piece1: ” + piece1);
System.out.println(“piece2: ” + piece2);
System.out.println(“piece3: ” + piece3);
} //end main

} //end class

This sets up pieces of text which are identical except the first one has some extra characters, namely “OGRE” . A third piece is created as the first one minus “OGRE”. So the third piece should be the same as the second. The if block will print ‘yeehah’ if this is true and ‘boo hiss’ if they’re not.

The actual output is:
boohiss
piece1: donkeyOGREsnake
piece2: donkeysnake
piece3: donkeysnake

So two identical-looking pieces of text are in fact not identical. Bah!

Need drugs!

 

When Mood Music
2012-04-20 13:31:00 shattered Chinese Rock – The Ramones

I’ve just drunk my first coffee for over 2 years. 90 minutes of intensely watching 11-year-olds cycling – and trying to demonstrate and lead them towards good cycling habits – is utterly draining. And this was in a school play-ground. I’m not really looking forward to taking them out on the road.

Now I’m back to writing JUnit tests…

Testing my head

 

When Mood Music
2012-04-19 15:24:00 puzzled Fiji – K-series

Original thoughts
On the last of the JUnit tests, namely testing writeSwampToDisk(). This is called within the game if the user decides he or she is bored with watching creatures move around and try to kill each other.

The way I’d want to test it is to invoke a game and record the current state, e.g. the current value of Swamp() or drawSwampmap(). Then I’d save the game (which automatically closes it. Then I’d recall the game and compare the recalled state with the previously-noted saved state. If the two are the same, the read and write have succeeded.

The write method is within Swamp(), so that it can be called from Swamp()‘s basic routine. However, the read method is within a separate interface.

(The interface presents the user with a choice of new game or saved game.

  • If the user wants a new game, an new instance of Swamp() is kicked off.
  • If the user wants to recall a game from disk and a saved game exists, the interface’s read method is invoked.
  • [If there isn’t a saved game, the user gets a blocking for not reading the warning previously presented and a new game is started anyway.])

The read method can’t be in Swamp() because this would involve Swamp() trying to read itself. I’d like move the write method fromSwamp() to the interface (partly so I don’t have to write this test but mostly because that would seem neater) but the interface abdicates control to Swamp()‘s basic routine and then exits.

I guess the saved thing could be Swamp()‘s list of creatures, rather than Swamp() itself…… yes, that seems to work.

Some experimentation later
The test didn’t work when I compared lists of creatures. So to make it obvious, I’ve changed to comparing maps – purely textual output. While the two bits of text look identical, the test fails – implying they’re not!

Bah!

Destruction testing

 

When Mood Music
2012-04-18 14:37:00 puzzled Take a Breath (Live) – David Gilmour

Doggedly writing JUnit tests for my Swamp class.

Is there any point in writing a test for a method that randomly returns either ‘true’ or ‘false’? The only test I can think of is that the return value should b done of these values.

Also puzzled how I write a test for my method that draws a map of the Swamp and its Creatures. This will vary each time the game is run because an Ogre is placed in a random position each time the game is run, before the map is ever drawn.

bug-hunt!

When Mood Music
2012-04-17 22:25:00 frustrated The Robots – Kraftwerk

This morning I traced the ‘not working when recalled from saved state’ to the code that that calculates where the ogre is. (This is used to decide whether there should be a battle by counting the number of enemies at the ogre’s location. If there are none, no battle ensues. If there is only 1 enemy, there is a battle which the ogre wins and the enemy is removed. If there is more than 1 enemy, the enemies win, the ogre is removed and the game ends.)

During a brand-new game, the ogre-location method works fine. In a recalled-from-disk game, it doesn’t. The code in question (and rather questionable code) is in two pieces, one for the x-coordinate and one for the y-coordinate. Here’s the x-coordinate code:

public int ogreXcoord() {
    //Declare variable – it needs to be initialised, so use a silly number
    int ogreXCoordinate = 2000;

    //Code
    for (Creature tempCreature : creaturesInSwamp) {
        if (tempCreature.getType() == OGRE ) {
ogreXCoordinate = tempCreature.getxCoord();
} //end if
} //end for-each

    return ogreXCoordinate;
} //end ogreXcoord

The green bits are just comments to help humans read the code. The pink bits are reserved words, i.e words that have special meanings to Java and the blue bit is an example of an instance variable, i.e. a variable that occurs in every instance of the Swamp class.

The code translates as.
This method, named ‘ogreXcoord’ may be called from outside the Swamp class. It will return an integer.
Create an integer variable (which only exists within this method) called ‘ogreXCoordinate’ and sets its value to 2000.
One-by-one, put each Creature in this Swamp’s list of Creatures into a variable called tempCreature.
Then, for each version of tempCreature, if it is an ogre, then set
‘ogreXCoordinate’ to the value of the tempCreature’s x-coordinate.
When you have run through all the creatures and thus definitely have the required xCoordinate, pass this back to the code that called this method.

Why so complex? Only the ogre knows where it is. Each time it moves, it calculates new coordinates for itself and moves to these coordinates. (All Creatures do this, and don’t tell anyone where they are until asked.) So the program doesn’t know where the ogre is until it can ask him. And it can’t ask him where he is until it has found him – and it can only find him by looking through all the creatures in the Swamp and asking them in turn ‘Are you an ogre?’ (I guess this could be shortened by stopping asking once the ogre has been found. And he’s likely to be the first creature in the ‘CreaturesInSwamp’ list because he’s the first creature to be created once the swamp has been created. However, I don’t know that this is guaranteed.)

Anyway, I can show this works when the game is working – the ogreXCoordinate is always right. But in a recalled-from-disk game, this fails – the ogreXCoordinate is always returned as 2000. So the for-each loop is either not working or being ignored.

Bah!!!!!!

Bah humbug

When Mood Music
2012-04-16 17:43:00 confused Save The Day – Diss-cuss

Wierdness: so long as I start a new game each time, the game plays out as it should. However, if I recall a saved game from disk, the creatures appear not to battle and are effectively immortal.

This may or may not be connected with the moveAllCreatures() method in Swamp.class appearing to call moveCreature() in Creature.class at least twice per Creature.

If it were always twice, I’d be a little less confused.

Statistics

When Mood Music
2012-04-16 14:25:00 ground state, i.e. prevaricating Robber Rock – Yabby U

My lecturer tells me ‘All debugging code should be removed. The only exception is when a program can be run in debug mode as per SD1 coursework.’

I’ve saved a copy with the extra methods and commented-out pieces to track events elsewhere, then looked at what’s left after removing these undesirables.

 

Version lines ‘words’ characters characters that are spaces
complete code
340
1834
15,021
2654
comment lines removed,
i.e. just code lines
217
986
8345
1260
%
64
54
55
47
Hence comment lines
123
848
6676
1394
%
36
46
45
43
inline comments (e.g. //end method) removed,
i.e. pure code
217
873
7517
1148
%
64
45
50
43

So I’ve written 217 lines of code for this class of which around 50% is substantive – the rest is spaces and comments to allow humans to read it.