Difficulty effects on AI

I have my game up (listed in Your Games Here) called Rimscape, located at:
http://www.gamelizard.com/webstart/rimscape.php

It’s a game with space fighting in it, and I think I have the AI decently coded in terms of combat. However what I’ve been TRYING to do is make some sort of adjustment to their combat skills in relation to the difficulty level, which goes from 1 to 10.

One thing I tried was, in the routine where the ship determines which way it needs to face, I’d add a random angle left or right, which was scaled by the difficulty so that lower difficulties would result in a greater value added to shift the angle. So essentially what that did was every frame the ship would make its calculations and adjust its aim toward the ship, but offset by a certain amount so that the easier the difficulty, the worse their aim.

This did in fact make the game easier, but it looked silly. Every frame the ships would wiggle back and forth because the random factor would be different every frame and that caused some problems with it being sensible, and sometimes a ship would so often think that it was “way off aim” that it would rarely fire at a target sitting still right in front of it.

So if you guys could just shoot at some stuff for a bit (you have to do some actual damage to a ship otherwise most of them who are set to friendly won’t attack you until you really convince them you’re NOT friendly :wink: ) and try to help me come up with new ideas to adjust how they fight, that’d be great!

Even if your suggestions aren’t really about making the difficulty change, just about modifying the AI, I’d greatly appreciate them all! Thanks

The best way is to implmement a FuSM (Fuzzy State Machine) which controls the difficulty of the NPC.

FuSM work by having relative truth, not crisp truth. Crisp truth is either true or false. But relative truth can be 3/4 true and 1/4 false.

There are three great articles in Game Programming Gems 1 and 2.

The first article goes through a FSM (Finite State Machine, crisp truth) then some articles below it goes through how you could use a FuSM in your video games.

Then in the second book, it goes through the adjustments needed to create a FuSM from a FSM.

Im in the process of creating a FuSM myself. When I finish, il post you the code.

DP

i suggest one of two things:
(1) scrap the random angle function altogether, and let the amount of damage done by a direct hit be adjustable so that at easier levels, less damage is incurred by each direct hit (this will achieve the same effect of your random angles, because in either case, the hit points of the player are preserved at easier levels) or
(2) don’t add a random angle to each frame or cycle of the routine in which the ship determines which way it needs to face. this is what cause the wiggling, stalling and silly look. instead, let the random function come in every fifth, or tenth or hundredth routine. this will make the direction seeking seem smoother, and still allow for poor aim at easier levels.

[quote]scrap the random angle function altogether, and let the amount of damage done by a direct hit be adjustable so that at easier levels, less damage is incurred by each direct hit
[/quote]
That’s an inverse of the usual smarter enemy approach in production strategy games, the “smarter” AI actually just produces more stuff than the player can. Instead of making the AI smarter or dumber the AI breaks the game rules to be more or less powerful. That’s not much fun and always feels like a bit of a gyp.

Perhaps a simple thing to make the ship AIs seem more like newbies is to reduce their rate of fire or to have them fire panic shots when they haven’t quite lined up on the target, or both so the panic shots use up a fire cycle.

Along the same lines you could have a ship on the easy levels take a little longer to decide to turn. Once the turn has started it can be as acurate as normal or you can have a small chance of planned over/undershoot followed by another delay before trying again.

Fuzzy State Machines can do all of those things anyone has suggested from the above.

In fact, you can make them learn too. So the more shots the AI fired, the more accurate they become (but dont let them become too accurate).

DP

[quote]Fuzzy State Machines can do all of those things anyone has suggested from the above.
[/quote]
Sure, but that’s an implementation choice. The question is not what method to use to implement the Ai, but what Ai to implement. 8)

cool! I like this :slight_smile: Currently damage to the player is affected by difficulty, but not this other stuff. I look forward to seeing your code you come up with darkprophet!

Sure malhkan, on the upside, i can describe to you what it can/can’t do:

