Ayo perang tank!


18

Ayo perang tank!

Sebagian terinspirasi oleh Hancurkan Mereka Dengan Lazers

Objektif

Tugas Anda adalah mengendalikan sebuah tangki. Bergerak dan tembak tank dan rintangan lain di medan perang 2D. Posisi tank terakhir akan menjadi pemenang!

Format peta

Tangki Anda akan berada di lapangan 2D didasarkan pada noleh njaringan unit kotak. Saya akan memutuskan apa nyang didasarkan pada jumlah pengiriman. Setiap kotak hanya dapat berisi satu dari:

  • Sebuah tank
  • Pohon
  • Sebuah batu
  • Dinding
  • Tidak ada

Semua rintangan dan tank sepenuhnya mengisi ruang mereka, dan mereka memblokir semua tembakan yang mengenai mereka untuk merusak barang-barang lebih jauh.

Berikut adalah contoh bidang dengan #= tangki; T= pohon; R= batu; W= dinding; .= tidak ada dengan n= 10

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

Koordinat berada dalam format di x, ymana xpeningkatan dari kiri ke kanan dan ynaik dari bawah ke atas. Ruang kiri bawah memiliki koordinat0, 0 . Setiap tangki dapat bergerak ke ruang kosong apa pun dan menembak ke segala arah.

Dinamika Peta

Tank Anda tidak hanya harus menembak tank lain! Jika menembak sesuatu di peta, hal-hal bisa terjadi.

  • Jika dinding tertembak, ia akan dihancurkan setelah beberapa tembakan, mulai dari 1 hingga 4
  • Jika pohon ditembaki, ia akan segera dihancurkan
  • Jika batu ditembak, tembakan akan melewatinya dan merusak benda berikutnya yang terkena

Setelah sesuatu dihancurkan, ia tidak lagi ada di peta (tidak akan diganti dengan apa pun). Jika tembakan menghancurkan penghalang, itu akan diblokir dan tidak akan merusak apa pun lebih jauh di sepanjang jalannya.

Dinamika tangki

Setiap tangki dimulai dengan life= 100. Setiap tembakan di tangki akan mengurangi 20-30 lifeberdasarkan jarak. Hal ini dapat dihitung dengan delta_life=-30+(shot_distance*10/diagonal_map_length)(di mana diagonal_map_lengthadalah (n-1)*sqrt(2)). Selain itu, setiap tangki meregenerasi 1 lifeper putaran.

Ternyata

Beberapa putaran akan dijalankan (saya akan memutuskan begitu saya memiliki kiriman). Di awal setiap putaran, peta akan dibuat secara acak, dan tank akan ditempatkan di sana di lokasi kosong acak. Selama setiap putaran, setiap tangki akan diberikan giliran, dalam urutan acak. Setelah setiap tangki diberikan putaran, mereka akan diberikan putaran lagi dalam urutan yang sama. Putaran berlanjut sampai hanya ada satu tangki yang tersisa. Tank itu akan menjadi pemenang, dan mereka akan menerima 1 poin. Pertandingan kemudian akan berlanjut ke babak berikutnya.

Setelah semua putaran telah dijalankan, saya akan memposting skor pada pertanyaan ini.

Selama giliran tangki, itu mungkin melakukan salah satu dari berikut ini

  • Bergerak hingga 3 spasi dalam satu arah, baik secara horizontal maupun vertikal. Jika tangki terhalang oleh rintangan atau tangki lain, itu akan dipindahkan sejauh mungkin tanpa melalui rintangan atau tangki.
  • Bidik dalam arah tertentu, diwakili oleh sudut titik mengambang dalam derajat. Sumbu x ruang lokal tangki Anda (horizontal kiri ke kanan, alias timur atauTurnAction.Direction.EAST ) adalah 0 derajat, dan sudut bertambah berlawanan arah jarum jam. Bidikan tidak akurat, dan sudut pengambilan gambar yang sebenarnya mungkin 5 derajat lebih besar atau kurang dari sudut yang Anda pilih.
  • Tidak melakukan apapun.

Gilirannya tidak terbatas dalam waktu, tetapi ini tidak berarti Anda dapat dengan sengaja membuang waktu untuk menutup semuanya.

