戦車戦争をしましょう!


18

戦車戦争をしましょう!

レーザーでそれら破壊することに部分的に触発された

目的

あなたの仕事は戦車を制御することです。2D戦場で動き回り、他の戦車や障害物を撃ちます。最後の戦車が勝者になります!

マップ形式

あなたの戦車は、単位正方形のnby nグリッドに基づく2Dフィールドにあります。n提出物の数に基づいて何を決定します。各正方形には、次のうち1つのみを含めることができます。

  • タンク
  • 岩石
  • なし

すべての障害物と戦車はそのスペースを完全に埋め、それらが命中したすべてのショットがさらに物事にダメージを与えないようにします。

#= tank を使用したフィールドの例を次に示します。T= tree; R=ロック; W=壁; .== n10で何もない

.....#....
..T....R..
WWW...WWWW
W......T..
T...R...Ww
W...W.....
W....W...T
WWWWWW...R
W.........
WWWWWWRT..

座標は、左から右に増加し、下から上x, yx増加する形式ですy。左下のスペースには座標があり0, 0ます。各タンクは空いているスペースに移動して、あらゆる方向に射撃できます。

マップダイナミクス

あなたの戦車は他の戦車を撃つだけではありません!マップ上で何かを撮影すると、事態が発生する可能性があります。

  • 壁が撃たれた場合、1から4の範囲のいくつかのショットの後、壁は破壊されます。
  • 木が撃たれた場合、すぐに破壊されます
  • 岩が撃たれた場合、そのショットはそれを通り過ぎ、次に当たるものにダメージを与えます

何かが破壊されると、それはもはやマップ上にありません(何も置き換えられません)。ショットが障害物を破壊した場合、それはブロックされ、そのパスに沿ってそれ以上の損傷はありません。

タンクダイナミクス

各タンクはlife= 100で始まります。タンクでの各ショットは、life距離に基づいて20〜30減少します。これはdelta_life=-30+(shot_distance*10/diagonal_map_length)diagonal_map_length(n-1)*sqrt(2))で計算できます。さらに、各戦車はlife1ターンにつき1回再生します。

ターン

いくつかのラウンドが実行されます(提出したら決定します)。すべてのラウンドの開始時に、マップがランダムに生成され、タンクがランダムな空の場所に配置されます。すべてのラウンドで、各タンクには任意の順序でターンが与えられます。すべての戦車にターンが与えられた後、同じ順番で再びターンが与えられます。ラウンドは、タンクが1つだけになるまで続きます。その戦車が勝者となり、1ポイントを獲得します。その後、ゲームは次のラウンドに進みます。

すべてのラウンドが実行されたら、この質問のスコアを投稿します。

戦車のターン中、次のいずれかを実行できます

  • 水平または垂直のいずれかの方向に最大3スペース移動します。タンクが障害物または別のタンクによってブロックされている場合、障害物またはタンクを通過せずに可能な限り移動されます。
  • 浮動小数点の角度(度単位)で表されるある方向で撮影します。戦車のローカルスペース(水平に左から右、別名東またはTurnAction.Direction.EAST)のx軸は0度で、角度は反時計回りに増加します。ショットは不正確であり、ショットの実際の角度は、選択した角度より5度大きい場合も小さい場合もあります。
  • 何もしない。

ターンは時間に制限はありませんが、これはすべてを掛けるために意図的に時間を浪費できるという意味ではありません。

提出/プロトコル

提出された各プログラムは、フィールド上の1つのタンクを制御します。制御プログラムはJavaであるため、現時点ではプログラムはJavaである必要があります(おそらく他の言語のラッパーを何らかの時点で作成するか、独自の言語を作成することもできます)。

プログラムTankは、次のメソッドを持つインターフェイスを実装します。

public interface Tank {
    // Called when the tank is placed on the battlefield.
    public void onSpawn(Battlefield field, MapPoint position);
    // Called to get an action for the tank on each turn.
    public TurnAction onTurn(Battlefield field, MapPoint position, float health);
    // Called with feedback after a turn is executed.
    // newPosition and hit will be populated if applicable.
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit);
    // Called when the tank is destroyed, either by another tank,
    // or because the tank won. The won parameter indicates this.
    public void onDestroyed(Battlefield field, boolean won);
    // Return a unique name for your tank here.
    public String getName();
}