The bot sees the enemy, and transitions into the ‘ANGRY’ state (it is an enemy, after all). At first, say the bot is barely angry (0) because the enemy isn’t a really bad enemy, and hasn’t done anything yet. The bot may just stand there and watch, not doing anything (this is defined by the beahavior for a ‘0’ anger level). This can be defined mathematically:

0.5 - 0 == 0.5
1 - 0.5/0.5 == 0

The bot moves at 0% of its average speed, so basically… it doesn’t move.

Then, the enemy makes some rude gesture that the bot sees. This increases the bot’s anger level by 0.2 (to… 0.2). Now say the behavior defines that the bot should approach the enemy at a walking pace at a 0.5 anger level (this ‘walking’ speed could also be defined on the bot, keeping the behavior object more generic, and enabling it to be a singleton instead of an instance object). Because the bot is less than that (0.2 is less than 0.5), it should approach the enemy at a pace relative to that:

0.5 - 0.2 == 0.3
1 - 0.3/0.5 == 0.4

So, the bot should approach the player at 40% of its average ‘approach’ speed.

Then, say the enemy pulls out a gun. This increases the bot’s anger level by 0.6, making it 0.8:

0.5 - 0.8 == -0.3
1 - -0.3/0.5 == 1.6

So, the bot should approach the player at 160% of its average ‘approach’ speed.

Then, say the enemy shoots the bot. This increases the bot’s anger level by 1, making it… 1 (maximum value).

0.5 - 1 == -0.5;
1 - -0.5/0.5 == 2

So the bot should approach the player at 200% of its average ‘approach’ speed.

Anyway… you get the idea. There are no specific intermediate states within ‘angry’. The reaction is based purely on the difference from the average.

And that my friend, is a direct quote from a guru called “Shmooh” on the jME boards. And that is what im going to be aiming at.

Anyway, if you change the “speed” bit to accuracy, you can see that it sorta does exactly what you need it to do.

DP

right, thats done ;D

The source:


/**
 * 
 * Fuzzy State Machine State whereby the state has a value
 * which detemines how potent this state is
 * 
 * @author Ahmed
 * @version: $Id: FuzzySMState.java, Sep 19, 2004 6:13:32 PM
 */
public class FuzzySMState {
      
      // the state's name
      private String name;
      
      // the current effectivness of the state
      private float fuzzyLogicEffectiveness;
      
      /**
       * Constructor for a fuzzy state machine state with
       * a given ID
       * 
       * @param name, the name of the state
       */
      public FuzzySMState(String name) {
            this.name = name;
            fuzzyLogicEffectiveness = 0f;
      }
      
      /**
       * Sets the name for this state
       * @param name
       */
      public void setID(String name) {
            this.name = name;
      }
      
      /**
       * Obtains the name of this state
       * @return String
       */
      public String getID() {
            return name;
      }
      
      
      /**
       * Returns the current effectiveness of this state
       * 
       * @return float
       */
      public float getEffectiveness() {
            return fuzzyLogicEffectiveness;
      }
      
      /**
       * Calculates a fuzzy transition. The input is how much
       * to add to the current effectiveness of this state by.
       * It returns a distance away from the mean of the state
       * 
       * @param input
       * @return float
       */
      public float fuzzyTransition(float input) {
            // check to see if the input is more than 1.
            // if it is, then make the input 1
            if (input > 1) {
                  input = 1;
            }
            
            // calculate how far it is from the mean
            float output = 0.5f - (input + fuzzyLogicEffectiveness);
            fuzzyLogicEffectiveness = 1.0f - (output/0.5f);
            
            // and return it
            return fuzzyLogicEffectiveness;
      }
}


import java.util.HashMap;

/**
 * <code>FiniteSM</code> (Finite State Machine) provides a framework for
 * providing a stimulus-response based actions.
 * 
 * @author Ahmed
 * @version: $Id: FSM.java, Jul 21, 2004 6:38:26 PM
 */
public class FuzzySM {

      // a list of all the states
      private HashMap states;
      
