Tank savaşı yapalım!


18

Tank savaşı yapalım!

Kısmen ilham Lazerler ile Yok

Amaç

Göreviniz bir tankı kontrol etmektir. Hareket edin ve 2D savaş alanındaki diğer tankları ve engelleri vurun. Son tank ayakta kazanacak!

Harita biçimi

Sizin tankı bir dayalı bir 2D sahada olacak ntarafından nbirim kare ızgarasının. nBaşvuru sayısına neyin dayandığına karar vereceğim . Her kare şunlardan yalnızca birini içerebilir:

  • Bir tank
  • Bir ağaç
  • Bir kaya
  • Bir duvar
  • Hiçbir şey değil

Tüm engeller ve tanklar alanlarını tamamen dolduruyor ve onları vuran tüm atışları daha aşağıya zarar vermekten alıkoyuyorlar.

İşte #= tank; T= ağacı; R= kaya; W= duvarı; .= hiçbir şey n= 10

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

Koordinatlar biçimindeki artışlar sağ ve sola üstüne yükselir alt. Sol alt boşluk koordinat içerir . Her tank herhangi bir boş yere gidebilir ve herhangi bir yönde ateş edebilir.x, yxy0, 0

Harita Dinamiği

Tankınızın sadece diğer tankları vurması gerekmez! Haritada bir şey vurursa, her şey olabilir.

  • Bir duvara vurulursa, 1'den 4'e kadar çeşitli atışlardan sonra yıkılacaktır.
  • Bir ağaç vurulursa, hemen yok edilir
  • Eğer bir kaya vurulursa, atış onun üzerinden geçecek ve vurduğu bir sonraki şeye zarar verecektir.

Bir şey yok edildiğinde, artık harita üzerinde değildir (hiçbir şeyle değiştirilmeyecektir). Bir atış bir engeli yok ederse, engellenir ve yolu boyunca başka hiçbir şeye zarar vermez.

Tank dinamiği

Her tank life= 100 ile başlar . Bir tanktaki her atış lifemesafeye göre 20-30 azalır . Bu hesaplanabilir delta_life=-30+(shot_distance*10/diagonal_map_length)(burada diagonal_map_lengtholduğu (n-1)*sqrt(2)). Ayrıca, her tank lifetur başına 1 tane üretir.

Dönüşler

Bazı turlar yapılacaktır (gönderimlerim olduğunda karar vereceğim). Her turun başında rastgele bir harita oluşturulacak ve üzerine tanklar rastgele boş yerlere yerleştirilecektir. Her turda, her tanka rasgele sırada bir dönüş verilir. Her tanka bir dönüş verildikten sonra, aynı sırayla tekrar dönüşler yapılacaktır. Tur sadece bir tank kalana kadar devam eder. Bu tank kazanan olacak ve 1 puan alacaklar. Oyun daha sonra bir sonraki tura geçecek.

Tüm turlar yapıldıktan sonra puanları bu soruya göndereceğim.

Bir tankın dönüşü sırasında aşağıdakilerden birini yapabilir

  • Yatay veya dikey olarak tek yönde 3 boşluk hareket ettirin. Tank bir engel veya başka bir tank tarafından engellenirse, engelden veya tanktan geçmeden mümkün olduğunca hareket ettirilir.
  • Derece olarak kayan nokta açısı ile temsil edilen bir yönde çekim yapın. TurnAction.Direction.EASTTankınızın yerel alanının x ekseni (yatay olarak soldan sağa, aka doğu veya ) 0 derece ve açılar saat yönünün tersine artar. Çekimler yanlış ve çekimin gerçek açısı seçtiğiniz açıdan 5 derece daha büyük veya daha az olabilir.
  • Hiçbir şey yapma.

Dönüşler zamanla sınırlı değildir, ancak bu her şeyi asmak için kasıtlı olarak zaman harcayabileceğiniz anlamına gelmez.

Gönderimler / Protokol