Battlefieldクラス(オブジェクトの2Dアレイ含まBattlefield.FIELD_SIZEによってBattlefield.FIELD_SIZE戦場でのものを表します)。Battlefield.getObjectTypeAt(...)与えるFieldObjectType指定された座標(のいずれかで目的のためにFieldObjectType.ROCKFieldObjectType.TREEFieldObjectType.TANKFieldObjectType.WALL、またはFieldObjectType.NOTHING)。マップの範囲外のオブジェクト(座標<0または> = Battlefield.FIELD_SIZE)を取得しようとすると、IllegalArgumentExceptionがスローされます。

MapPointマップ上のポイントを指定するためのクラスです。使用MapPoint.getX()してMapPoint.getY()座標にアクセスします。

編集:いくつかのユーティリティメソッドが追加されましたMapPoint.distanceTo(MapPoint)MapPoint.angleBetween(MapPoint)Battlefield.find(FieldObjectType)、およびTurnAction.createShootActionRadians(double)によって示唆されているようにWasmoo

詳細については、javadocsを参照してください。以下のセクションを参照してください。

すべての(パブリックAPI)クラスはパッケージの下にありますzove.ppcg.tankwar

制御プログラム

制御プログラムとタンクAPIの完全なソースとjavadocは、私のGitHubリポジトリで見つけることができます:https : //github.com/Hungary-Dude/TankWarControl

バグが見つかった場合や改善が必要な場合は、プルリクエストやコメントを送信してください。

私は2つのサンプルタンクプログラムを作成RandomMoveTankしましたRandomShootTank(その名前がす​​べてを物語っています)。

タンクを実行するには、完全修飾(パッケージ名+クラス名)タンククラスをtanks.list(行ごとに1つのクラス)に追加し、必要に応じて設定を編集しますzove.ppcg.tankwar.Control(遅延を有効にする、フィールドのGUI表現を表示するかどうかなど)。実行しzove.ppcg.tankwar.Controlます。リストに少なくとも2つのタンクがあることを確認してください。そうでない場合、結果は未定義です。(必要に応じてサンプルタンクを使用します)。

あなたのプログラムは、この制御プログラムの下で私のマシンで実行されます。ソースを作成したら、ソースへのリンクを含めます。ソースの編集を自由に提案してください。

ルール

  • 投稿は上記のガイドラインに従う必要があります
  • あなたのプログラムがファイルシステム、ネットワークにアクセスしたり、私のマシンを攻撃したりすることは決してありません
  • あなたのプログラムは、私の制御プログラムを悪用して不正行為を試みることはできません。
  • トローリングなし(プログラムを意図的にすべての電話を切るのに時間を浪費させるなど)
  • 複数の提出がある場合があります
  • 投稿で創造的になってみてください!
  • 私はプログラムをallow意的に許可または許可しない権利を留保します

幸運を!

更新:壁のテレポーテーションのバグを修正し、再生成を実装した後、現在の提出を100ラウンド実行しました。Battlefield.FIELD_SIZE = 30

更新2: Groovyを少しだまして、新しいサブミッションRunTankを追加しました...

更新された結果:

+-----------------+----+
| RandomMoveTank  | 0  |
| RandomShootTank | 0  |
| Bouncing Tank   | 4  |
| Richard-A Tank  | 9  |
| Shoot Closest   | 19 |
| HunterKiller 2  | 22 |
| RunTank         | 23 |
| Dodge Tank      | 24 |
+-----------------+----+

現在、戦車は1ターンにつき1ライフ回復します。それを増やすべきですか?


1
なぜMapPointさんxy floats?彼らはそうではありませんintsか?
IchBinKeinBaum

いい視点ね。なぜそれらをフロートにすることにしたのか分かりません。それらをintに変更します。編集:それらをintに更新し、レポを確認します
DankMemes

ポイント1,1に立って0度の角度で撮影すると、発射体は東方向に移動しますよね?
CommonGuy 14

@Manuはい。それがはっきりしていなかったらごめんなさい。
DankMemes 14

いくつかのエラーが見つかりました
。Battlefield.java:88

回答:


2

ハンターキラー

このインテリジェントハンターは、正確に1つのターゲットをきれいに射撃できる安全な位置を見つけようとします。(したがって、1つのターゲットのみがそれを撃つことができます)

カバーが多い場合に最適です。

import zove.ppcg.tankwar.*;
import java.util.*;
public final class HunterKiller implements Tank {

    private final int MAX_DEPTH = 2;
    private Battlefield field;
    private List<MapPoint> enemies;
    private MapPoint me;
    private HashMap<MapPoint, MoveNode> nodeMap = new HashMap();

