0

I am a novice programmer, and I've just started reading about decision tables. I have read Chapter 18 in Code Complete and it was very enlightening. I looked around the web to try to find any kind of example of decision tables in Objective-C and I was unable to find any boilerplate or real world examples of how to implement this.

I am programming a game in Objective-C in my spare time, and I have been dealing with increasing complexity for the rules of the game. There are a handful of somewhat deeply nested if-else statements, as well as a few switch statements that already have 10 or more cases to deal with. I think it would be easier to work with decision tables, but I have no idea how to implement this in Objective-C for something non-trivial like the logic of a game.

For example, I need different methods to execute for different combinations of states. How would I implement a decision table in Objective-C that could take different combinations of states as keys, and run specific logic based on the combination of them?

bazola
  • 270
  • 5
  • 16
  • I noticed you posted some example code related to this question on the [Code Review SE](http://codereview.stackexchange.com/questions/58292/decision-table-for-the-movement-ai-in-a-game). You should [post it here as an answer](http://stackoverflow.com/help/self-answer)--it may help someone else out! – Malcolm Jul 28 '14 at 21:48

1 Answers1

0

Well I thought about decision tables in Objective-C some more and came up with a solution to implement a basic one. I will not post the entire code here, just the snippets that make the decision table work and their basic purpose. I posted this over at Code Review SE if you want to see the full code and some great suggestions for how to improve it. I'm posting this now because someone posted a comment requesting that I do so, but I will definitely end up improving this and integrating the suggestions from the review. Anyway, here is the code.

First before the initialization method I establish a number of NSString constants that will be used as the keys in an NSDictionary.

//Two options for the decision table, either access the dictionary directly with 0-x, the enum values, or make strings for their names
//the advantage of strings is that it is more extensible, and the position in the enum doesnt matter
NSString* const kEnemyMovementStateJustSpawned = @"enemyMovementStateJustSpawned";
NSString* const kEnemyMovementStateIdle = @"enemyMovementStateIdle";
NSString* const kEnemyMovementStateNeedsMoving = @"enemyMovementStateNeedsMoving";
NSString* const kEnemyMovementStateToFloor = @"enemyMovementStateToFloor";
NSString *const kEnemyMovementStateAtDestinationFloor = @"enemyMovementStateAtDestinationFloor";
NSString* const kEnemyMovementStateToFloorExit = @"enemyMovementStateToFloorExit";
NSString* const kEnemyMovementStateToAttackWalls = @"enemyMovementStateToAttackWalls";
NSString* const kEnemyMovementStateToAttackFloor = @"enemyMovementStateToAttackFloor";
NSString* const kEnemyMovementStateToAttackRoom = @"enemyMovementStateToAttackRoom";

Then I use these constants along with the names of methods in the class to build the NSDictionary:

-(void) setupDecisionTable {
    //the string objects are the names of methods in the class
    _decisionTable = @{kEnemyMovementStateJustSpawned: @"doEnemyJustSpawned",
                       kEnemyMovementStateIdle: @"doEnemyIdle",
                       kEnemyMovementStateNeedsMoving: @"doEnemyNeedsMoving",
                       kEnemyMovementStateToFloorExit: @"doFloorMovement",
                       kEnemyMovementStateToFloor: @"doVerticalMovement",
                       kEnemyMovementStateAtDestinationFloor: @"doEnemyAtDestinationFloor",
                       kEnemyMovementStateToAttackWalls: @"doFloorMovement",
                       kEnemyMovementStateToAttackFloor: @"doFloorMovement",
                       kEnemyMovementStateToAttackRoom: @"doFloorMovement"
                       };
}

Then every tick I call this method, which executes the method with the name of the object pulled from the dictionary:

-(void) doMovement {
    //the selector is formed from a string inside the decision table dictionary
    SEL methodToCallName = NSSelectorFromString([_decisionTable objectForKey:[self stringForState:self.state]]);
    if (methodToCallName) {
        IMP functionPointer = [self methodForSelector:methodToCallName];
        void (*methodToCall)(id, SEL) = (void *)functionPointer;
        methodToCall(self, methodToCallName);
    }
}
-(NSString *) stringForState:(EnemyMovementState)state {
    switch (state) {
        case EnemyMovementStateJustSpawned:
            return kEnemyMovementStateJustSpawned;
        case EnemyMovementStateIdle:
            return kEnemyMovementStateIdle;
        case EnemyMovementStateNeedsMoving:
            return kEnemyMovementStateNeedsMoving;
        case EnemyMovementStateToFloor:
            return kEnemyMovementStateToFloor;
        case EnemyMovementStateAtDestinationFloor:
            return kEnemyMovementStateAtDestinationFloor;
        case EnemyMovementStateToFloorExit:
            return kEnemyMovementStateToFloorExit;
        case EnemyMovementStateToAttackWalls:
            return kEnemyMovementStateToAttackWalls;
        case EnemyMovementStateToAttackFloor:
            return kEnemyMovementStateToAttackFloor;
        case EnemyMovementStateToAttackRoom:
            return kEnemyMovementStateToAttackRoom;
        default:
            return nil;
    }
}

Finally here are a couple of the methods that execute, just for a complete example:

-(void) doEnemyIdle {
    if ([self checkFloorsForJobs]) {
        self.state = EnemyMovementStateNeedsMoving;
    } else {
        [self doIdleMovement];
    }
}
-(void) doEnemyNeedsMoving {
    [self calculateFloorExitPositionByFloor];
    self.state = EnemyMovementStateToFloorExit;
}

This is a pretty simple implementation. Currently it can only deal with one input, and a better decision table would be able to evaluate multiple inputs and provide the proper output. I think it could be extended by having an intermediate method that took the state combined with other variables to choose the proper object from the dictionary.

After doing all this, I'm not sure that decision tables are worth the effort in Objective-C. I do not know if the code is easier to understand than a switch statement. In order to add new logic to the code, it has to be modified in more places than a switch statement would seem to require. I provide this code as an example, and it would be cool to see other versions of decision tables in Objective-C if anyone has one.

Community
  • 1
  • 1
bazola
  • 270
  • 5
  • 16