1

I'm coding AI objects for a game.

My goal is to have the code optimized so that I can handle as many AI processes as possible simultaneously.

The game is 2d physics, so I'm already dealing with entity-number bottle-necking in collision handling, so the AI will need to be efficient.

The commands I'm dealing with are simple ASDF + QE movement, and mouse clicks and positions.

Modelling the AI movements on the actual keys/mouse may not be that good of an idea, I realize. But I want to start here and then pick it apart later.

I've decided to use boolean arrays to represent the commands as follows:

//Commands: continue, up, down, left, right, turnQ, turnE, mouse0, mouse1 

boolean[] moveBools = {false, false, false, false, false, false, false, false, false}; 

//The first boolean in the array 'continue' will terminate the loop if its true.

What I'm struggling with is how to pass this data into my function that actually moves the physics bodies. I could simply pass an entire boolean array each game iteration, or pass the /changes/ from iteration to iteration.

This type programming bridges into actual boolean mathematics, which is well beyond my knowledge. I theorize that I could actually create operators, as can be done in C++, to have the array states subtract from each other and pipe out the result.

The Question is: Given the previous context, how can I pass boolean arrays at optimum speed?

bigcodeszzer
  • 916
  • 1
  • 8
  • 27

3 Answers3

2
enum Command {
    continuing, up, down, left, right, turnQ, turnE, mouse0, mouse
}

void process(EnumSet<Command> commands) {
    while (commands.isEmpty()) {
        ...
    }
    ...
}

Enum is possible. An EnumSet is something optimal like BitSet, and EnumMap like an array.

Otherwise use bit masks and pass an int. Bit operations on int are easy and slightly more efficient.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • Do you recommend EnumSet over bit masks? If so, why? – bigcodeszzer Nov 03 '15 at 16:34
  • I would start with EnumSet. This develops faster, is less error prone, and reads better. IF then you need more speed you can introduce bit masks, named constants and such. Mind that EnumSet already is quite optimal though a primitive type int is indeed better. – Joop Eggen Nov 03 '15 at 16:50
1

A straight conversion of what you've written would be to use a BitSet. It allows you to get the intersection/or, and, andNot of the bits. You could make:

BitSet move = new BitSet(9);
move.set(1); // going up
move.set(2); // also going down?

You can send this relatively packed (EG over a network) by streaming the toByteArray() and reconstructing it on the recieving side with new BitSet(inputByteArray)

How many of these booleans would or should be set true in a valid expression of state?
If we guess one, we can do something simpler. E.G. when continue is true all other values are ignored and it doesn't make sense (to me) to have both up and down or both left and right true. Though it may make sense to have either or both mouse buttons down with the other states and similarly to have something like up and left and turnQ on at the same time the issue can be simplified if we allow one state only.

I.E. If you're representing state, I'd define an enum of the states and consider that done, effectively passing the bool array equivalent as single number, the cardinality of the enum.

public enum SingleAiMoves {
    NONE, STOP,
    LEFT, RIGHT,
    UP, DOWN,
    TURN_Q, TURN_E,
    MOUSE1, MOUSE2
}

That allows only one state at a time.
If you needed to combine multiple states, but only the valid ones, you could define a logical group of states like:

public enum AiMove {
    //         u/d    l/r    q/e    m1     m2     stop
    STOP      (null,  null,  null,  false, false, true),
    NONE      (null,  null,  null,  false, false, false),
    UP        (true,  null,  null,  false, false, false),
    DOWN      (false, null,  null,  false, false, false),
    LEFT      (null,  true,  null,  false, false, false),
    RIGHT     (null,  false, null,  false, false, false),
    TURN_Q    (null,  null,  true,  false, false, false),
    TURN_E    (null,  null,  false, false, false, false),
    MOUSE_1   (null,  null,  null,  true,  false, false),
    MOUSE_2   (null,  null,  null,  false, true,  false),
    MOUSE_1_2 (null,  null,  null,  true,  true,  false),
    UP_LEFT   (true,  true,  null,  false, false, false),
    DOWN_LEFT (false, true,  null,  false, false, false),
    ... // some 108(?) combinations listed here.... ;

    private final Boolean upDown;
    private final Boolean leftRight;
    private final Boolean turnQE;
    private final boolean mouse1;
    private final boolean mouse2;
    private final boolean stop;