    //A description of how safe the position is from each tank in enemies
    private class Safety extends java.util.ArrayList<Double> {
        public int threats;
        public Safety(MapPoint position) {
            for (MapPoint p : enemies) {
                int obstacles = countObstacles(position, p, false);
                if (obstacles > 0) {
                    add((double) obstacles);
                } else {
                    add(missChance(position.distanceTo(p)));
                    threats++;
                }
            }
        }
    };

    //A description of a move
    private class Move {

        public TurnAction.Direction direction;
        public int distance;
        public MapPoint point;

        public Move(TurnAction.Direction direction, int distance, MapPoint point) {
            this.direction = direction;
            this.distance = distance;
            this.point = point;
        }

        public TurnAction action() {
            return TurnAction.createMoveAction(direction, distance);
        }

        @Override
        public String toString() {
            return direction + " " + distance;
        }
    }

    /**
     * A MoveNode holds a point and all the moves available from that point.
     * The relative safety of the node can be calculated as a function
     * of its depth; ie. this position is safe because we can can move to safety
     */
    private class MoveNode {

        MapPoint point;
        ArrayList<Move> moves;
        ArrayList<Safety> safetyArray = new ArrayList();

        public MoveNode(MapPoint point) {
            this.point = point;
            this.moves = getMoves(point);
        }

        public Safety getSafety(int depth) {
            if (safetyArray.size() <= depth) {
                Safety value;
                if (depth == 0 || this.moves.isEmpty()) {
                    value = new Safety(point);
                } else {
                    ArrayList<Safety> values = new ArrayList();
                    for (Move m : moves) {
                        MoveNode n = nodeMap.get(m.point);
                        if (n == null) {
                            n = new MoveNode(m.point);
                            nodeMap.put(n.point, n);
                        }
                        values.add(n.getSafety(depth - 1));
                    }
                    Collections.sort(values, cmp);
                    value = values.get(0);
                }
                safetyArray.add(depth, value);
            }
            return safetyArray.get(depth);
        }
    }

    /**
     * Find all the points between here and there, excluding those points
     */
    private java.util.ArrayList<MapPoint> path(final MapPoint p1, MapPoint p2) {
        java.util.ArrayList<MapPoint> ret = new ArrayList();
        float tankX = p1.getX();
        float tankY = p1.getY();
        double angle = p1.angleBetweenRadians(p2);
        double maxDistance = p1.distanceTo(p2);
        for (int x = 0; x < Battlefield.FIELD_SIZE; x++) {
            for (int y = 0; y < Battlefield.FIELD_SIZE; y++) {
                float x2 = (float) (((x - tankX) * Math.cos(-angle)) - ((y - tankY) * Math.sin(-angle)));
                float y2 = (float) (((x - tankX) * Math.sin(-angle)) + ((y - tankY) * Math.cos(-angle)));
                if (x2 > 0 && y2 >= -0.5 && y2 <= 0.5) {
                    MapPoint p = new MapPoint(x, y);
                    if (maxDistance > p1.distanceTo(p)) {
                        ret.add(p);
                    }
                }
            }
        }
        Collections.sort(ret, new Comparator<MapPoint>() {
            @Override
            public int compare(MapPoint o1, MapPoint o2) {
                return (int) Math.signum(p1.distanceTo(o2) - p1.distanceTo(o1));
            }
        });
        return ret;
    }

    /**
     * Find the number of obstacles between here and there, excluding those
     * points
     */
    private int countObstacles(MapPoint p1, MapPoint p2, boolean countRocks) {
        java.util.ArrayList<MapPoint> points = path(p1, p2);
        int count = 0;
        for (MapPoint p : points) {
            Object obj = field.getObjectTypeAt(p);
            if (FieldObjectType.NOTHING.equals(obj)
                    || (!countRocks && FieldObjectType.ROCK.equals(obj))
                    || (!countRocks && FieldObjectType.TANK.equals(obj))
                    || p.equals(me)) {
                count += 0;
            } else {
                count += 1;
            }
        }
        return count;
    }

    /**
     * Returns a value between 1.0 and 0.0, where 1.0 is far and 0.0 is close
     */
    private double missChance(double distance) {
        return distance / Battlefield.DIAGONAL_FIELD_SIZE;
    }

