Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Design pattern for modeling two competing objects

I'm trying to figure out the best design pattern to use for managing a "competition" between two interacting objects. For example, if I want to have a Fox class that chases a Rabbit class through a simple environment. I want to let them "compete" and find out which one wins. Eventually it would become a teaching tool that students can use to experiment with inheritance and other OO programming skills.

Is there an established design patter for this use case?

Here's the best I could come up with: one class to represent the environment that hosts both other objects. I'm keeping it very simple and assuming the animals only run in straight lines and the fox catches the rabbit if he gets close enough to bite the rabbit. Here is some code demonstrating what I'm describing. I used PHP because I can write it quickly, but I don't want to focus on the specifics of the language. My question is really about the design pattern / architecture.

class Forrest() {
        public $fox;
        public $rabbit;
        public $width = 100; //meters?
        public $length = 100;

        __construct() {
                $this->fox = new Fox();
                $this->rabbit = new Rabbit();
                $this->theChase();
        }

        public function theChase() {
                 while (!$this->rabbit->isBitten) {
                         $this->rabbit->react($fox);
                         $this->fox->react($rabbit);
                 }
                 log('The fox got the rabbit!');
        }
}

abstract class Animal() {
        public $speed;
        public $hasTeeth = false;
        public $position;
        public $direction;
        public $isBitten = false;
        public function run($distance) {
                // update coordinates based on direction and speed
        }

        public function bite($someone) {
                 if (isCloseEnough( $someone ) && $this->hasTeeth) {
                          $someone->isBitten = true;
                          log(get_class($this) . ' bit the ' . get_class($someone)); //the Fox bit the Rabbit
                 }
        }

        public abstract function react($someone);
}

class Rabbit extends Animal {
         __construct() {
                  $this->speed = 30;
                  $this->position = [0,0];
                  $this->direction = 'north';
                  log(get_class($this) . ' starts at 0,0');
         }

         public react($fox) {
                //avoid the fox
         }
}

class Fox extends Animal {
          __construct() {
                  $this->speed = 20;
                  $this->position = [100,100];
                  $this->direction = 'south';
                  log (get_class($this) . ' starts at 100,100');
          }

          public react($rabbit) {
                  //try to catch the rabbit
          }
}

There are two immediate problems I see with this approach:

  1. This architecture results in sequential, alternating actions. In other words, first the rabbit does something then the fox does something then the rabbit does something... This is more like a card game where each player takes turns moving. It would be more interesting if both objects were able to react simultaneously.

  2. There is not yet a system for limiting the amount of activity per "turn". There needs to be some sort of restriction on what can happen in a single "turn" to keep it interesting. Otherwise the fox could simply run() run() run() ... run() until it catches the rabbit on it's first turn. 

There are probably other problems that I haven't noticed yet. I suspect that the answer to both (1) and (2) above is some sort of event system that allows action from one animal to trigger action from the other and visa versa. I also think there may need to be some representation of time associated with each action, but I'm not entirely sure.

like image 565
emersonthis Avatar asked Aug 20 '16 01:08

emersonthis


People also ask

What are the 3 types of patterns?

Three Types of Design Patterns (Behavioral, Creational, Structural) Distinguish between Behavioral, Creational, and Structural Design Patterns.

Can design patterns be combined?

Yes, you can, of course file for a patent for a “combination” invention, however, the law is quite strict when it comes to patentable combination inventions. For any invention to receive a patent, the creation or design of the idea must not be obvious.


2 Answers

So, you are tasked with fitting something like game under design patterns, which originally were created for enterprise kind of software only. Games are not enterprise software by definition, and that's the reason why many people have avoided thinking about design patterns when designing games. That does not mean it's not doable though.

My recommendations:

  • Think model first: design your problem as you envision it.
  • Remember it's still a game, so it needs to follow game development patterns: there's just one actually - game loop.

So, if you combine the two above (I wish the second was not there), then I'd design it this way (I will mention the design pattern if my suggestions remind me one):

  1. Mark current time point.
  2. Environment starts game loop.
  3. For each loop step, calculate time passed since the last time point. This will give you time span in some units (e.g. N milliseconds passed).
  4. Given time span, you need to ask each object to update its state (conceptually, ask them - where would you be now if N milliseconds had passed?). This reminds me of Visitor pattern a bit.
  5. After all objects have updated their states, Environment displays results on the screen (in real games this means to draw the current state of the game - each object gets redrawn on the screen; for your simple application, you could check whether Fox has reported that it has caught the rabbit).
  6. Obviously, while within the loop step, you need to keep marking the current time, so that the time span difference can be calculated at each step.

Step #4 involves some complexities, especially if the accuracy is critical. e.g. what if the time span is about a second, and within that second (in the middle somewhere) the fox would have caught the rabbit, but in the end there is still distance? this can happen if the speeds of fox and rabbit are functions of time (sometimes they slow down, sometimes they speed up) [btw, this sounds like a Strategy pattern - variation for calculating current speed - e.g. linear vs time-function). Obviously, if the fox and rabbit both just report their positions at the end of the time span, the catching moment will be missed, which is not desirable.

Here is how I would solve it: for given time span, if it's more than a single millisecond (let's assume millisecond is the shortest acceptable atomic time for good enough accuracy), then split it into the time spans of millisecond length each, and for each millisecond, ask every object to update its state. After all, if the object can update its state based on a time span, we can call it as many times as we want with shorter time spans. Obviously, there is unavoidable side effect - you need to update states in some order, but given that millisecond is too short period of time, it should be just fine to do that.

Pseudo code would look like this:

var foxAndRabbitGame = new FoxAndRabbitGame();
foxAndRabbitGame.RunGame(screen); //visitor
/* when this line is reached, game is over. Do something with it. */

class FoxAndRabbitGame
{
    private fox = new Fox(Speed.Linear()); //strategy
    private rabbit = new Rabbit(Speed.Linear()); //strategy


    void RunGame(screen)
    {
        var currentTime = NOW;
        while (true)
        {
            var timePassed = NOW - currentTime;
            currentTime = NOW;

            foreach (millisecond in timePassed)
            {
                fox.UpdateState ( millisecond , rabbit );
                rabbit.UpdateState ( millisecond, fox );

                if (fox.TryBite(rabbit))
                {
                    //game over.
                    return;
                }
            }

            //usually, drawing is much slower than calculating state,
            //so we do it once, after all calculations.
            screen.Draw(this); //visitor
            screen.Draw(Fox); //visitor
            screen.Draw(rabbit); //visitor
        }
    }

}
like image 171
Tengiz Avatar answered Sep 27 '22 19:09

Tengiz


In a game-loop, one usually would update the velocities (here in your the react function) of both objects and then update the position of the objects. Thus moving simultaneously.

while(!gameOver) {
 rabbit->react(fox);
 fox->react(rabbit);
 rabbit->updatePosition();
 fox->updatePosition();
}

For limiting activity per turn/frame you'd have to think of something clever. For example you could make a certain set of actions that one can do, and each action has an energy cost. You would get a certain amount of energy each turn to use. You'd have to have more than one run() action though to make this interesting :).

like image 44
macco Avatar answered Sep 27 '22 20:09

macco