    AiMove(Boolean upDown, Boolean leftRight, Boolean turnQE,
           boolean mouse1, boolean mouse2, boolean stop) {
        this.upDown = upDown;
        this.leftRight = leftRight;
        this.turnQE = turnQE;
        this.mouse1 = mouse1;
        this.mouse2 = mouse2;
        this.stop = stop;
    }
    public boolean isStopped() { return stop; }
    public boolean hasUp() { return Boolean.TRUE.equals(upDown); }
    public boolean hasDown() { return Boolean.FALSE.equals(upDown); }
    public boolean hasLeft() { return Boolean.TRUE.equals(leftRight); }
    public boolean hasRight() { return Boolean.FALSE.equals(leftRight); }
    public boolean hasTurnQ() { return Boolean.TRUE.equals(turnQE); }
    public boolean hasTurnE() { return Boolean.FALSE.equals(turnQE); }
    public boolean hasMouse1() { return mouse1; }
    public boolean hasMouse2() { return mouse2; }
}

Personally I think if you go this route you may consider representing more detailed states than some internal controller inputs.

Also you could get fancy with bit flags or masking, or the cleaner looking, non-fancy, EnumSet<SingleAiMoves> but unlike the enum above, and like your array and the BitSet, that would allow states that go both up and down, and/or left and right to exist.

Lastly, because I find writing the enum of supposedly valid combinations tedious and relatively unreadable in my above example, you can use a BitSet inside the enum and clarify the construction of the enum. It probably uses a bit more memory this way.

public enum AiMove {
    NONE      (),
    UP        (0),
    DOWN      (1),
    LEFT      (2),
    RIGHT     (3),
    TURN_Q    (4),
    TURN_E    (5),
    MOUSE_1   (6),
    MOUSE_2   (7),
    STOP      (8),
    MOUSE_1_2 (MOUSE_1, MOUSE_2),
    UP_LEFT   (UP, LEFT),
    DOWN_LEFT (DOWN, LEFT),
    ... // some 108(?) combinations listed here.... ;

    private final BitSet bitsUDLRQE12S = new BitSet(9);

    AiMove(int index) {
        bitsUDLRQE12S.set(index);
    }

    AiMove(AiMove... moves) {
        for (AiMove move : moves) {
            bitsUDLRQE12S.or(move.getBitSet());
        }
    }

    private BitSet getBitSet() { return bitsUDLRQE12S; }

    public boolean hasUp() { return bitsUDLRQE12S.get(0); }
    public boolean hasDown() { return bitsUDLRQE12S.get(1); }
    public boolean hasLeft() { return bitsUDLRQE12S.get(2); }
    public boolean hasRight() { return bitsUDLRQE12S.get(3); }
    public boolean hasTurnQ() { return bitsUDLRQE12S.get(4); }
    public boolean hasTurnE() { return bitsUDLRQE12S.get(5); }
    public boolean hasMouse1() { return bitsUDLRQE12S.get(6); }
    public boolean hasMouse2() { return bitsUDLRQE12S.get(7); }
    public boolean isStopped() { return bitsUDLRQE12S.get(8); }
}
dlamblin
  • 43,965
  • 20
  • 101
  • 140
  • Very clever with the null as a third state, and then left/right. That will shorten the array for sure. Still parsing the rest of the answer. Upvoted. – bigcodeszzer Nov 03 '15 at 17:04
  • Wait... no... the logical operators !=, == are not defined for boolean and null in java. Though I might be able to overload those. – bigcodeszzer Nov 03 '15 at 17:07
  • Correction - apparently you can't overload operators in java. – bigcodeszzer Nov 03 '15 at 17:08
  • @bigcodeszzer The `upDown == true` syntax was bad, yep, sorry about that. I'm changing these to `Boolean.TRUE.equals(upDown)` and the like. – dlamblin Nov 03 '15 at 17:23
0

Java passes by values but in the cases of arrays it passes the reference to the array by value - the individual elements are not passed, only the initial reference to the array.

This means that when passing the Boolean array you're not passing all of the array but merely a value that references the array. For clarity and speed that may be your best bet.

If you're trying to optimize, not on speed but for memory then you may want to consider using java.util.BitSet.

Craig Taylor
  • 1,689
  • 1
  • 11
  • 13