    //Returns a list of valid moves from the given position
    private ArrayList<Move> getMoves(MapPoint position) {
        ArrayList<Move> ret = new ArrayList();
        for (TurnAction.Direction d : TurnAction.Direction.values()) {
            for (int i = 1; i <= 3; i++) {
                MapPoint p = d.translate(position, i);
                try {
                    FieldObjectType t = field.getObjectTypeAt(p);
                    if (t != FieldObjectType.NOTHING && !p.equals(me)) {
                        break;
                    }
                    ret.add(new Move(d, i, p));
                } catch (IllegalArgumentException ex) {
                    //Can't move off the map...
                    break;
                }
            }
        }
        return ret;
    }

    //Compares two safeties with a preference for exactly 1 threat
    private Comparator<Safety> cmp = new Comparator<Safety>() {
        @Override
        public int compare(Safety o1, Safety o2) {
            int tc1 = o1.threats;
            int tc2 = o2.threats;
            //Prefer 1 threat
            if (tc2 == 1 && tc1 != 1) {
                return 1;
            }
            if (tc2 != 1 && tc1 == 1) {
                return -1;
            }

            //Prefer fewer threats
            if (tc2 != tc1) {
                return tc1 - tc2;
            }

            //We're splitting hairs here
            //Determine the least safe option
            int ret = -1;
            double s = Double.MAX_VALUE;
            for (Double d : o1) {
                if (d < s) {
                    s = d;
                }
            }
            for (Double d : o2) {
                if (d < s) {
                    ret = 1;
                    break;
                }
                if (d == s) {
                    ret = 0;
                }
            }
            if (tc1 > 1) {
                //Prefer the safest
                return -ret;
            } else {
                //Prefer the least safest
                return ret;
            }
        }
    };

    @Override
    public TurnAction onTurn(Battlefield field, MapPoint position, float health) {
        //Find all the tanks
        List<MapPoint> enemies = field.find(FieldObjectType.TANK);
        enemies.remove(position);
        if (enemies.isEmpty()) {
            return TurnAction.createNothingAction();
        }

        //Set the constants needed for this turn
        this.enemies = enemies;
        this.field = field;
        this.me = position;

        //Create a new NodeMap
        MoveNode n = new MoveNode(position);
        this.nodeMap.clear();
        this.nodeMap.put(position, n);

        //Find the "best" safety within MAX_DEPTH moves
        int depth = 0;
        Safety safety = n.getSafety(0);
        for (depth = 0; depth < MAX_DEPTH; depth++) {
            int lastThreat = safety.threats;
            safety = n.getSafety(depth);
            int newThreat = safety.threats;
            if (newThreat == 1) {
                //Always prefer 1 threat
                break;
            }
            if (depth != 0 && lastThreat - newThreat >= depth) {
                //Prefer fewer threats only if we are much safer;
                //  Specifically, don't move twice for only 1 less threat
                break;
            }
        }

        //Depth == 0         : Only 1 threat; best position
        //Depth == MAX_DEPTH : Many or no threats, but no good moves available
        if (depth > 0 && depth < MAX_DEPTH) {
            //Move towards the better spot
            for (Move m : n.moves) {
                if (nodeMap.get(m.point).getSafety(depth - 1) == safety) {
                    return m.action();
                }
            }
        }

        //We're in a good position, shoot now
        //Calculate tank with most threat
        MapPoint threat = null;
        double biggestThreat = Double.MAX_VALUE;
        for (MapPoint p : enemies) {
            double t = missChance(position.distanceTo(p)) + countObstacles(position, p, false);
            if (t < biggestThreat) {
                biggestThreat = t;
                threat = p;
            }
        }

        return TurnAction.createShootActionRadians(position.angleBetweenRadians(threat));
    }
    public void onDestroyed(Battlefield field, boolean won) {}
    public void onSpawn(Battlefield field, MapPoint position) {}
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit) {}
    public String getName() {
        return "HunterKiller " + MAX_DEPTH;
    }

}

以上です。費やした。


2

