Design pattern for modeling two competing objects

I am trying to figure out which best design template is used to control the "competition" between two interacting objects. For example, if I want to have a Fox class that pursues the Rabbit class through a simple environment. I want to let them "compete" and find out which one will win. In the end, it will become a learning tool that students can use to experiment with inheritance and other OO programming skills.

Is there an established design pattern for this use case?

Here is the best I could come up with: one class to represent the environment in which both other objects are hosted. I keep it very simple and believe that animals run only in straight lines, and the fox catches a rabbit if it is close enough to bite a rabbit. Here is the code that demonstrates what I am describing. I used PHP because I can write it quickly, but I do not 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     } } 

With this approach, I see two problems:

  • This architecture leads to sequential, alternating actions. In other words, first the rabbit does something, then the fox does something, and the rabbit does something ... This is more like a card game in which each player takes turns moving. It would be more interesting if both objects could respond simultaneously.

  • While there is no system to limit the amount of activity per “turn”. There must be some kind of restriction on what can happen in one “turn” in order to keep it interesting. Otherwise, the fox could just run() run() run() ... run() until it catches the rabbit on the first turn.

There are probably other problems that I have not noticed yet. I suspect that the answer to both (1) and (2) above is some kind of system of events that allows you to act from one animal to trigger an action from another and vice versa. I also think that maybe there should be some idea of ​​the time associated with each action, but I'm not quite sure.

+7
events design-patterns software-design game-engine game-loop
source share
7 answers

So, you are entrusted with customizing something like a game under design patterns that were originally created only for enterprise software. Games are not corporate software by definition, and the reason many people avoid thinking about design patterns when developing games. This does not mean that it is not feasible, though.

My recommendations:

  • Think about the model first: ask about your problem, how do you imagine it.
  • Remember that this is still a game, so it must follow the patterns of game development: there is only one - the game cycle.

So, if you combine the two above (I would like the second not to be there), then I would design it this way (I mentioned the design template if my suggestions remind me of one):

  • Mark current time point.
  • The environment begins the cycle of the game.
  • For each step of the cycle, calculate the elapsed time since the last moment. This will give you a time interval in some units (e.g. missed milliseconds).
  • For a given time interval, you need to ask each object to update its state (conceptually, ask them - where would you be now if N milliseconds have passed?). This is a bit like a visitor style.
  • After all the objects have updated their states, the environment will display the results on the screen (in real games this means that you need to draw the current state of the game - each object is redrawn on the screen, for your simple application you can check whether Fox reported that he caught a rabbit).
  • Obviously, during the step of the cycle, you need to continue to mark the current time, so that the difference in time interval can be calculated at each step.

Step number 4 includes some difficulties, especially if the accuracy is critical. for example, what if the time span is about a second, and during that second (somewhere in the middle) the fox would catch a rabbit, but in the end there is still distance? this can happen if the speeds of the fox and the rabbit are functions of time (sometimes they slow down, sometimes they accelerate) [By the way, this sounds like a strategy template - a variation for calculating the current speed - for example, a linear and time function). Obviously, if the fox and the rabbit simply report their positions at the end of the time interval, a trapping moment will be missed, which is undesirable.

Here is how I would decide: for a given period of time, if it is more than one millisecond (suppose that a millisecond is the shortest acceptable atomic time for reasonably good accuracy), then divide it into time intervals of one millisecond each, and for each millisecond ask each object to update its state. After all, if an object can update its state based on a time interval, we can name it as many times as we want, with shorter periods of time. Obviously, the inevitable side effect is that you need to update the states in some order, but given that the millisecond is too short, it should be very good to do so.

The 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 } } } 
+3
source share

In the game loop, the speeds (here in your reaction function) of both objects were usually updated, and then the position of the objects was updated. Thus, moving simultaneously.

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

To limit activity per revolution / frame, you need to think about something smart. For example, you can do a certain set of actions that can be done, and each action has a cost of energy. You must receive a certain amount of energy for each turn. For this you need to have more than one run () action to make it interesting :).

+2
source share
  • I would suggest a reseller template . Usually this contributes to a direct connection between a set of objects (in your case, a fox and a rabbit). You will need to enter another object that captures the state of the system as a result of the actions of the rabbit and the fox (for example, call it "chase").
  • An intermediary object will handle the interaction between all objects. I. Assess the actions requested by the rabbit and the fox, then determine what the actual result of these actions will be. (everything goes through an intermediary!) and updates the “harassment”, respectively. This way you can control your problems 1 and 2 above, or other problems.
  • I previously implemented this template for HMIs, where users can interact with the system through the keyboard and screen, and depending on the choice / state of the system / previous choice, etc. appropriate state transitions are needed.
+2
source share

