Skip to main content
added 65 characters in body
Source Link
Doc Brown
  • 220.6k
  • 35
  • 410
  • 625

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities which could lead to the complexity you scetched in your question.

Instead, you could implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     var nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI (driven by the UI event loop, not yours) then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well:

  HandleUserInputEvent(CellType cell)
  {
      if(ticTacToeGame.IsMarkingValid(cell))
      {
         ticTacToeGame.Mark(cell);
         DisableMoveByUI();
         queue.Insert(Event.MoveCompleted);
      }
  }

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like. I hope you get the idea.

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities.

Instead, you could implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     var nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI (driven by the UI event loop, not yours) then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well:

  HandleUserInputEvent(CellType cell)
  {
      if(ticTacToeGame.IsMarkingValid(cell))
      {
         ticTacToeGame.Mark(cell);
         DisableMoveByUI();
         queue.Insert(Event.MoveCompleted);
      }
  }

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like. I hope you get the idea.

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities which could lead to the complexity you scetched in your question.

Instead, you could implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     var nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI (driven by the UI event loop, not yours) then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well:

  HandleUserInputEvent(CellType cell)
  {
      if(ticTacToeGame.IsMarkingValid(cell))
      {
         ticTacToeGame.Mark(cell);
         DisableMoveByUI();
         queue.Insert(Event.MoveCompleted);
      }
  }

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like. I hope you get the idea.

Added pseudo code for UI event handler
Source Link
Doc Brown
  • 220.6k
  • 35
  • 410
  • 625

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities.

Instead, you could try to implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     var nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             DisableMoveByUI();
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI (driven by the UI event loop, not yours) then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well.:

  HandleUserInputEvent(CellType cell)
  {
      if(ticTacToeGame.IsMarkingValid(cell))
      {
         ticTacToeGame.Mark(cell);
         DisableMoveByUI();
         queue.Insert(Event.MoveCompleted);
      }
  }

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like. I hope you get the idea.

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities.

Instead, you could try to implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             DisableMoveByUI();
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well.

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like.

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities.

Instead, you could implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     var nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI (driven by the UI event loop, not yours) then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well:

  HandleUserInputEvent(CellType cell)
  {
      if(ticTacToeGame.IsMarkingValid(cell))
      {
         ticTacToeGame.Mark(cell);
         DisableMoveByUI();
         queue.Insert(Event.MoveCompleted);
      }
  }

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like. I hope you get the idea.

Source Link
Doc Brown
  • 220.6k
  • 35
  • 410
  • 625

I would try to keep the TicTacToeGame completely UI agnostic. No observer, no publisher-subscriber inside that class. Only "business logic" (or call it "game-logic") inside that class, no mixed responsibilities.

Instead, you could try to implement the turn-logic by utilizing your own event queue. I give an example in pseudo-code using polling for the sake of simplicity, depending on your environment you can implement it without polling instead:

  MainLoop()
  {
     while(queue.IsEmpty())
        WaitSomeMiliseconds(); // or use some queue.WaitForEvent() command, if available

     nextEvent=queue.getNextEvent();
     if(nextEvent==Event.MoveCompleted)
     {
          Display(ticTacToeGame);
          if(ticTacToeGame.GameOver())
              break;
          nextPlayer=PickNextPlayer();
          if(nextPlayer.Type()==PlayerType.Human)
          {
             AllowMoveByUI();  // enable UI controls for entering moves by human
          }
          else
          { 
             DisableMoveByUI();
             LetAIMakeMove(ticTacToeGame);
             queue.Insert(Event.MoveCompleted);
          }
      }
  }

And the event handlers of the UI then should have some logic to mark a cell by the user and insert an Event.MoveCompleted into the queue as well.

Of course, using a queue is a little bit overengineered in the example above, since there is currently only one type of event, so a simple global boolean flag would do the trick as well. But in your real system, I assume there will be different types of events, so I tried to gave a rough outline on how the system may look like.