この簡単な戦車は、最も近い敵戦車を見つけて撃ちます。いいだろうならばfinddistanceangleに建設された、そして場合createShootActionラジアンで(すなわちの結果をダブル受け入れangle

編集:新しいユーティリティメソッドを含めるためにクラスを書き直しました

public final class ShootClosestTank implements Tank {

    @Override
    public TurnAction onTurn(Battlefield field, MapPoint position, float health) {
        //Find all the tanks
        List<MapPoint> tanks = field.find(FieldObjectType.TANK);
        tanks.remove(position);

        if (tanks.isEmpty()) return TurnAction.createNothingAction();

        //Calculate closest tank
        MapPoint cPoint = null;
        double cDist = Double.POSITIVE_INFINITY;
        for (MapPoint p : tanks) {
            double dist = position.distanceTo(p);
            if (dist < cDist) {
                cDist = dist;
                cPoint = p;
            }
        }

        //Shoot at closest tank
        return TurnAction.createShootActionRadians(position.angleBetweenRadians(cPoint));

    }

    @Override
    public void onDestroyed(Battlefield field, boolean won) {
        //Sucks to be me
    }

    @Override
    public void onSpawn(Battlefield field, MapPoint position) {
        //No setup
    }

    @Override
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit) {
        //Nothing to update
    }

    @Override
    public String getName() {
        return "Shoot Closest";
    }
}

Would be nice if find, distance, and angle were built in, and if createShootAction accepted a double in radians (i.e. the result of angle)-素晴らしいアイデア、私はそれを追加します。
DankMemes

1

私はこれがあまり得意ではありませんが、私はそれを試してみると思います、あなたが知っているように、練習とか。

私の戦車は、ランダムに移動または射撃を決定します。撮影を決定すると、最も近い利用可能なターゲットで撮影を試みます。

package com.richarda.tankwar;

import zove.ppcg.tankwar.*;

import java.util.Random;
import java.util.ArrayList;


public class RichardATank implements Tank
{
    private String name;

    public RichardATank()
    {
        this.name = "Richard-A Tank";
    }

    /**
     *
     * @param field
     *            The current battlefield, complete with all obstacle and tank
     *            positions
     * @param position
     */
    @Override
    public void onSpawn(Battlefield field, MapPoint position)
    {

    }

    /**
     * The tank will randomly move around and occasionally shoot at the nearest available target.
     *
     * @param field
     *            The current battlefield, complete with all obstacle and tank
     *            positions
     * @param position
     *            The tank's current position
     * @param health
     *            The tank's current health
     * @return
     */
    @Override
    public TurnAction onTurn(Battlefield field, MapPoint position, float health)
    {
        Random r = new Random();
        int n = r.nextInt(2);

        if(n == 1)
        {
            return this.tryShootAtNearestTank(field, position);
        }

        return TurnAction.createMoveAction(TurnAction.Direction.getRandom(), r.nextInt(2) + 1);
    }

    /**
     *
     * @param newPosition
     *            The tank's new position (may not be changed)
     * @param hit
     *            What the tank hit, if it decided to shoot
     */
    @Override
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit)
    {

    }

    /**
     *
     * @param field
     *            The battlefield
     * @param won
     */
    @Override
    public void onDestroyed(Battlefield field, boolean won)
    {

    }

    @Override
    public String getName()
    {
        return this.name;
    }

    /**
     * Try and shoot at the nearest tank
     *
     * @param bf The battlefield
     * @param curTankLocation The current tank's location
     * @return TurnAction the shoot action to the nearest tank
     */
    private TurnAction tryShootAtNearestTank(Battlefield bf, MapPoint curTankLocation)
    {
        MapPoint nearestTankLoc = this.getNearestTankLocation(bf, curTankLocation);

        double firingAngle = curTankLocation.angleBetween(nearestTankLoc);

        return TurnAction.createShootAction((float) firingAngle);
    }

    /**
     * Try to find the nearest tank's location
     *
     * @param bf The battlefield
     * @param curTankLocation The current tank's location
     * @return MapPoint The location of the nearest tank
     */
    private MapPoint getNearestTankLocation(Battlefield bf, MapPoint curTankLocation)
    {
        ArrayList<MapPoint> enemyTankLocations = this.getEnemyTanksOnField(bf, curTankLocation);

        MapPoint nearestTankLoc = null;

        for(MapPoint enemyTankLoc : enemyTankLocations)
        {
            if(nearestTankLoc == null || curTankLocation.distanceTo(enemyTankLoc) < curTankLocation.distanceTo(nearestTankLoc))
            {
                nearestTankLoc = enemyTankLoc;
            }
        }

        return nearestTankLoc;
    }

    /**
     * Get all enemy tanks on the field
     *
     * @param bf The battlefield
     * @param curTankLocation The current tank's location
     * @return ArrayList<MapPoint> A list with all enemy tanks in it
     */
    private ArrayList<MapPoint> getEnemyTanksOnField(Battlefield bf, MapPoint curTankLocation)
    {
        int maxSize = Battlefield.FIELD_SIZE;
        ArrayList<MapPoint> tanks = new ArrayList<MapPoint>();

        for(int i = 0; i < maxSize; i++)
        {
            for(int j = 0; j < maxSize; j++)
            {
                FieldObjectType objType = bf.getObjectTypeAt(i, j);

                if(objType == FieldObjectType.TANK)
                {
                    MapPoint tankLocation = new MapPoint(i, j);

                    if(!tankLocation.equals(curTankLocation))
                    {
                        tanks.add(tankLocation);
                    }
                }
            }
        }

        return tanks;
    }
}