      private String currentState;

      /**
       * Creates a new instance of a fuzzy state machine
       * 
       * @param stateID
       *            the initial state at which the machine resides
       */
      public FuzzySM(String stateID) {
            states = new HashMap();
            currentState = stateID;
      }

      /**
       * returns the current state of the FSM
       * 
       * @return string which holds the information
       */
      public FuzzySMState getCurrentState() {
            return (FuzzySMState)states.get(currentState);
      }
      /**
       * sets the current state
       * 
       * @param arg_state
       *            the state to set the current state to
       */
      public void setCurrentState(String state) {
            currentState = state;
      }

      /**
       * Called to add another state to the FSM machine.
       * 
       * @see com.jme.ai.fsm.FuzzySMState
       * @param fusmstate
       */
      public void addState(FuzzySMState fusmstate) {
            states.put(fusmstate.getID(), fusmstate);
      }

      /**
       * Deletes a state associated with the FuSM Should be used carefully as the
       * state that is removed could be the current state
       * 
       * @param stateID
       */
      public void deleteState(String stateID) {
            states.remove(stateID);
      }
}

Test Case:


/**
 * @author Ahmed
 * @version: $Id: TestFuzzySM.java, Sep 19, 2004 6:52:18 PM
 */
public class TestFuzzySM {

      public static void main(String[] args) {
            FuzzySMState uncaring_state = new FuzzySMState("uncaring");
            FuzzySMState annoyed_state = new FuzzySMState("annoyed");
            
            FuzzySM machine = new FuzzySM("uncaring");
            machine.addState(uncaring_state);
            machine.addState(annoyed_state);
            
            float effectivness = machine.getCurrentState().getEffectiveness();
            System.out.println(effectivness);
            
            effectivness = machine.getCurrentState().fuzzyTransition(0.2f);
            System.out.println(effectivness);
            
            effectivness = machine.getCurrentState().fuzzyTransition(1.0f);
            System.out.println(effectivness);
            
            effectivness = machine.getCurrentState().fuzzyTransition(1.0f);
            System.out.println(effectivness);
      }
}

All of the above code is under the BSD License, so please adhere to that.

DP

sry, forgot how to tell you how relevant it is in your game.

Basically, the Maximum input value is 1, minimum: 0;

You input a float between those two variables, and you will get an output. Say that output is 2, then you should aim twice as better. 7.6, well, can you really miss?!

And so on and so forth. You could start to decrease the variable using time, so the longer he doesn’t fire, the more out of practice he becomes, the less accurate it is.

This is useful in congunction with a FiniteSM whereby you switch states and then those states determine the FuzzySM’s state and the FuSM’s value.

Hope that helps, DP

Very neat! Thanks for that. I like the idea, hopefully I’ll have time to implement it :slight_smile:

[quote]Very neat! Thanks for that. I like the idea, hopefully I’ll have time to implement it :slight_smile:
[/quote]
In regard to your ship steering mechanism I’d recommend looking into feedback loops. A nice article was put out in Game Developer magazine in the June/July issue; pg. 18. It would be quite easy to vary the loop used (its equation) to create lazy ship navigation that is smooth and simulates a green pilot.

Another may be tuned to be more exact, etc.

One could use this not only for navigation, but say for any turrets on the ship (if any), etc. lots of uses.

Some links given from the game dev article:
http://www.expertune.com/articles.html

http://www.tcnj.edu/~rgraham/PID-tuning.html

Hunt down the article though as it explains things clearly. These links aren’t the best…

Search around for info relating to “proportional feedback loops” (P loops). More advanced loops are PI, PD, PID (proportional, integral, derivative).

oh btw, an up to date of my AISystem can be found at:

http://www.myjavaserver.com/~digiwired/ai-code.zip - Source Code

http://www.myjavaserver.com/~digiwired/jmeai.jar - Jar

That includes an up to date FiniteStateMachine as well as a FuzzyStateMachine. :slight_smile:

DP