2

I'm developing a BlackJack game for iOS. Keeping track of the current state and what needs to be done is becoming difficult. For example, I have a C++ class which keeps track of the current Game:

class Game {
  queue<Player> playerQueue;
  void hit();
  void stand();
}

Currently I'm implementing it using events (Method A):

- (void)hitButtonPress:(id)sender {
  game->hit();
}

void Game::hit() {
  dealCard(playerQueue.top());
}

void Game::stand() {
  playerQueue.pop();
  goToNextPlayersTurn();
}

as more and more options are added to the game, creating events for each one is becoming tedious and hard to keep track of.

Another way I thought of implementing it is like so (Method B):

void Game::playersTurn(Player *player) {
  dealCards(player);
  while (true) {
    string choice = waitForUserChoice();
    if (choice == "stand") break;
    if (choice == "hit")
      dealCard(player);
    // etc.
  }
  playerQueue.pop();
  goToNextPlayersTurn();
}

Where waitForUserChoice is a special function that lets the user interact with the UIViewController and once the user presses a button, only then returns control back to the playersTurn function. In other words, it pauses the program until the user clicks on a button.

With method A, I need to split my functions up every time I need user interaction. Method B lets everything stay a bit more in control. Essentially the difference between method A and B is the following:

A:

function A() {
  initialize();
  // now wait for user interaction by waiting for a call to CompleteA
}

function CompleteA() {
  finalize();
}

B:

function B() {
  initialize();
  waitForUserInteraction();
  finalize();
}

Notice how B keeps the code more organized. Is there even a way to do this with Objective-C? Or is there a different method which I haven't mentioned recommended instead?

A third option I can think of is using a finite state machine. I have heard a little about them, but I'm sure if that will help me in this case or not.

What is the recommended design pattern for my problem?

4

1 回答 1

1

我理解你遇到的困境。当我第一次开始使用 iOS 时,我很难将自己的注意力集中在放弃与操作系统之间的控制权上。

一般来说,iOS 会鼓励你使用方法 A。通常你的 ViewController 中有变量在方法 A() 中设置,然后在 CompleteA() 中检查它们以验证 A() 是否首先运行等。

关于您关于有限状态机的问题,我认为它可以帮助您解决问题。我在 iOS 中写的第一件事是 FSM(这是非常糟糕的代码),但是你可以看一下这里(在 FlipsideViewController.m 的底部附近:

https://github.com/esromneb/ios-finite-state-machine

一般的想法是你把它放在你的 .h 文件中的 @interface 块内

static int state = 0;
static int running = 0;

在你的 .m 你有这个:

- (void) tick {

    switch (state) {
        case 0:
            //this case only runs once for the fsm, so setup one time initializations

            // next state
            state = 1;

            break;
        case 1:
            navBarStatus.topItem.title = @"Connecting...";
            state = 2;
            break;
        case 2:
            // if something happend we move on, if not we wait in the connecting stage
            if( something )
                state = 3;
            else
                state = 1;
            break;
        case 3:
            // respond to something

            // next state
            state = 4;
            break;
        case 4:
            // wait for user interaction
            navBarStatus.topItem.title = @"Press a button!";
            state = 4;

            globalCommand = userInput;

            // if user did something
            if( globalCommand != 0 )
            {
                // go to state to consume user interaction
                state = 5;  
            }

            break;

        case 5:
            if( globalCommand == 6 )
            {
                // respond to command #6
            }
            if( globalCommand == 7 )
            {
                // respond to command #7
                }

                        // go back and wait for user input
                        state = 4;
            break;

        default:
            state = 0;
            break;
    }

    if( running )
    {
        [self performSelector:@selector(tick) withObject:nil afterDelay:0.1];
    }
}

在此示例中(根据 github 上的示例修改)globalCommand 是一个表示用户输入的 int。如果 globalCommand 为 0,则 FSM 将在状态 4 中旋转,直到 globalCommand 不为零。

要启动 FSM,只需将 running 设置为 1 并从 viewController 调用 [self tick]。FSM 将每 0.1 秒“滴答”一次,直到运行设置为 0。

在我最初的 FSM 设计中,我必须响应来自运行它自己的软件的 Windows 计算机的用户输入和网络输入。在我的设计中,Windows PC 也运行着类似但不同的 FSM。对于这个设计,我使用 NSMutuableArray 构建了两个 FIFO 队列对象的命令。用户交互和网络数据包会将命令排入队列,而 FSM 会将项目出列并响应它们。我最终使用https://github.com/esromneb/ios-queue-object作为队列。

如果您需要任何澄清,请发表评论。

于 2013-02-21T10:28:34.393 回答