首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >AI运动逻辑的细化

AI运动逻辑的细化
EN

Code Review用户
提问于 2014-04-10 18:18:07
回答 4查看 1.2K关注 0票数 24

我有下面的类,移动人工智能到给定的工厂,这是很好的工作,但它感觉真的很混乱。

任何关于更好地布局逻辑的输入都将是非常感激的,逻辑遵循这样的思想:我们将从植物位置获取人工智能的x和y位置,如果值为正,则添加30,如果值为负值,则取掉30。

如果你还需要解释逻辑的话,请告诉我。

代码语言:javascript
运行
复制
private void movePosistion(Plant p) {
        /*
         * set which direction to move,the number generated relates to the
         * direction as below:
         *      1   2   3 
         *      4       5
         *      6   7   8
         */
        int xdiff = p.getXpos() - xpos;
        int ydiff = p.getYpos() - ypos;
        if (xdiff > 0){
            if (ydiff > 0){
                //8
                xpos += 30;
                ypos += 30;
            }else if(ydiff < 0){
                //3
                xpos += 30;
                ypos -= 30;
            }else{
                //5
                xpos += 30;
            }
        }else if(xdiff < 0){
            if (xdiff > 0){
                //6
                xpos -= 30;
                ypos += 30;
            }else if(xdiff < 0){
                //1
                xpos -= 30;
                ypos -= 30;
            }else{
                //4
                xpos -= 30;
            }
        }else{
            if (ydiff < 0){
                //7
                ypos -= 30;
            }else{
                //2
                ypos += 30;
            }
        }

        if (xpos > 720)
            xpos = 1;
        if (ypos > 720)
            ypos = 1;
        if (xpos < 1)
            xpos = 720;
        if (ypos < 1)
            ypos = 720;
    }
EN

回答 4

Code Review用户

回答已采纳

发布于 2014-04-10 20:54:13

以下是我的重构:

代码语言:javascript
运行
复制
private void movePosition(Plant p) {
    xpos += Integer.signum(p.getXpos() - xpos) * DELTA_X;
    ypos += Integer.signum(p.getYpos() - ypos) * DELTA_Y;

    xpos = Math.floorMod(xpos, MAX_X);
    ypos = Math.floorMod(ypos, MAX_Y);
}

使用正确的import static,也可以是:

代码语言:javascript
运行
复制
private void movePosition(Plant p) {
    xpos = floorMod(xpos + signum(p.getXpos() - xpos) * DELTA_X, MAX_X);
    ypos = floorMod(ypos + signum(p.getYpos() - ypos) * DELTA_Y, MAX_Y);
}
  1. Signum -- signum实现符号函数,它分别为负整数、零整数和正整数提供-1、0或1。它用一个非常简短和可读的表达式对原始逻辑进行编码。符号乘以适当数量的单位(常量在代码中没有详细说明,顺便说一句)。

原则上,我不反对决策表(见rolfl的回答),但我认为这里没有必要这样做。在他的回答中,palacsint引用了“代码完整”。我也能做到!

代码完整第二版,第19章:一般控制问题,第431页:

使用决策表来替换复杂的条件,有时需要进行涉及多个变量的复杂测试。...

..。有时候你不知道;-)

  1. 模数

通过模数运算可以实现原码的包绕行为.请注意,您不能只在Java中使用%运算符,因为它计算余数,当位置低于零时,余数可能为负数。floorMod操作实际上是计算模块化算法。

现在,你可能会想:这是错误的,原来的代码不是这样工作的!是的,但让我解释一下:

  • 首先,OP的坐标范围为1到720 (都包括在内)。我对此有异议,我故意改变了这里的做法。原因是,在原始代码中,原点不是使用原点为(0,0)的坐标空间,而是在(1,1)处翻译原点。大多数情况下,您最终不得不添加或减1到您的操作。这最终会导致错误的离题。但是,如果坐标为0到719,则用模运算简单地给出环绕逻辑。
  • “但最初的行为并不像模数!”,你可能会说。你为什么这么说?因为,假设x是710,然后加上30:对于模,我返回到20,而使用OP的代码,我会得到1,因为当我们超出界限时,我们返回到最小(或最大)位置。对此,我的答复是,你从来没有在第710位,但只在0,30,60,…,690。至少,这是我从OP的代码中了解到的,在该代码中,对象似乎在网格上移动,而不是随意地移动。我想物体最初的位置总是在30的倍数上,然后只能移动30个单位。如果我错了,那么(1)对不起,(2)是的,模数不是很好的答案;您最好使用rolfl中的boundPos函数。
票数 15
EN

Code Review用户

发布于 2014-04-10 18:44:50

预先计算可能的移动,并将条件简化为一个简单的数学表达式是解决此类问题的好方法。

您的代码还会受到重复块和重复逻辑的影响:干-不要重复自己。

通过函数提取来解决这一问题。