In general terms, my approach would be as follows:

  • Each object (fox, rabbit, etc.) has a state (in your case, speed, position and direction).
  • The environment (container) has a state that is a combination of the states of the subjects and other restrictions, if necessary (impervious zones, destroyed terrain, etc.).
  • Each subject has a cost function that must be minimized (a fox has a distance between subjects, a rabbit has the opposite of that distance).
  • Each subject must have some kind of restriction (for example, max_distance_per_turn, max_direction_changes_per_turn, etc.) so that the first subject does not win at a time.
  • The state of the environment can affect the actions of the subject (for example, the work of a fox dug a hole that cannot be transmitted by a rabbit), so each action should be aware of the global current state.
  • Each subject changes its state, starting only with the initial environment + the state of the subjects; the fox moves accordingly to the position of the rabbit at time 0 (and the rabbit does the same). Note that this is different from fox.react (rabbit); rabbit.react (fox); because the rabbit knows where the fox is at time 1 (after moving it).
  • If necessary, the whole transaction should also be checked: if at the time 0 the rabbit is not fatigued, and at the moment 1 he is also not subject to removal, but during the transition he reached the point at which he was bitten, the fox must win. To avoid this goal, you need to make the "turns" as atomic as possible in order to ignore these transactions. Alternatively, you can add transactional verification to the environment, which will be launched after each move.

In a more general form, you can think of this environment as a closed system with a retroaction: each action modifies the entire state that will affect new actions. In this scenario, each of them is still sequential, but each "turn" is really a closed transaction from one state to the next, in which all objects are executed simultaneously.

+1
source share