制御プログラムを含む完全なコードはここにあります


試してみてくださいDirection.getRandom()
DankMemes

@ZoveGamesが編集しました。ヒントをありがとう。
MisterBla 14

1

ダッジタンク

この戦車は最も近い戦車を撃ちます。時々、その健康状態と前回の移動に応じて、レーザーをかわそうとするために最も近いタンクに垂直に移動しようとします。

public class DodgeTank implements Tank {

private int lastMove;
@Override
public void onSpawn(Battlefield field, MapPoint position){
    //nada
}
@Override
public TurnAction onTurn(Battlefield field, MapPoint position, float health){
    List<MapPoint> tanks = field.find(FieldObjectType.TANK);
    tanks.remove(position);
    MapPoint nearest= new MapPoint(0,0);
    double dis=Double.POSITIVE_INFINITY;
        for (MapPoint tank: tanks){
            double distance=tank.distanceTo(position);
            if (distance<dis){
                nearest=tank;
                dis=distance;
            }
        }
    if (lastMove*Math.random()+(4-health/25)<5){//Attack
        lastMove++;
        return TurnAction.createShootActionRadians(position.angleBetweenRadians(nearest));
    }
    else {
        lastMove=0;
        double cumulativeAngle=position.angleBetweenRadians(nearest);
        for (MapPoint tank : tanks){
            cumulativeAngle+=position.angleBetweenRadians(tank);
        }
        cumulativeAngle/=tanks.size();
        cumulativeAngle/=(Math.PI/2);
        int dir=(int)Math.floor(cumulativeAngle);
        TurnAction move;
        switch (dir){
            case 0:
                if (position.getX()>2&&field.getObjectTypeAt(position.cloneAndTranslate(-3, 0)).equals(FieldObjectType.NOTHING)){
                    return TurnAction.createMoveAction(TurnAction.Direction.WEST, 3);
                }
                return TurnAction.createMoveAction(TurnAction.Direction.EAST, 3);
            case 1: 
                if (position.getY()>2&&field.getObjectTypeAt(position.cloneAndTranslate(0, -3)).equals(FieldObjectType.NOTHING)){
                    return TurnAction.createMoveAction(TurnAction.Direction.SOUTH, 3);
                }
                return TurnAction.createMoveAction(TurnAction.Direction.NORTH, 3);
            case -1:
                if ((position.getY()<(Battlefield.FIELD_SIZE-4))&&field.getObjectTypeAt(position.cloneAndTranslate(0,3)).equals(FieldObjectType.NOTHING)){
                    return TurnAction.createMoveAction(TurnAction.Direction.NORTH, 3);
                }
                return TurnAction.createMoveAction(TurnAction.Direction.SOUTH, 3);
            default:
                if ((position.getX()<(Battlefield.FIELD_SIZE-4))&&field.getObjectTypeAt(position.cloneAndTranslate(3,0)).equals(FieldObjectType.NOTHING)){
                    return TurnAction.createMoveAction(TurnAction.Direction.EAST, 3);
                }
                return TurnAction.createMoveAction(TurnAction.Direction.WEST, 3);
        }
    }
}
@Override
public void turnFeedback(MapPoint newPosition, FieldObjectType hit){
    //Nada
}
@Override
public void onDestroyed(Battlefield field, boolean won){
  //if (won) this.taunt();
  //else this.selfDestruct();
}
@Override
public String getName(){
    return "Dodge Tank";
}
}

1

これは思ったよりずっと複雑だった。

これはgroovyの私のエントリです、groovyをインストールしてコンパイルする必要があります

groovyc zove\ppcg\tankwar\felsspat\RunTank.groovy

呼び出すには、$ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar(または任意のバージョン)をクラスパスに追加する必要があります。

インストールしない場合は、コンパイル済みの.classファイルとライブラリを送信できます。

