views:

198

answers:

9

Ok the title is not clear, here is what I mean.

I am programming some kind of game (like the game of life). For example, there are animals (each animal is a Java instance of a class).

All these animals are on a map, and all this "world" evolves each "turn".

These animals can make actions at each turn. Example : a wolf kills a sheep.

But, I have trouble regarding the "way" to make these evolution between states, because the result will depend of the order I loop through the animals.

Example :

  • Wolf first : the wolf kill the sheep (then the sheep is dead, so no action)
  • Sheep first : the sheep eats some grass and THEN (turn of the wolf) the wolf kills the sheep

How can I solve this problem ?

Multithreading ? (but I will have a lot of animals, like 1000 and even more...). Is there an algorithm, a "way" to do that ?

Thanks

+10  A: 

You should model the turns properly, i.e. have every animal take its turn based on the current state of the world, so that the results of the action get updated into the next state of the world, not the current one. I.e.

  1. In the current step, the sheep eats some grass, and the wolf kills it.
  2. In the next state of the world, there is a content wolf and no sheep (or a sheep carcass - depending on your model).

This way the order of evaluation is not affecting the outcome, which opens up the possibility to e.g. parallelize the execution, preferably using FutureTasks and a ThreadPoolExecutor.

Péter Török
This is the best option. Consider in real life the sheep would eat some and then run once it became aware of the wolf. It might eat none and run immediately. Anyway, given the sheep does eat and is not spooked, then the end result would be some grass eaten and a dead sheep.Another option would be to order traversal based on some quantifier of the animals action/reaction rate. Premise being that a spooked sheep will react more quickly than a content one. A fat wolf will react more slowly than a hungry one (unless it is starved).
Tim Bender
And what if there are 2 wolves and 1 sheep ? I this case, the 2 wolves would have eaten a whole sheep, wich is incorrect
Matthieu
@Matthieu, good point. Since you did not give lots of details about your domain model, I kept the answer simple. Howver, in case of a more complex domain model where the actions of different actors may influence each other, one could a) take speed of animals into account (faster acts first), b) increase granularity (e.g. one step represents 1 second instead of 1 hour). These should reduce the chance and severity of such conflicts: the faster wolf catches the sheep, and/or both wolves get their fair share of it.
Péter Török
+3  A: 

Oh, you don't want to get into multi-threading, things will get even more confusing.

In this case, iterate through all animals and let them choose their action. THEN update the map.

Rodney Gitzel
Since Java5, if you have well defined (i.e. preferably independent) tasks, the Executor framework makes multithreading really easy and clean.
Péter Török
True, but in this case if coordinating step-wise state of many instances is confusing, then coordinating simultaneous state will be more so...
Rodney Gitzel
You're kidding, right? This is a great candidate for multi-threading.
Stephen P
Nope, not kidding. In a real-time model, absolutely multi-threading would be great, but this is a turn-based model. What would be the advantage of multi-threading?
Rodney Gitzel
+1  A: 

If you had a central Manager which could receive and emit notifications, you could use an Observer pattern. Every time a turn evolves, the animals notify where they are. The Manager notifies who is there, and they execute their eat() method according to the context. When they are done eating, they notify() the manager, who in turn tells them what to do (e.g. sheep.die())

Arrieta
+4  A: 

Thread concurrency is not the main issue here. I believe that the right model should be allowing every entity to decide on an action in the current turn. Then, for determining the game state at the next turn you should process the actions, resolving conflicts if needed. You should decide on rules for resolving all kinds of "conflicting" action combinations taken by two different players.

Eyal Schneider
A: 

Off the top of my head, you might do something like this:

Have a single World object that manages a list of Player objects. The player has the following methods:

Player

  • public void takeTurn( World world )
  • public boolean isDead()
  • public void kill()

When the World instance gets to each player, it asks it if it is dead. If not, it calls the takeTurn method. Or, takeTurn can call isDead() internally and just return if it is. If you kill another player (or yourself), you just call the player's kill method.

Passing in the world to the takeTurn is just in case you need to make a callback to update the state of the world.

Javid Jamae
A: 

One way to do this is:

  1. Have the sheep do their actions and record them on the next step.
  2. Update the board for the sheeps actions
  3. Do each wolf action and record them on the next step.
  4. Update the board for the wolf actions.

Two ways two multithread this are:

  • Divide the Board into different regions and assign each thread to a region. You'll have problems figuring out how to move it from one region to another.

  • The other way could be to break the loop through creatures to different threads. eg

    for(w=THREAD_NUM; w<NUM_OF_WOLVES; w+=NUM_OF_THREADS) { w.doAction(); }

mikek3332002
+1  A: 

It sounds like you wish to have the world evolve in the same manner every time like Conway's life. In Life, you look at each cell and calculate what the outcome for it would be based on the previous state. If the cell has two neighbors it will live to the next generation, no matter if those neighbors won't be there in the next generation. The original state should be read-only and the rules should work no matter what.

For instance, what if you have two wolves near a sheep, would they both eat it, or just one? If just one, then you need a rule for which one gets to eat the sheep and the other one needs to know that. You could go the opposite way, i.e. look at the sheep, find pick an animal that would eat it. Let's say that you only want one wolf to eat the sheep and you say that the wolf with the lowest 'Y' coordinate gets the sheep, or the lowest 'X' coordinate if there are two wolves with the same 'Y'. If you are processing a wolf you need to make sure there is no other wolf that would eat the sheep first. This could get more confusing because maybe that would has another sheep near it that it would eat, so it wouldn't eat the first one...