When developing a proposal for a mediator template to add the illusion of simultaneity, the game state can be extracted to a separate object (simple old data) and updated after all objects have made their decisions. For example (in java-ish)

 public class OpponentData { private Position theirPosition; // + public get // constructor with theirPosition param, keeping the class immutable } public interface Animal { // returns data containing their updated data OpponentData React(OpponentData data); Position GetPosition(); } public class Fox implements Animal { public OpponentData React(OpponentData data) { if (this.position == data.GetPosition()) // this method can be a little tricky to write, depending on your chosen technology, current architecture etc // Fox can either have a reference to GameController to inform it about victory, or fire an event // or maybe even do it itself, depending if you need to destroy the rabbit object in game controller EatTheRabbit(); else { // since the game state won't be updated immediately, I can't just run until I reach the rabbit // I can use a bunch of strategies: always go 1 meter forward, or use a random value or something more complicated ChaseTheRabbit(); } return new OpponentData(this.position); } } public class Rabbit implements Animal { public OpponentData React(OpponentData data) { KeepRunningForYourLife(); // maybe you can add something more for the rabbit to think about // for example, bushes it can run to and hide in return new OpponentData(this.position); } } public class GameController { private Fox fox; private Rabbit rabbit; private OpponentData foxData; private OpponentData rabbitData; private void Init() { fox = new Fox(); rabbit = new Rabbit(); foxData = new OpponentData(fox.GetPosition()); rabbitData = new OpponentData(rabbit.GetPosition()); } private void PerformActions() { var oldData = foxData; foxData = fox.React(rabbitData); // giving the old data to the rabbit so it doesn't know where the fox just moved rabbitData = rabbit.React(oldData); } } 

If you want the game to depend on more factors than just a position, you can easily expand the OpponentData class by adding a level of health, strength, etc.

This approach solves both of your problems because each player (the fox and the rabbit) does not know what the other is doing in the same turn, so the rabbit can evade the fox, and the fox cannot just run() run() run() to her victim (because she does not know where the rabbit is going to move). Fun fact - the Game of Thrones game uses the same technique to create the illusion of giving orders to your armies at the same time as other players.

+1
source share

I thought there should be two abstract classes associated with an abstract animal class (Omnivore and carnivorous classes, both have different attributes)

here Animal Abstract class

 public abstract class Animal implements Runnable{ private double speed = 0 ; // Default private Point location = new Point(new Random().nextInt(50) + 1 , new Random().nextInt(50) + 1); abstract void runAway(Animal animal); abstract void chase(Animal animal); abstract void search4Feed(); abstract void feed(); public synchronized Point getLocation() { return location; } public synchronized void setLocation(Point location) { this.location = location; } public double getSpeed() { return speed; } public void setSpeed(double speed) { this.speed = speed; } } 

Here are the Carnivore and Omnivore classes

 public abstract class Carnivore extends Animal { Animal targetAnimal ; 

}

 public abstract class Omnivore extends Animal { Animal chasingAnimal; 

}

For a forest class and its implementation, Iforest can be implemented by various forest classes. And he must maintain his own ecosystem of animals.

 public class Forest implements IForest { private List<Animal> animalList = new ArrayList<Animal>(); public Forest() { } @Override public void addAnimalToEcoSystem(Animal animal) { animalList.add(animal); } @Override public void removeAnimalFromEcoSystem(Animal animal) { animalList.remove(animal); } @Override public void init() { // to do: } @Override public List<Animal> getAnimals() { return this.animalList; } 

}

 public interface IForest { void removeAnimalFromEcoSystem(Animal animal); void addAnimalToEcoSystem(Animal animal); List<Animal> getAnimals(); void init(); 

}

Here are the classes of rabbits and foxes. The rabbit and fox classes have an instance of the IForest class in their constructor. Pursuit of any animal or running away from any animal should be a forest ecosystem. And these classes should notify their movements to forest classes through the IForest interface. So I used the Runnable thread, because these classes must move independently, and not sequentially. In the launch method, you can define rules for a hunter or hunt according to your specified conditions.

 public class Rabbit extends Omnivore { private IForest forest = null ; public Rabbit(IForest forest) { this.forest = forest; this.setSpeed(40); } @Override public void runAway(Animal animal) { this.chasingAnimal = animal; this.run(); } @Override public void chase(Animal animal) { // same as fox's } @Override void feed() { // todo: } @Override void search4Feed() { } @Override public void run() { double distance = 10000; //default, this.chasingAnimal.runAway(this); // notify rabbit that it has to run away while(distance < 5){ // fox gives chasing up when distance is greater than 5 distance = Math.hypot(this.getLocation().x - this.chasingAnimal.getLocation().x, this.getLocation().y - this.chasingAnimal.getLocation().y); if(distance < 1) { break; // eaten } //here set new rabbit location according to rabbit location } } 

}

 public class Fox extends Carnivore { private IForest forest = null ; public Fox(IForest forest) { this.forest = forest; this.setSpeed(60); } @Override public void chase(Animal animal) { this.targetAnimal = animal; this.run(); } @Override public void run() { double distance = 10000; //default, this.targetAnimal.runAway(this); // notify rabbit that it has to run away while(distance < 5){ // fox gives chasing up when distance is greater than 5 distance = Math.hypot(this.getLocation().x - this.targetAnimal.getLocation().x, this.getLocation().y - this.targetAnimal.getLocation().y); if(distance < 1) { feed(); break; } //here set new fox location according to rabbit location } } @Override public void runAway(Animal animal) { // same as rabbit's } @Override public void feed() { // remove from forest animal list for the this.targetAnimal } @Override void search4Feed() { // here fox searches for closest omnivore double distance = -1; Animal closestFeed = null; List<Animal> l = this.forest.getAnimals(); for (Animal a : l) { double d = Math.hypot(this.getLocation().x - a.getLocation().x, this.getLocation().y - a.getLocation().y); if (distance != -1) { if(d < distance){ this.chase(a); } } else{ distance = d ; } } } 

} init method below

 public static void main(String[] args) { // you can use abstract factory pattern instead. IForest forest = new Forest(); forest.addAnimalToEcoSystem(new Rabbit(forest)); forest.addAnimalToEcoSystem(new Fox(forest)); forest.init(); } 

if you want to make it more complex, such as collabation or something else you need to use the observed pattern. It can be used to create animals, forests through an abstract factory template. Sorry for the dirty code due to the fact that I don't have much time. Hope this helps you.

+1
source share

Competition between rabbits, foxes and, possibly, with other animals can be modeled using discrete event modeling , which can be considered as an example of design as such (simulation clock, event queue, ...).

Objects can implement a strategy template . In this case, the execute method can be called decideAction - it will receive the old state of the world (read-only) and issue a solution (description of the action).

Then the simulation planned an event that was the result of the decision. When the event is processed, the simulation will change the state of the world. Thus, the simulation can be considered an instance of the Mediator , since it isolates the agents from direct interaction - they simply see the state of the world and make decisions, while the production of a new state of the world and the evaluation of rules (such as speed and detection of a successful bite or escape) are left for modeling.


In order to simultaneously make decisions of all agents (animals), plan all events at the same time (during the simulation) and update the world state only after all events occurring at the same time of modeling have been processed (decisions made).

Then you will not need event queues and hours of simulation. Just a loop that collects all the solutions and finally updates the state of the world at each iteration will be enough.

Perhaps this is not what you want. because initially it might take some time for the rabbit to notice that the fox is approaching. Perhaps it would be more interesting if the timeout between events for the animal (reaction time) varies depending on its state (warning, sleep, etc.).


The amount of activity per “turn” cannot be limited when the animal can directly change the world state and is implemented using an almost arbitrary code. If the animal simply describes its action, its type and parameters can be confirmed by simulation and possibly rejected.

+1
source share

All Articles