Special Effects
Heroes
That is, the symphony by Philip Glass (from Bowie and Eno) that I am currently listening to.
Where was I? Ah yes, the one failing test - testHittingThumbMakesHomerSayDoh. I'm going to load success and failure effects into the test ability "Hit Thumb" and check to make sure that the canonical Abilities object can return them.
Testing
This is much better - the tests are leading me to small steps. I take a small failing step (such as finding the ability), get it to pass, and then improve my test until it fails again (ok, I've found the ability, but does it have any success effects?).
I'm building a safety net as I go.
Waste of time?
It's curious, and I think I'll be better able to express this later, but I'm alternating between two thoughts. I suppose I have a devil on one shoulder:
"Running tests at this point is a waste of time. I haven't changed any of the tests yet because I'm not done coding. I run the tests anyways, and feel disappointed to see that the same tests are failing (or passing) as last time. I feel like I haven't made any pogress."
But, if I ignore the little devil and run the tests anyways, and I see something that failed, an angel appears and hark:
"Ye cats! I didn't realize I introduced an error there. I just made a few simple changes - oh, I've lost the AbilityID along the way, so now Homer can read books (e.g. that test is failing). Let's see, ctrl-z a few dozen times, ah ha! That's it. Easily fixed. Done."
Functional tests
Ok, I think I'm ready to start on a functional test. I want Lisa to attempt to read a book, and I want it to give her a new ability as a result.
Actually, I should finish up the failure effects, shouldn't I? More tests...
Effects
Functionality Testing
I seem to be stuck, functionality speaking. I have a few things to clean up, but no major smells. This means I need to start making some messes. But I'm not sure how to proceed. Which means I need to create a new test.
Test
Now that I have abilities that characters can use (with success or failure), I need effects. One basic effect is to increase a character's statistic. Something like TestReadingABookMakesLisaSmarter.
Responsibility
Do I want objCharacter.UseAbility to implement the effects of that ablity? Or do I just want it to indicate whether it was successful (and then have another call to implement the effects)? For now, I'll have the method return a set of effects, and then call effect.Occur() or something.
This vagueness is what got me stuck, and writing this test is helping me to proceed.
Ability
Ok, so the ability object should have two collections - successEffects and failureEffects. I've written a bunch of code without and tests yet, so I'll implement this in steps:
1. Successful, no effects
3. Failure, no effects
2. Successful, 1 effect
4. Failure, 1 effect.
No effect
So, My first test should be testReadingABookDoesNOTMakeLisaSmarter. Ok, I wrote the test, but also implemented it at the same time. I'm not letting the tests drive yet (as I know they are going to fail, so I skip that step). I've got to improve my habits here.
New test - testReadingABookDoesNOTMakeHomerSmarter. Well, this one passes vacuously. Not so interesting.
Another effect
Now, to implement my first effect. It should be as simple as possible - let's say returning a message to be displayed. Most effects will do this.
I need to write a test first, and I need another effect. Something like testHittingThumbMakesHomerSayDoh. Writing test now.
Failure Unexpected
Well, okay, I expected the test to fail, but I wasn't expecting a runtime error. Checking... Ah, I wrote the test wrong. Trying again. Ok, the test is failing for the expected reason now.
More Responsibility
Who should set the success or failure effect? Should it be part of the canonical ablity? I think that's a good place to start. The canonical ability object (Ability Factory? ) should be able to produce ability objects (that, perhaps can be modified or tweaked). That seems like a good intermediate test. I could have the CanonicalAbility object return a test ability and make sure that it's debugPrint matches an expected value. This would also be a good place for AssertStringsMatch function. Ok, one thing at a time.
Little Detour
Two additional sub-tests to prove that my AssertStringsMatch function works (it does). Yet again, tests written after code (I've got to stop doing that). Next, test the CanonicalAbility object returning function.
DebugPrint everywhere
To do this, I've added debugPrint functions to abilities and effects. Along the way, I'm making some notes indicating where I need to shuffle things around. I still think that not using a database is making things more difficult at this point, so I may need to add that next.
Cleaning up
At this point, I have one non-passing test. It will have to wait, as my time is up for now.
Three hats
Refactoring, Functionality, and UI?
Last time, I distinguished between the hats that a developer wears (one for adding functionality, and another for refactoring). There's a third, in this case, which is UI work. My first goal for today is in this third case - cleaning up the UI so it's more clear how many, and which tests are passing.
In addition to listing all of the test results, I'll add a summary section with little green/red boxes, each linking to that test result, with mouse over text of the name of the test (and the result). To accomplish this, I will be doing a bit of refactoring, putting all of the results in an array to be displayed later.
UI - Done
Ok, this enhancement is complete, although I did see some good areas for future refactoring (such as many copy/pasted calls to RunGenericTest function). But all my tests are passing now (and the UI work will really shine once I get more than 25 additional tests), so it's time to put on my functionality hat.
Functionality
I have distressingly little on my Functionality todo list. I'm worried that I don't know where this program is headed. Well, let's forge ahead - I want to make a canonical list of Abilities, just as there is a list of statistics. I should be able to copy, paste, and go from there.
Beatles Revolver has finished playing - started Clapton's From the Cradle.
Refactoring again
Ah I see the problem, I mix calling/storing Statistic by name and by ID. I'd better clear up the confusion before copying statistics to Abilities, or I'll have to fix it in multiple places.
Ok, I've added StatisticID to the Statistic class (with the idea of replacing the Name member, to force all comparisons to be done by ID). The Name should only be retrieved for display purposes, and should only be known by the CanonicalStatistics object. Run tests - they pass.
Now to remove the Name member from the Statistic class. Two missed references, fixed, run tests- they pass.
Copying
Okay, now I'm relatively happy with the Statistics Class. Time to copy & rename. Done. Added SuccessThreshold. Now, I need to remove the references to Ability.Name (for the same reasons I removed references to Statistic.Name). I'll also need to add a CanonicalAbilities class to the global arrParameters.
Make changes, Run tests - I suppose it would help if I instantiated the object when the Character class is created (:
Interesting - that turned out to be easier than expected, as no tests required any changes.
Relationship between abilities and Statistics
It's clear to me that an instance of an ability (e.g. this character's success threshold for reading books) may differ from one character to the next. Especially as the characters gain levels. It's not clear to me, however, if the statistic that an ability relies on (such as Reading Books relies on Intelligence and Lift Heavy Object relies on Strength) can vary from character to character. Could a wizard lift heavy objects based on Intelligence, or a fighter read books by strength? While it's tempting (and slightly amusing) to consider these fringe cases, I believe it would severely impact the intuitiveness of the game.
Thus, the canonical list of abilities should include more than just the name of the ability, but also the ID of the relevant statistic. This is my next goal.
So, should CCanonicalAbilities have a local copy of CCanonicalStatistics? I think so. Does this mean that the CCharacter class should use the CCanonicalAbilities's copy of CCanonicalStatistics instead of its own? That, I'm not so sure of. For now, no. The CCharacter will include two copies of the object, one for itself, and one its copy of CCanonicalAbilities.
Patience, please...
I'm fighting with arrays. I've just a moment to update you on my progress, as Interdev has decided to unexpected quit. Phew, finally found the error (programmer error, of course, trying to use a zero-based index when a 1-based index is more appropriate).
Ok, now I can make CanonicalAbilities include both name and relevant statistic. But I've run out of music. I'll be right back.
Dinner in 5 minutes - let's see if I can whip this together. Whoops! All tests failed! Oh I need to create the CCanonicalStatistics before trying to add Abilities. Ok, all tests pass now.
Next time I need to add some functional tests. But for now, it's time for lamb chops. See you next time.