戦車が他の車両を見ることができない状況があるようです。それが意図されているかどうかはわかりません。テスト中にデッドロックが発生しました。

とにかくRunTankは次のとおりです。RunTankは、最も近いタンクに最も近いタンクであるか、複数のタンクがFIELD_SIZE / 3内にある場合、最も近いタンクの反対方向に大胆に前進します。

package zove.ppcg.tankwar.felsspat

import zove.ppcg.tankwar.Battlefield
import zove.ppcg.tankwar.FieldObjectType
import zove.ppcg.tankwar.MapPoint
import zove.ppcg.tankwar.Tank
import zove.ppcg.tankwar.TurnAction
import zove.ppcg.tankwar.TurnAction.Direction

public class RunTank implements Tank {  

    @Override
    public TurnAction onTurn(Battlefield field, MapPoint position, float health) {
        def targets = (field.find(FieldObjectType.TANK) - position).sort{ position.distanceTo(it) }

        if (targets) {

            def runDistance = (Battlefield.FIELD_SIZE / 3).toInteger()

            def closeTargets = targets.grep {
                position.distanceTo(it) < runDistance
            }

            if (position.distanceTo(closestEnemy(position, field)) < targets.first().distanceTo(closestEnemy(targets.first(), field))) {
                return run(field, position, targets)
            }           

            if (closeTargets.size() > 1) {
                return run(field, position, targets)
            } else  {
                return shootEnemy(position, targets)
            }
        } else {
            println "WTF! Targets: ${field.find(FieldObjectType.TANK)} + Position ${position}"
            return TurnAction.createMoveAction(Direction.getRandom(), 1);
        }

    }

    def shootEnemy(position, targets) {
        return TurnAction.createShootActionRadians(position.angleBetweenRadians(targets.first()))   
    }

    def run(field, position, targets) {
        def freePositions = (field.find(FieldObjectType.NOTHING) - position).grep { position.distanceTo(it) <= 3.0 && [0d, 90d, 180d, 270d].contains(position.angleBetween(it))}.sort{position.distanceTo(it)}      
        def availablePositions = []
        freePositions.each { targetPosition ->          
            def positions = getPositionsBetween(position,targetPosition)
            if (! positions || positions.every { it.equals(FieldObjectType.NOTHING) }) {
                availablePositions.add(targetPosition)  
            }                   
        }
        availablePositions = availablePositions.sort{closestEnemy(it, field)}.reverse()

        if (availablePositions) {
            def targetPosition = availablePositions.first()
            if (targetPosition.distanceTo(closestEnemy(targetPosition, field)) > position.distanceTo(closestEnemy(position, field))) { // Don't move closer to an enemy
                return moveTo(position, targetPosition)
            } else {
                return shootEnemy(position, targets)
            }       
        } else {
            return shootEnemy(position, targets)
        }
    }

    def moveTo(position, targetPosition) {
        def distance = position.distanceTo(targetPosition).toInteger()
        switch (position.angleBetween(targetPosition)) {
            case 0d:
                return TurnAction.createMoveAction(TurnAction.Direction.NORTH, distance)
                break

            case 90d:
                return TurnAction.createMoveAction(TurnAction.Direction.EAST, distance)
                break

            case 180d:
                return TurnAction.createMoveAction(TurnAction.Direction.SOUTH, distance)
                break

            case 270d:
                return TurnAction.createMoveAction(TurnAction.Direction.West, distance)
                break           

            default:
            println "I'm stupid :("

        }
    }

    def closestEnemy(position, field) {
        return field.find(FieldObjectType.TANK).sort { position.distanceTo(it) }.first()
    }

    def getPositionsBetween(self, target) {
        def positions = []
        if(self.x == target.x) {
            for (y in self.y..target.y) {
                positions.add(new MapPoint(self.x, y))
            }
        } else {
            for (x in self.x..target.x) {
                positions.add(new MapPoint(x, self.y))
            }
        }
        return positions - self - target
    }

    @Override
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit) {
    }

    @Override
    public void onDestroyed(Battlefield field, boolean won) {
        println ":("
    }

    @Override
    public void onSpawn(Battlefield field, MapPoint position) {
        println "Go!"
    }

    @Override
    public String getName() {
        return "RunTank";
    }   
}

提案が1つあります。タンクに色を追加し、それを実装する方法です。また、GUIではラベルがいいでしょう:)


def RandomMoveTank() {}-それはそこにあることを意図していますか?(私はグルーヴィーを知らない)
DankMemes 14

