Configuration Shuffling
August 12, 2008 10:32 PM
In order to make the headless CocoaBugs actually useful for most models, we want the ability to set a range of random values for a simulation to assume. Then, researchers can build a model, decide on a range of values to investigate, and send it out to their grid to run many slightly different simulations at once and compare the results.
Building the GUI for this is relatively straightforward. We'll add a shuffle button next to the value slider, which will hide and show a few more controls to set the delta for a given value.
(For now I swiped the shuffle icon from an Apple code sample, I'll be making a new one one of these days.) The shuffle button is type "recessed", so it only shows a border when you hover over it or have selected it (like the Safari bookmark bar), and behaves as a push on/push off button. We also add an IBOutlet for each of the new controls (shuffleDial, shuffleLabel, and plusMinusLabel) as well, so that we can manipulate them programmatically.
Start with the IntegerOptionViewController. We'll add two new @properties to this class, shuffling and delta, and bind them to the values of, respectively the value of the shuffle button, and the values of the delta text field and spinner. In Interface Builder, we can bind the "Hidden" property of these fields to !shuffling, so they are only visible when the button is selected like so:

Now, clicking the shuffle button will hide and show the shuffle controls, and moving the twirly delta selector will change the value of the "delta" instance variable and the associated label.
(The UI for this is pretty bad--spinners suck, there's no way to type in values, and the whole thing is very nonobvious--but for now it'll do.)
Now, in the value method of IntegerOptionViewController, we used to do this:
- (NSNumber *)value;
{
return [NSNumber numberWithInt:[slider intValue]];
}
You might think at first glance that we'd replace this with a random number generator, but keep in mind that we want to retain the indeterminacy of the specific value at this point, and save the generation of a value within the range to when a new simulation is actually created. That way, we can send one configuration file to many different clients and see different behavior on each.
So, rather than returning a NSNumber here, we're going to return a dictionary--but only if the value is set to shuffle.
- (NSNumber *)value;
{
if (shuffling)
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:[slider intValue]], @"principalValue",
[NSNumber numberWithInt:delta], @"delta", nil];
else
return [NSNumber numberWithInt:[slider intValue]];
}
Then in our ALifeWindowController, where currently we pass the configuration dictionary straight through to the ALifeController, we'll instead loop through the configuration dictionary and, if we find a dictionary, we'll generate a new random number with the specified principal value and delta.
- (id)initWithSimulationClass:(Class <ALifeController>)modelClass configuration:(NSDictionary *)configuration;
{
...
// check for shuffled parameters
for (NSDictionary *configurationOptions in opts) {
NSString *type = [configurationOptions objectForKey:@"type"];
NSString *name = [configurationOptions objectForKey:@"name"];
id entry = [theConfiguration objectForKey:name];
if ([entry respondsToSelector:@selector(objectForKey:)]
&& [entry objectForKey:@"shuffle"]) {
[theConfiguration setValue:[ALifeShuffler shuffleType:type withOptions:entry] forKey:name];
}
}
lifeController = [[modelClass alloc] initWithConfiguration:theConfiguration];
...
}
Note the new class up my sleeve: ALifeShuffler. This is a new class created to manage shuffling various types of values. Originally, I had the ALifeWindowController class managing shuffling, but it became obvious that this was a pretty poor and inextensible design. (Conceivably, for example, we could want a plugin to be able to define a new configuration type and shuffling method.)
Here's how the ALifeShuffler class works. It has one class method, illustrated above: shuffleType:withOptions:, which takes a string type (as used in the configuration dictionary) and an options dictionary, as maintained by the individual option view controllers.
This method is driven by a singleton shuffleTable object, which maps string types to first-class objects conforming to the ALifeValueShuffler protocol.
This is, in my experience, the standard Objective-C idiom for maintaining such singletons:
static NSDictionary *gShuffleTable = NULL;
...
+ (NSDictionary *)shuffleTable;
{
if (!gShuffleTable) {
gShuffleTable = [[NSMutableDictionary dictionary] retain];
[gShuffleTable setValue:[[[IntegerShuffler alloc] init] autorelease]
forKey:@"Integer"];
[gShuffleTable setValue:[[[FloatShuffler alloc] init] autorelease]
forKey:@"Float"];
}
return gShuffleTable;
}
Note that the gShuffleTable is never released, since classes are never deallocated; traditionally, this is a memory leak, but since we only ever create the thing once, it's not too bad. If more experienced Objective-C coders want to show me the error of my ways, I'm very amenable.
Since the value shuffler classes are so small, they're defined inline in the ALifeShuffler.m file:
@interface IntegerShuffler : NSObject <ALifeValueShuffler> @end
@implementation IntegerShuffler
- (id)shuffle:(NSDictionary *)options;
{
int p = [[options valueForKey:@"principalValue"] intValue];
int d = [[options valueForKey:@"delta"] intValue];
int v = p + (random() % (2 * d + 1)) - d;
return [NSNumber numberWithInt:v];
}
@end
Theoretically, a plugin class could define their own type and shuffler class for this, and add it to the [ALifeShuffler shuffleTable]; however, there is currently no functionality for defining new types of option view controllers (which would probably be done in a similar way), which would be a prerequisite of such work.
Next time: a look at refactoring out some of this functionality from ALifeWindowController into a more MVC-acceptible class.

Leave a comment