Gönderilen her program sahadaki bir tankı kontrol edecektir. Kontrol programı Java'da, bu yüzden programlarınızın şimdilik Java'da olması gerekiyor (muhtemelen bir noktada diğer diller için bir sarıcı yazacağım, ya da kendi yazabilirsiniz).

Programlarınız Tank, aşağıdaki yöntemlere sahip arayüzü uygular :

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

BattlefieldSınıf (nesneler bir 2D dizi içerir Battlefield.FIELD_SIZEgöre Battlefield.FIELD_SIZEsavaş şeyler temsil etmektedir). Battlefield.getObjectTypeAt(...)Bir verecek FieldObjectTypebelirtilen koordinatlarda nesne için (biri FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, ya da FieldObjectType.NOTHING). Bir nesneyi haritanın menzilinin dışına çıkarmaya çalışırsanız (koordinatlar <0 veya> = Battlefield.FIELD_SIZE) o zaman bir IllegalArgumentExceptionatılır.

MapPointharitadaki noktaları belirtmek için kullanılan bir sınıftır. Koordinatlara erişmek için MapPoint.getX()ve tuşlarını kullanın MapPoint.getY().

DÜZENLEME: Bazı yardımcı yöntemleri eklenmiştir: MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType)ve TurnAction.createShootActionRadians(double)tarafından önerilen Wasmoo .

Javadoc'larda daha fazla bilgi bulunabilir, aşağıdaki bölüme bakın.

Tüm (genel API) sınıfları paketin altındadır zove.ppcg.tankwar.

Kontrol Programı

Kontrol programının ve tank API'sının tam kaynağı ve javadocları GitHub repo'mda bulunabilir: https://github.com/Hungary-Dude/TankWarControl

Bir hata görürseniz veya bir iyileştirme istiyorsanız, çekme istekleri ve / veya yorum göndermekten çekinmeyin.

İki örnek tank programı yazdım RandomMoveTankve RandomShootTank(isim her şeyi söylüyor).

Tankınızı çalıştırmak için tam nitelikli (paket adı + sınıf adı) tank sınıfınızı tanks.list(satır başına bir sınıf) ekleyin, ayarları gerektiği gibi düzenleyin zove.ppcg.tankwar.Control(dönüş gecikmesi, alanın GUI temsilini gösterilip gösterilmeyeceği vb.), ve koş zove.ppcg.tankwar.Control. Listede en az 2 tank bulunduğundan veya sonuçların tanımsız olduğundan emin olun. (Gerekirse örnek tankları kullanın).

Programlarınız bu kontrol programı altında makinemde çalıştırılacak. Yazdıktan sonra kaynağa bir bağlantı ekleyeceğim. Kaynakta düzenlemeler yapmaktan çekinmeyin.

kurallar

  • Gönderileriniz yukarıdaki yönergelere uymalıdır
  • Programlarınız dosya sistemine, ağa erişemez veya makineme hiçbir şekilde saldırmaya çalışamaz
  • Programlarınız hile yapmak için kontrol programımdan yararlanmaya çalışmayabilir
  • Troll yok (programınızın her şeyi kapatmak için kasıtlı olarak zaman kaybetmesi gibi)
  • Birden fazla gönderiminiz olabilir
  • Sunumlarla yaratıcı olmaya çalışın!
  • Programlara keyfi olarak izin verme veya vermeme hakkını saklıyorum

İyi şanslar!

GÜNCELLEME: Duvar ışınlama hatasını düzelttikten ve rejenerasyon uyguladıktan sonra, mevcut başvuruları 100 tur için çalıştırdım.Battlefield.FIELD_SIZE = 30

GÜNCELLEME 2: Groovy ile biraz kandırdıktan sonra yeni gönderimi RunTank'ı ekledim ...

Güncellenmiş sonuçlar:

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

Şu anda tanklar tur başına 1 can üretiyor. Bu artırılmalı mı?


1
Neden MapPoints' xve y floats? Olmamalılar mı ints?
IchBinKeinBaum

İyi bir nokta. Onları neden yüzmeye karar verdim emin değilim. Onları ints olarak değiştireceğim. Düzenleme : onları ints güncellendi, repo kontrol
DankMemes

