0

我是一名新手程序员,我刚刚开始阅读有关决策表的内容。我已经阅读了 Code Complete 中的第 18 章,它很有启发性。我环顾网络,试图在 Objective-C 中找到任何类型的决策表示例,但我找不到任何样板文件或如何实现这一点的真实示例。

我在业余时间用 Objective-C 编写游戏,我一直在处理日益复杂的游戏规则。有一些嵌套比较深的 if-else 语句,还有一些 switch 语句已经有 10 个或更多的情况需要处理。我认为使用决策表会更容易,但我不知道如何在 Objective-C 中实现这一点,以解决诸如游戏逻辑之类的不平凡的事情。

例如,我需要不同的方法来执行不同的状态组合。我将如何在 Objective-C 中实现一个决策表,它可以将不同的状态组合作为键,并根据它们的组合运行特定的逻辑?

4

1 回答 1

0

好吧,我更多地考虑了 Objective-C 中的决策表,并提出了一个实现基本决策表的解决方案。我不会在这里发布整个代码,只是使决策表工作的片段及其基本用途。如果你想查看完整的代码和一些关于如何改进它的好建议,我在Code Review SE上发布了这个。我现在发布这个是因为有人发表了评论要求我这样做,但我肯定会最终改进这一点并整合评论中的建议。无论如何,这是代码。

首先在初始化方法之前,我建立了一些 NSString 常量,它们将用作 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";

然后我使用这些常量以及类中的方法名称来构建 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"
                       };
}

然后我每次调用这个方法,它使用从字典中提取的对象的名称执行方法:

-(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;
    }
}

最后,这里有几个执行的方法,只是一个完整的例子:

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

这是一个非常简单的实现。目前它只能处理一个输入,一个更好的决策表将能够评估多个输入并提供适当的输出。我认为可以通过使用中间方法来扩展它,该方法将状态与其他变量结合起来从字典中选择合适的对象。

在完成所有这些之后,我不确定决策表是否值得在 Objective-C 中付出努力。我不知道代码是否比 switch 语句更容易理解。为了向代码中添加新逻辑,它必须在比 switch 语句似乎需要的更多地方进行修改。我提供此代码作为示例,如果有人有的话,在 Objective-C 中查看其他版本的决策表会很酷。

于 2014-07-29T00:49:16.477 回答