いいえ、RandomMoveTankをコピーしてコンストラクターを削除するのを忘れました、ありがとう:)
Fels 14

コードをコンパイルし、.classファイルとgroovy jarを含むフォルダーをプロジェクトのクラスパスに追加しました。反射が働いた!更新されたスコアを投稿しました。あなたのタンクはかなりうまくやった:)
DankMemes 14

1
いいね!そして、あなたはダッジタンクを気にします!
フェルズ14

1

これはShoot-Closestのバリエーションであり、1ターンおきに、それができなくなるまで方向に移動します。1ターンおきに撃ちます。

便利なユーティリティがありpath、2つのポイント間のすべてのポイント(つまりオブジェクト)を識別するために使用できます。

public final class BouncingTank implements Tank {

    /**
     * Find all the points between here and there, excluding those points
     * @param p1 Here
     * @param p2 There
     * @return 
     */
    private java.util.ArrayList<MapPoint> path(MapPoint p1, MapPoint p2) {
        double dist = p1.distanceTo(p2);
        double dx = (p2.getX() - p1.getX()) / dist;
        double dy = (p2.getY() - p1.getY()) / dist;

        java.util.ArrayList<MapPoint> ret = new java.util.ArrayList();
        MapPoint lastP = null;
        for (int i = 0; i < dist; i++) {
            MapPoint p = p1.cloneAndTranslate((int)(i*dx), (int)(i*dy));
            if (p.equals(p1) || p.equals(lastP)) continue;
            if (p.equals(p2)) break;
            ret.add(p);
            lastP = p;
        }
        return ret;
    }

    /**
     * Find the number of legal moves in the given direction
     * @param field
     * @param position
     * @param dir
     * @return 
     */
    private int findMoves(Battlefield field, MapPoint position, TurnAction.Direction dir) {
        if (dir == null) return -1;
        int count = 0;
        MapPoint dest = dir.translate(position, Battlefield.FIELD_SIZE);
        java.util.ArrayList<MapPoint> obs = path(position, dest);
        for (MapPoint oMP : obs) {
            try {
                if (FieldObjectType.NOTHING.equals(field.getObjectTypeAt(oMP))) {
                    count++;
                } else {
                    break;
                }
            } catch (IllegalArgumentException ex) {
                break;
            }
        }
        return count;
    }

    /**
     * Finds the direction towards which there are the fewest obstacles
     * @param field
     * @param position
     * @return 
     */
    private TurnAction.Direction findDirection(Battlefield field, MapPoint position) {
        TurnAction.Direction ret = null;
        int mostMoves = 0;
        for (TurnAction.Direction d : TurnAction.Direction.values()) {
            int count = findMoves(field, position, d);
            if (count > mostMoves) {
                ret = d;
                mostMoves = count;
            }
        }
        return ret; //Maybe null
    }

    private TurnAction shootClosest(Battlefield field, MapPoint position) {
        //Find all the tanks
        List<MapPoint> tanks = field.find(FieldObjectType.TANK);
        tanks.remove(position);

        if (tanks.isEmpty()) return TurnAction.createNothingAction();

        //Calculate closest tank
        MapPoint cPoint = null;
        double cDist = Double.POSITIVE_INFINITY;
        for (MapPoint p : tanks) {
            double dist = position.distanceTo(p);
            if (dist < cDist) {
                cDist = dist;
                cPoint = p;
            }
        }

        //Shoot at closest tank
        return TurnAction.createShootActionRadians(position.angleBetweenRadians(cPoint));
    }

    private int turnsUntilShoot = 1;
    private TurnAction.Direction moveToward = null;

    @Override
    public TurnAction onTurn(Battlefield field, MapPoint position, float health) {
        //Determine if current direction is valid
        int moves = findMoves(field, position, moveToward);
        if (moves <= 0) {
            moveToward = findDirection(field, position);
            //Determine if we're stuck
            if (moveToward == null) {
                return shootClosest(field, position);
            }
        }


        //Shoot if it's time
        if (turnsUntilShoot == 0) {
            turnsUntilShoot = 1;
            return shootClosest(field, position);
        } else {
            turnsUntilShoot--;
            return TurnAction.createMoveAction(moveToward, Math.min(3, moves));
        }
    }

    public void onDestroyed(Battlefield field, boolean won) {}
    public void onSpawn(Battlefield field, MapPoint position) {}
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit) {}
    public String getName() {
        return "Bouncing Tank";
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.