1,1 noktasında durursanız ve 0 derecelik bir açıyla ateş ederseniz, mermi DOĞU yönünde gider, değil mi?
commonguy

@Manu Evet. Bu açık değilse üzgünüm.
DankMemes

Birkaç hata buldum: Battlefield.java:88 Bazen obj boş (sanırım bir tank hamle eylemi beklemede öldüğünde) Control.java:151 Tanklar aynı anda birbirini öldürdüğünde, (0) geçersiz olur
Wasmoo

Yanıtlar:


2

HunterKiller

Bu akıllı avcı, tam olarak bir hedefe temiz bir şekilde vurabileceği güvenli bir pozisyon bulmaya çalışacaktır. (Ve böylece, sadece bir hedef vurabilir)

Çok fazla kapak olduğunda en iyi sonucu verir.

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

}

Ve bu kadar. Ben harcadım.


2

Bu düz tank en yakın düşman tankını bulur ve ona ateş eder. Güzel olurdu eğer find, distanceve angleinşa edildi ve eğer createShootActionradyan (yani sonucunu çift kabul angle)

Edit: Sınıf yeni yarar yöntemleri eklemek için yeniden yazıldı

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)- Harika bir fikir, ekleyeceğim.
DankMemes

1

Ben bu konuda pek iyi değilim ama yine de denemek istiyorum, biliyorsun, pratik ve şeyler.

Tankım rastgele hareket etmeye veya ateş etmeye karar verecek. Ateş etmeye karar verdiğinde, mümkün olan en yakın hedefe ateş etmeye çalışır.

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

Kontrol programını içeren kodun tamamını burada bulabilirsiniz .


DeneyinDirection.getRandom()
DankMemes

@ZoveGames Düzenledi, bahşiş için teşekkürler.
MisterBla

1

Dodge Tankı

Bu tank en yakın tankta ateş edecek. Her zaman, sağlığına ve son taşınmasına bağlı olarak, lazerlerini atlatmak için en yakın tanka dik olarak hareket etmeye çalışacaktır.

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

Bu düşündüğümden çok daha karmaşıktı ..

Bu benim harika girişim, harika yüklü ve derlemelisin

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

Bunu çağırmak için sınıf yoluna $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (veya herhangi bir sürüm) eklemeniz gerekir.

Yüklemek istemiyorsanız size derlenmiş bir .class dosyası ve kitaplık gönderebilirim.

Tankların başkalarını göremediği bir durum var gibi görünüyor, bunun amaçlanıp amaçlanmadığını bilmiyorum. Bu test sırasında kilitlenmelere neden oldu.

Her neyse burada RunTank: RunTank cesur, en yakın tankın en yakın tankıysa veya birden fazla tank FIELD_SIZE / 3 içinde ise en yakın tankın ters yönünde ilerler. Umarım bu sarhoşum :)

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

Bir önerim var: Tanka renkler ve onu uygulamak için bir yöntem ekleyin. Ayrıca etiketler GUI'de iyi olurdu :)


def RandomMoveTank() {}- orada olmak mı gerekiyor? (Ben bilmiyorum groovy)
DankMemes

Hayır, RandomMoveTank'ı kopyaladım ve kurucuyu kaldırmayı unuttum, teşekkürler :)
Fels

Kodunuzu derledim ve .class dosyalarını ve harika kavanozu içeren klasörü proje sınıf yoluma ekledim. Yansıma işe yaradı! Güncellenen puanları gönderdim. Senin tank oldukça iyi yaptı :)
DankMemes

1
Güzel! Ve lanet olsun DodgeTank!
Fels

1

Bu, Shoot-Closest'te bir varyanttır, çünkü diğer her dönüşte, artık yapamayana kadar bir yönde hareket eder. Her turda ateş ediyor.

pathİki nokta arasındaki tüm noktaları (ve böylece nesneleri) tanımlamak için kullanılabilecek kullanışlı bir yardımcı programa sahiptir .

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";
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.