Pengajuan / Protokol

Setiap program yang dikirimkan akan mengendalikan satu tangki di lapangan. Program kontrol ada di Java, jadi program Anda harus di Java untuk saat ini (saya mungkin akan menulis pembungkus untuk bahasa lain di beberapa titik, atau Anda bisa menulis sendiri).

Program Anda akan mengimplementasikan Tankantarmuka, yang memiliki metode berikut:

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

The Battlefieldkelas berisi array 2D obyek ( Battlefield.FIELD_SIZEoleh Battlefield.FIELD_SIZE) yang mewakili hal-hal di medan perang. Battlefield.getObjectTypeAt(...)akan memberikan FieldObjectTypeuntuk objek pada koordinat yang ditentukan (salah satu FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, atau FieldObjectType.NOTHING). Jika Anda mencoba untuk mendapatkan objek di luar jangkauan peta (koordinat <0 atau> = Battlefield.FIELD_SIZE) maka sebuah IllegalArgumentExceptionakan dilempar.

MapPointadalah kelas untuk menentukan titik pada peta. Gunakan MapPoint.getX()dan MapPoint.getY()untuk mengakses koordinat.

EDIT: Beberapa metode utilitas telah ditambahkan: MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType), dan TurnAction.createShootActionRadians(double)seperti yang disarankan oleh Wasmoo .

Info lebih lanjut dapat ditemukan di javadocs, lihat bagian di bawah.

Semua kelas (API publik) berada di bawah paket zove.ppcg.tankwar.

Program Kontrol

Sumber lengkap dan javadocs dari program kontrol dan API tangki dapat ditemukan di repo GitHub saya: https://github.com/Hungary-Dude/TankWarControl

Jangan ragu untuk mengirim permintaan tarik dan / atau komentar jika Anda melihat bug atau ingin peningkatan.

Saya telah menulis dua program tangki sampel, RandomMoveTankdan RandomShootTank(namanya mengatakan itu semua).

Untuk menjalankan tangki Anda, tambahkan kelas tangki Anda yang memenuhi syarat (nama paket + nama kelas) ke tanks.list(satu kelas per baris), edit pengaturan seperlunya di zove.ppcg.tankwar.Control(tunda giliran, apakah akan menampilkan representasi GUI dari bidang, dll), dan larizove.ppcg.tankwar.Control . Pastikan setidaknya ada 2 tank dalam daftar, atau hasilnya tidak ditentukan. (Gunakan tangki sampel jika perlu).

Program Anda akan dijalankan di mesin saya di bawah program kontrol ini. Saya akan menyertakan tautan ke sumber begitu saya menulisnya. Jangan ragu untuk menyarankan pengeditan ke sumber.

Aturan

  • Kiriman Anda harus mengikuti pedoman di atas
  • Program Anda mungkin tidak mengakses sistem file, jaringan, atau mencoba menyerang mesin saya dengan cara apa pun
  • Program Anda tidak boleh mencoba mengeksploitasi program kontrol saya untuk menipu
  • Tanpa trolling (seperti sengaja membuat program Anda membuang waktu untuk menutup semuanya)
  • Anda mungkin memiliki lebih dari satu pengiriman
  • Cobalah menjadi kreatif dengan kiriman!
  • Saya berhak untuk mengizinkan atau tidak mengizinkan program secara sewenang-wenang

Semoga berhasil!

UPDATE: Setelah memperbaiki bug teleportasi dinding dan menerapkan regenerasi, saya menjalankan kiriman saat ini untuk 100 putaran denganBattlefield.FIELD_SIZE = 30

UPDATE 2: Saya menambahkan kiriman baru, RunTank, setelah bermain-main dengan Groovy sebentar ...

Hasil yang diperbarui:

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

Saat ini tangki meregenerasi 1 kehidupan per putaran. Haruskah itu ditingkatkan?


1
Mengapa MapPoint's xdan y floats? Bukankah seharusnya begitu ints?
IchBinKeinBaum

Poin bagus. Saya tidak yakin mengapa saya memutuskan untuk membuatnya mengapung. Saya akan mengubahnya menjadi int. Sunting : perbarui ke ints, periksa repo
DankMemes