The easiest way would be to say that all animals can do their actions no matter what happens to them in the evolution to the next round or what any other animal does. If a sheep is surrounded by three wolves, it will be dead the next round and all wolves will share in the meal. If there is grass next to a sheep, the sheep will eat it even if the sheep is surrounded by wolves. For the wolves, they will all be fed that round because they were next to a sheep.

public class Cell {
    public int CellType = 0; // 0 == empty, 1 == grass, 2 == sheep, 3 == wolf
    public int Feedings = 0;
}

public class World {
public Cell [] Cells = new Cell[100];
public int Rows = 10, Cols = 10;

public Cell GetCell(x, y) {
    if (x < 0 || x >= Cols || y < 0 || y >= Rows) return null;
    if (Cells[y * Cols + x] == null) {
        Cells[y * Cols + x] = new Cell();
    }
    return Cells[y * Cols + x];
}

public World Evolve() {
    World w = new World();
    for (int y = 0; y < Rows; y++) {
        for (int x = 0; x < Cols; x++) {
            HandleCell(w, x, y);
        }
    }
    return w;
}

public void HandleCell(World newWorld, int x, int y) {
    Cell result = newWorld.GetCell(x, y);

    Cell c = GetCell(x, y);
    if (c.CellType == 2) { // sheep
        bool foundWolf = false;
        bool foundGrass = false;

        // code here to find if a wolf or grass are around the sheep

        if (foundWolf) {
            // default cell type is empty, so leave it be (wolf ate me)
        } else {
            result.cellType = 2; // still a sheep here
            if (foundGrass) {
                result.Feedings = c.Feedings + 1; // and he ate!
            } else {
                result.Feedings = c.Feedings; // or not...
            }
        }
    }

    if (c.CellType == 3) { // wolf
        bool foundSheep = false;

        // code here to find if a sheep is around the wolf

        result.CellType = 3;
        if (foundSheep) {
            result.Feedings = c.Feedings + 1; // ate the sheep!
        } else {
            result.Feedings = c.Feedings;
        }
    }
}
Jason Goemaat
Well if there a 3 wolves around the sheep, it is illogical that they end up fed the same way (i.e. like they had eaten a whole sheep each one)
Matthieu
Then you need a well defined rule to determine which wolf would eat the sheep, or have the code for the sheep detect how many wolves will eat it and feed them each a portion based on some logic.
Jason Goemaat
+1  A: 

I think it is only possible for entities to evolve in parallel in very simple scenarios (Conway's life as an example) - for your "wolves-sheep-grass" world it's getting more complex the more you think about it:

  • Consider a situation with two wolves and one sheep. Wolf One decides to eat the sheep; Wolf Two decides to eat the sheep - how would you decide which one gets the sheep (let's suppose that eating is an atomic operation - wolves can't share their meals). So you still will need to decide on some order so, say Wolf One gets a sheep and Wolf Two gets nothing (which is completely unexpected for the Wolf Two - there was a sheep a second ago, it opens its mouth and - BAM! - there's nothing)

  • There are two wolves and two sheep; both sheep "attractiveness" is the same for both wolves, so they choose their prey randomly. Unfortunately, they're choosing the same sheep, Wolf One eats Sheep One, Wolf Two tries to eat Sheep One too but it magically disappears right under its nose. End of turn. In the next turn Sheep Two runs away, Wolf Two starves - which is completely illogical.

So I think simply iterating over the list of Animals and calling a method for each animal which performs an atomic action results in a more logical universe. If you're concerned that animals spawned earlier have unfair advantage over the ones spawned later you may just randomize the list of Animals before each turn - this way, in your example, either Sheep eats some grass and then is getting eaten by the Wolf, or the Wolf eats it before it has a chance to eat any grass. C'est la vie.

Sergey
Hey you are right about the 2 wolf scenario (except both wolves will be fed, but on 1 sheep, so this is illogical because they will be fed the same amount as if they ate a whole sheep, instead of sharing for example) ! Yes maybe having an order is not such a bad idea, and randomize the list could be a good thing (and simpler to develop).
Matthieu
Yeah, the point is that in such coarse-granular world allowing each animal to act based on a 'snapshot' of the world made at the start of the turn would inevitably create conflicts (parallel universes? :) )
Sergey
Another scenario: Wolf at 10,9 and a sheep at 10,10. Turn 1: Wolf moves to 10,10 and eats the sheep. The sheep, at the same time, moves to 10,11, thus getting out of the wolf's reach. Turn 2: Fed wolf at 10,10 and live sheep at 10,11.As was pointed out, you could also increase the world's granularity - sheep eats, wolf sees sheep, wolf runs; wolf bites; sheep.health-=10; wolf.tummy+=10; if (sheep.health==0) {sheep.die()}; if (wolf.tummy.is_full()) { wolf.stop_eating(); wolf.feel_happy();} - this way the sheep would eat some grass and then two wolves will be able to eat a part of a sheep each.
Sergey
Answer accepted : I will iterate through the list of animals, so there will be no conflicts. As you suggested, I will randomize the list of animals at each turn. This is the only answer that will not create conflict (and I don't want to go into extreme granularity too). Thanks
Matthieu
A: 

Maybe you could do this totally random.

First random example: sheep eats the grass and then the wolf eats the sheep (sheep -> wolf)

Second random example: wolf eats the sheep, because the poor thing was too slow to get to the grass (wolf -> /)

Is the real world deterministic? :)

_simon_