Java AI - 布尔数组的最佳传递

Java AI - Optimum Passing of Boolean Arrays

我正在为游戏编写 AI 对象。

我的目标是优化代码,以便我可以同时处理尽可能多的 AI 进程。

该游戏是 2d 物理游戏,所以我已经在处理碰撞处理中的实体数瓶颈问题,因此 AI 需要高效。

我处理的命令是简单的 ASDF + QE 移动,以及鼠标点击和定位。

根据实际 keys/mouse 模拟 AI 动作可能不是一个好主意,我意识到。不过我想先从这里开始,以后再拆开。

我决定使用布尔数组来表示如下命令:

//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.

我正在苦苦挣扎的是如何将这些数据传递到我实际移动物理体的函数中。我可以简单地在每个游戏迭代中传递一个完整的布尔数组,或者将 /changes/ 从一个迭代传递到另一个迭代。

这种类型的编程桥接到实际的布尔数学,这远远超出了我的知识范围。我推测我实际上可以创建运算符,就像在 C++ 中所做的那样,让数组状态相互减去并输出结果。

问题是:在前面的上下文中,我怎样才能以最佳速度传递布尔数组?

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

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

枚举是可能的。 EnumSet 像 BitSet 一样是最优的,而 EnumMap 像数组。

否则使用位掩码并传递一个整数。 int 上的位操作简单且效率稍高。

Java 按值传递,但在数组的情况下,它按值传递对数组的引用 - 不传递单个元素,仅传递对数组的初始引用。

这意味着在传递布尔数组时,您不会传递整个数组,而只是传递一个引用该数组的值。为了清晰度和速度,这可能是您最好的选择。

如果您尝试优化的不是速度而是内存,那么您可能需要考虑使用 java.util.BitSet。

您所写内容的直接转换是使用 BitSet。它允许您获取 intersection/or, and, andNot 的位。你可以:

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

您可以通过流式传输 toByteArray() 并在接收方使用 new BitSet(inputByteArray)

重建它来发送这个相对打包的(通过网络的 EG)

这些布尔值中有多少会或应该在有效的状态表达式中设置为真?
如果我们猜到一个,我们可以做一些更简单的事情。例如。当 continue 为真时,所有其他值都将被忽略,并且(对我而言)上下或左右都为真没有意义。虽然将一个或两个鼠标按钮与其他状态一起按下可能是有意义的,并且类似地同时打开、向上和向左以及打开 turnQ 之类的东西可能是有意义的,但如果我们只允许一个状态,则可以简化问题。

即如果你代表状态,我会定义一个状态的枚举并考虑完成,有效地传递等效于单个数字的 bool 数组,枚举的基数。

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

一次只允许一个状态。
如果您需要组合多个状态,但只需要组合有效状态,您可以定义一组逻辑状态,例如:

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; }
}

我个人认为,如果你走这条路,你可能会考虑表示比某些内部控制器输入更详细的状态。

你也可以喜欢位标志或掩码,或者看起来更干净,不花哨,EnumSet<SingleAiMoves> 但不像上面的枚举,就像你的数组和 BitSet,那会允许状态同时向上和向下,and/or 向左和向右存在。

最后,因为我发现在我上面的示例中编写假定有效组合的枚举乏味且相对难以阅读,您可以在枚举中使用 BitSet 并阐明枚举的构造。这样可能会占用更多内存。

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); }
}