Jika Anda berdiri di titik 1,1 dan menembak dengan sudut 0 derajat, proyektil berjalan ke arah TIMUR, kan?
CommonGuy

@ Manu Ya. Maaf kalau itu tidak jelas.
DankMemes

Saya menemukan beberapa kesalahan: Battlefield.java:88 Kadang-kadang obj adalah nol (saya pikir ketika sebuah tank mati ketika tindakan bergeraknya tertunda) Control.java:151 Ketika tank secara bersamaan membunuh satu sama lain, dapatkan (0) tidak valid
Wasmoo

Jawaban:


2

HunterKiller

Pemburu yang cerdas ini akan berusaha menemukan posisi aman di mana ia dapat menembak dengan tepat pada satu sasaran. (Dan dengan demikian, hanya satu target yang bisa menembaknya)

Berfungsi paling baik ketika ada banyak penutup.

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

}

Dan itu saja. Saya dihabiskan.


2

Tank lurus ke depan ini menemukan tank musuh terdekat dan menembaknya. Akan lebih baik jika find,, distancedan angledibangun di, dan jika createShootActionmenerima rangkap dalam radian (yaitu hasil angle)

Sunting: Kelas ditulis ulang untuk memasukkan metode utilitas baru

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)- Ide bagus, saya akan menambahkannya.
DankMemes

1

Saya tidak pandai dalam hal ini, tetapi saya pikir saya masih akan mencobanya, Anda tahu, berlatih dan semacamnya.

Tank saya akan secara acak memutuskan untuk bergerak atau menembak. Ketika memutuskan untuk menembak, ia akan mencoba menembak target terdekat yang tersedia.

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

Kode lengkap termasuk program kontrol dapat ditemukan di sini .


CobaDirection.getRandom()
DankMemes

@ZoveGames Mengeditnya, terima kasih atas tipnya.
MisterBla

1

Dodge Tank

Tangki ini akan menembak ke tangki terdekat. Sering kali, tergantung pada kesehatannya dan terakhir kali ia bergerak, ia akan mencoba untuk bergerak tegak lurus ke tangki terdekat dalam upaya untuk menghindari lasernya.

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

Ini jauh lebih rumit daripada yang saya pikir ..

Ini adalah entri saya di groovy, Anda perlu groovy diinstal dan kompilasi dengannya

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

Untuk menyebutnya, Anda perlu menambahkan $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (atau versi apa pun) ke classpath.

Saya bisa mengirimkan Anda file .class yang dikompilasi dan perpustakaan jika Anda tidak ingin menginstalnya.

Tampaknya ada situasi di mana tank tidak bisa melihatnya, saya tidak tahu apakah itu dimaksudkan. Itu menyebabkan kebuntuan saat pengujian.

Pokoknya di sini adalah RunTank: RunTank berani maju dalam arah yang berlawanan dari tangki terdekat jika itu adalah tangki terdekat dengan tangki terdekat atau lebih dari satu tangki dalam FIELD_SIZE / 3. Saya harap masuk akal, saya mabuk :)

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

Saya punya satu saran: Tambahkan warna ke tangki, dan metode untuk menerapkannya. Label juga akan menyenangkan di GUI :)


def RandomMoveTank() {}- Apakah itu dimaksudkan untuk berada di sana? (Saya tidak tahu asyik)
DankMemes

Tidak, saya menyalin RandomMoveTank dan lupa menghapus konstruktor, terima kasih :)
Fels

Saya mengkompilasi kode Anda dan menambahkan folder yang berisi file .class dan toples asyik ke classpath proyek saya. Refleksi berhasil! Saya telah memposting skor yang diperbarui. Tangki Anda bekerja cukup baik :)
DankMemes

1
Bagus! Sialan kau DodgeTank!
Fels

1

Yang ini adalah varian pada Shoot-Closest di mana, setiap belokan lainnya, ia bergerak ke arah hingga tidak bisa lagi. Ia menembak setiap belokan lainnya.

Ini memiliki utilitas praktis path, yang dapat digunakan untuk mengidentifikasi semua titik (dan dengan demikian objek) antara dua titik.

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";
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.