最后,您的代码充满了神奇的数字,这些数字很容易泄漏错误……

考虑下面的代码,虽然在行方面比您的代码更长,但实际上可读性要高得多(我承认,除了复杂的2D数组之外)。

代码语言:javascript
运行
复制
private static final int DELTA = 30;
private static final int MINPOS = 0;
private static final int MAXPOS = 720;
private static final int XINDEX = 0;
private static final int YINDEX = 1;

private final int[][] MOVES = {
        {-DELTA, DELTA}, {0, DELTA}, {DELTA,DELTA},
        {-DELTA, 0},     {0, 0},     {DELTA,0},
        {-DELTA, -DELTA},{0, -DELTA},{DELTA,-DELTA},

};

private final int step(final int diff) {
    return diff < 0 ? 0 : (diff > 0 ? 2 : 1);
}

private final int boundPos(int pos) {
    return pos < MINPOS ? MAXPOS : (pos > MAXPOS ? MINPOS : pos);
}

private void movePosistion(Plant p) {
    /*
     * set which direction to move,the number generated relates to the
     * direction as below:
     *      1   2   3 
     *      4       5
     *      6   7   8
     */

    int xdiff = step(p.getXpos() - xpos);
    int ydiff = step(p.getYpos() - ypos);
    // Convert the two x/y directions to an index in to MOVES.
    int index = ydiff * 3 + xdiff;
    xpos += MOVES[index][XINDEX];
    ypos += MOVES[index][YINDEX];
    xpos = boundPos(xpos);
    ypos = boundPos(ypos);
}
票数 11
EN

Code Review用户

发布于 2014-04-10 19:17:07

我同意@rolfl,您需要提取方法并使用决策表

代码完整第二版,第19章:一般控制问题,第431页:

使用决策表来替换复杂的条件,有时需要进行涉及多个变量的复杂测试。使用决策表执行测试可能会有帮助,而不是使用ifs或case。决策表查找一开始更容易编码,只有几行代码,没有复杂的控制结构。这种复杂性的最小化减少了出错的机会。如果数据发生更改,则可以更改决策表而不更改代码;只需更新数据结构的内容即可。

为了可读性,我在这里会更详细。首先,对可能存在的差异作一说明:

代码语言:javascript
运行
复制
public enum Difference {
    ZERO,
    BELOW_ZERO,
    ABOVE_ZERO
}

然后是包含可用规则的类:

代码语言:javascript
运行
复制
public class MoveLogic {

    private final Map<Rule, Move> rules = new LinkedHashMap<>();

    public MoveLogic() {
        addRule(Difference.ABOVE_ZERO, Difference.ABOVE_ZERO, 30, 30); // 8
        addRule(Difference.ABOVE_ZERO, Difference.BELOW_ZERO, 30, -30); // 3
        addRule(Difference.ABOVE_ZERO, Difference.ZERO, 30, 0); // 5

        addRule(Difference.BELOW_ZERO, Difference.ABOVE_ZERO, -30, 30); // 6
        addRule(Difference.BELOW_ZERO, Difference.BELOW_ZERO, -30, -30); // 1
        addRule(Difference.BELOW_ZERO, Difference.ZERO, -30, 0); // 4

        addRule(Difference.ZERO, Difference.BELOW_ZERO, 0, -30); // 7
        addRule(Difference.ZERO, Difference.ABOVE_ZERO, 0, 30); // 2
    }

    private void addRule(Difference xDifference, Difference yDifference, int x, int y) {
        rules.put(new Rule(xDifference, yDifference), new Move(x, y));
    }

    public Move getMove(Difference xDifference, Difference yDifference) {
        return rules.get(new Rule(xDifference, yDifference));
    }
}

用法:

代码语言:javascript
运行
复制
final Difference xDifference = calculateDifference(plant.getXpos(), xpos);
final Difference yDifference = calculateDifference(plant.getXpos(), ypos);

final Move move = moveLogic.getMove(xDifference, yDifference);
xpos += move.getX();
ypos += move.getY();

最后,创建Difference引用的助手方法:

代码语言:javascript
运行
复制
private Difference calculateDifference(final int plantPosition, final int position) {
    final int difference = plantPosition - position;
    if (difference > 0) {
        return Difference.ABOVE_ZERO;
    }
    if (difference < 0) {
        return Difference.BELOW_ZERO;
    }
    return Difference.ZERO;
}

以及Rule

代码语言:javascript
运行
复制
public class Rule {

    private Difference xDifference;
    private Difference yDifference;

    public Rule(Difference xDifference, Difference yDifference) {
        this.xDifference = xDifference;
        this.yDifference = yDifference;
    }

    // generate hashCode and equals, the Map needs it!

}

Move

代码语言:javascript
运行
复制
public class Move {

    private int x;
    private int y;

    public Move(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

它可能看起来过于冗长,但它避免重复的条件,以及神奇的计算与索引和数组。

票数 11
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/46834

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档