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:
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.
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.
Three Types of Design Patterns (Behavioral, Creational, Structural) Distinguish between Behavioral, Creational, and Structural Design Patterns.
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.
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:
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):
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
}
}
}
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 :).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With