Advertisement

Unnatural selection

Started by August 31, 2009 10:32 PM
7 comments, last by owl 15 years, 2 months ago
I wrote up a simple program where squirrels wonder around and attack eachother. I then thought it would be fun to make the attackers gain in size every time they attack, and the victims decrease in size every time they are hit. The interesting result was that the same squirrel, the "alpha squirrel", would always get the attack, no matter which squirrel it collided with, and another squirrel, the "runt", would always get attacked no matter who collided with it. After looking at the code, I realized this was becaus of the order in which they are setup in the array of squirrels. Since squirrel[0] checkes for collisions before any other squirrel, it will always register the attack. Likewise, squirrel[n] is the last one in the array to check for collisions, so everyone else has already had the chance to attack it, so it is always attacked. So how do I overcome this 'unnatural selection' symptom? Should I create a 50/50 chance check? Use some sort of thac0 or initiave check? What have others done to get around this?
Sure, you might try doing something like this (this assumes certain things about how your AI works).

Make each squirel have an "ActionSpeed", as well as a "CurrentActionPoints" variable that you set to 0.

Randomize the action speed for squirels to be in some certain range (you could also make it so larger squirels moved slower but lets forget that for now hehe)

Every frame do this..

#1 - loop through your squirels and do CurrentActionPoints += ActionSpeed
#2 - sort the list of squirels so that that the squirels with the most CurrentActionPoints are at the beginning
#3 - loop through the sorted list and for every squirel that has CurrentActionPoints > 1.0, have them do 1 tick of logic (1 tick of logic being to decide whether to move or attack someone) and subtract 1.0 from their CurrentActionPoints, repeating for an individual squirel until his CurrentActionPoints < 1.0 (this lets really fast squirels do multiple things a turn).

Ta da!
Advertisement
You could just randomly shuffle the array each turn...

Edit: another option would be go through the list and build a separate "colliding" list which just contains pairs of colliding squirrels. Then you go through that list and randomly choose one of the squirrels from each pair to "register" the attack.
You could limit squirrels to one action a round, that way small squirrels could gang up to attack larger ones. As well as randomizing the order the squirrels act every rounds.
Quote: Original post by Atrix256
Sure, you might try doing something like this (this assumes certain things about how your AI works).[...]
Ta da!
The usual implementation of this kind of system actually has a few more variables and is a bit more complicated. One simple addition is to make different actions cost different point values.

A slightly expanded (and IMO more "fair") system could work very similarly:

As in your system, each agent typically has "Action Index" and "Speed" attributes. Then, instead of subtracting a fixed value (such as 1.0), each possible action has an assigned cost. Perhaps "move" is 1.0 points and "attack" is 1.1 points. Sometimes, "passive actions" have a cost as well - "take damage" might cost an agent 0.2 points to represent a short stun or something like that.

Next, set each agent's "Action Index" to that agent's "Speed". Create a list with all the agents in it.

Repeat until you're bored:
* Sort the agent list by "Action Index" with the highest at the top.
* Subtract the top agent's "Action Index" from the "Action Index" of each agent (resetting the top's to 0)
* The first agent in the list chooses and executes a single action.
* Divide the cost for their action by their "Speed" attribute, and subtract that amount from their "Action Index".

This way, faster agents get to act more often, and those that waste less time (by selecting unnecessarily slow actions) will get to act more often, but one agent doesn't get to dominate the action unless they have extreme speed and/or chooses extremely fast actions.

As far as creating some kind of to-hit and damage system, I really like the way Civilization 4 does it:

Each unit has a strength value. We'll call the attacker's strength A and the defender's strength D. In civ, combat lasts until one unit does.
Each round of combat, a random number from 0 to 1 is selected. In the range 0..(A/(A+D)), the attacker hits the defender. In the rest of the range, the defender hits the attacker (giving the defender a change of D/(A+D) to hit the attacker). When the attacker hits the defender, the damage dealt is equal to 20 * (3 * A + D) / (3 * D + A). When the defender hits the attacker, the damage formula is the same with the A and D reversed. The 20 comes from each unit having 100 hit points, so that equally-match opponents take 5 hits to die.
"Walk not the trodden path, for it has borne it's burden." -John, Flying Monk
You could store all actions in an array, then after everyone has set their actions loop through that (not following other rules) to actually set the results of everyone's actions. Kind of how conway's life works.
Advertisement
I think you should consider the distance between the squirrels and allow them to attack only if they are in the attack range.

You could add a position value [x,y{,z}] to each squirrel, make them move at a certain ratio every frame and allow them to attack each other only if the difference between their positions is less than some thresold.

This way if the first squirrel in the array is far-far away and the others are moving really slow and the last one is moving fast near them, the last in the array will probably be the one with more chances of attaking others.

I can imagine very fun strategies like:

If in radius of attack
Attack everyone and run away
OR
Attack one
OR
Attack the weakest
OR
Run away
etc.
[size="2"]I like the Walrus best.
You have a turn-based system where 1 squirrel always goes first. Solution is pretty simple, do what they did in BattleTech pen and paper game:

- first array pass: do all attacks (but dont apply damage)
- second array pass: apply all damage

Thus all attacks/damage are simultaneous, despite being resolved in a turn-based order.
oh, it was turn based. Sorry.
[size="2"]I like the Walrus best.

This topic is closed to new replies.

Advertisement