Lass uns einen Panzerkrieg führen!


18

Lass uns einen Panzerkrieg führen!

Teilweise inspiriert von Destroy Them With Lazers

Zielsetzung

Ihre Aufgabe ist es, einen Panzer zu steuern. Bewege dich und schieße auf andere Panzer und Hindernisse im 2D-Schlachtfeld. Der letzte Panzer ist der Gewinner!

Kartenformat

Ihr Tank wird auf einem 2D - Feld auf einer Basis sein , ndurch nGitter von Einheit Plätzen. Ich werde entscheiden, was nauf der Anzahl der Einsendungen basiert. Jedes Quadrat kann nur eines der folgenden Elemente enthalten:

  • Ein Panzer
  • Ein Baum
  • Ein Stein
  • Eine Mauer
  • Nichts

Alle Hindernisse und Panzer füllen ihre Räume vollständig aus, und sie blockieren alle Schüsse, die sie treffen, davor, Dinge weiter unten zu beschädigen.

Hier ist ein Beispiel für ein Feld mit #= tank; T= Baum; R= Rock; W= Wand; .= nichts mit n= 10

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

Koordinaten haben das Format, x, yin dem von xlinks nach rechts und von yunten nach oben erhöht wird. Das untere linke Feld enthält die Koordinate 0, 0. Jeder Panzer darf sich in einen beliebigen freien Raum bewegen und in eine beliebige Richtung schießen.

Kartendynamik

Ihr Panzer muss nicht nur andere Panzer abschießen! Wenn es etwas auf der Karte schießt, können Dinge passieren.

  • Wenn auf eine Wand geschossen wird, wird sie nach einer gewissen Anzahl von Schüssen im Bereich von 1 bis 4 zerstört
  • Wird auf einen Baum geschossen, wird dieser sofort zerstört
  • Wenn auf einen Stein geschossen wird, überschreitet der Schuss diesen und beschädigt den nächsten Treffer

Sobald etwas zerstört ist, befindet es sich nicht mehr auf der Karte (es wird durch nichts ersetzt). Wenn ein Schuss ein Hindernis zerstört, wird es blockiert und beschädigt nichts weiter auf seinem Weg.

Panzerdynamik

Jeder Tank beginnt mit life= 100. Jeder Schuss auf einen Panzer verringert lifesich je nach Entfernung um 20-30 . Dies kann mit delta_life=-30+(shot_distance*10/diagonal_map_length)(wo diagonal_map_lengthist (n-1)*sqrt(2)) berechnet werden . Zusätzlich regeneriert jeder Tank 1 lifepro Runde.

Wendet sich

Es werden einige Runden abgehalten (ich werde entscheiden, sobald ich Einsendungen habe). Zu Beginn jeder Runde wird nach dem Zufallsprinzip eine Karte erstellt und Panzer werden an zufälligen leeren Stellen darauf platziert. In jeder Runde erhält jeder Panzer eine Drehung in beliebiger Reihenfolge. Nachdem jeder Panzer gedreht wurde, werden sie in der gleichen Reihenfolge erneut gedreht. Die Runde wird fortgesetzt, bis nur noch ein Panzer übrig ist. Dieser Panzer wird der Gewinner sein und 1 Punkt erhalten. Das Spiel geht dann in die nächste Runde.

Sobald alle Runden gelaufen sind, werde ich die Punktzahlen zu dieser Frage veröffentlichen.

Während des Zuges eines Panzers kann es eine der folgenden Aktionen ausführen

  • Bewegen Sie sich horizontal oder vertikal um bis zu 3 Felder in eine Richtung. Wenn der Tank durch ein Hindernis oder einen anderen Tank blockiert ist, wird er so weit wie möglich bewegt, ohne das Hindernis oder den Tank zu passieren.
  • Schießen Sie in eine Richtung, dargestellt durch einen Gleitpunktwinkel in Grad. Die x-Achse des lokalen Raums Ihres Panzers (horizontal von links nach rechts, auch bekannt als Ost oder TurnAction.Direction.EAST) ist 0 Grad, und die Winkel nehmen gegen den Uhrzeigersinn zu. Aufnahmen sind ungenau und der tatsächliche Schusswinkel kann 5 Grad größer oder kleiner sein als der von Ihnen gewählte Winkel.
  • Nichts tun.

Abbiegungen sind nicht zeitlich begrenzt, aber dies bedeutet nicht, dass Sie absichtlich Zeit verschwenden können, um alles aufzuhängen.

Einreichungen / Protokoll

Jedes eingereichte Programm kontrolliert einen Panzer auf dem Feld. Das Steuerungsprogramm ist in Java, daher müssen Ihre Programme vorerst in Java sein (ich werde wahrscheinlich irgendwann einen Wrapper für andere Sprachen schreiben, oder Sie könnten Ihren eigenen schreiben).

Ihre Programme implementieren die TankSchnittstelle mit den folgenden Methoden:

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

Die BattlefieldKlasse enthält eine 2D-Anordnung von Objekten ( Battlefield.FIELD_SIZEvon Battlefield.FIELD_SIZE), die Dinge auf dem Schlachtfeld darstellen. Battlefield.getObjectTypeAt(...)gibt a wird FieldObjectTypefür das Objekt an den angegebenen Koordinaten (eine von FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, oder FieldObjectType.NOTHING). Wenn Sie versuchen, ein Objekt aus dem Kartenbereich zu bringen (Koordinaten <0 oder> = Battlefield.FIELD_SIZE), IllegalArgumentExceptionwird ein geworfen.

MapPointist eine Klasse zum Festlegen von Punkten auf der Karte. Verwenden Sie MapPoint.getX()und, MapPoint.getY()um auf die Koordinaten zuzugreifen.

EDIT: Einige Utility - Methoden wurden hinzugefügt: MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType), und TurnAction.createShootActionRadians(double)wie vorgeschlagen Wasmoo .

Weitere Informationen finden Sie in den Javadocs, siehe unten.

Alle (öffentlichen API-) Klassen sind im Paket enthalten zove.ppcg.tankwar.

Steuerungsprogramm

Den vollständigen Quellcode und die Javadocs des Steuerprogramms und der Tank-API finden Sie auf meinem GitHub-Repository: https://github.com/Hungary-Dude/TankWarControl

Sie können Pull-Anfragen und / oder Kommentare senden, wenn Sie einen Fehler sehen oder eine Verbesserung wünschen.

Ich habe zwei Beispieltankprogramme geschrieben RandomMoveTankund RandomShootTank(der Name sagt schon alles).

Um Ihren Panzer zu betreiben, fügen Sie Ihre vollqualifizierte Panzerklasse (Paketname + Klassenname) zu tanks.list(einer Klasse pro Zeile) hinzu, bearbeiten Sie die Einstellungen nach Bedarf in zove.ppcg.tankwar.Control(Abbiegeverzögerung, ob eine GUI-Darstellung des Feldes angezeigt werden soll usw.) und renne zove.ppcg.tankwar.Control. Stellen Sie sicher, dass mindestens 2 Tanks auf der Liste sind, da sonst die Ergebnisse undefiniert sind. (Verwenden Sie ggf. die Probentanks).

Ihre Programme werden auf meinem Computer unter diesem Steuerungsprogramm ausgeführt. Ich werde einen Link zur Quelle hinzufügen, sobald ich es schreibe. Sie können der Quelle gerne Änderungen vorschlagen.

Regeln

  • Ihre Einsendungen müssen den oben genannten Richtlinien entsprechen
  • Ihre Programme greifen möglicherweise nicht auf das Dateisystem, das Netzwerk oder versuchen, meinen Computer in irgendeiner Weise anzugreifen
  • Ihre Programme versuchen möglicherweise nicht, mein Steuerungsprogramm zum Schummeln auszunutzen
  • Kein Trolling (z. B. dass Ihr Programm absichtlich Zeit verschwendet, um alles aufzulegen)
  • Möglicherweise haben Sie mehr als eine Einreichung
  • Versuche kreativ zu sein mit Einsendungen!
  • Ich behalte mir das Recht vor, Programme willkürlich zuzulassen oder nicht zuzulassen

Viel Glück!

UPDATE: Nachdem ich den Mauerteleportationsfehler behoben und die Regeneration durchgeführt hatte, lieferte ich die aktuellen Einreichungen für 100 Runden mitBattlefield.FIELD_SIZE = 30

UPDATE 2: Ich habe die neue Submission RunTank hinzugefügt, nachdem ich mich ein bisschen mit Groovy getäuscht hatte ...

Aktualisierte Ergebnisse:

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

Derzeit regenerieren Panzer 1 Leben pro Runde. Sollte das erhöht werden?


1
Warum sind MapPoint's xund y floats? Sollten sie nicht sein ints?
IchBinKeinBaum

Guter Punkt. Ich bin mir nicht sicher, warum ich beschlossen habe, sie schwimmen zu lassen. Ich werde sie in Ints ändern. Edit : aktualisiert auf ints, check the repo
DankMemes

Wenn Sie auf Punkt 1.1 stehen und mit einem Winkel von 0 Grad schießen, bewegt sich das Projektil in Richtung OST, oder?
CommonGuy

@Manu Ja. Es tut mir leid, wenn das nicht klar war.
DankMemes

Ich habe ein paar Fehler gefunden: Battlefield.java:88 Manchmal ist obj null (Ich denke, wenn ein Panzer stirbt, wenn seine Bewegungsaktion ansteht) Control.java:151 Wenn sich Panzer gleichzeitig töten, ist get (0) ungültig
Wasmoo

Antworten:


2

HunterKiller

Dieser intelligente Jäger wird versuchen, eine sichere Position zu finden, in der er sauber auf genau ein Ziel schießen kann. (Und somit kann nur ein Ziel darauf schießen.)

Funktioniert am besten, wenn viel Deckung vorhanden ist.

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

}

Und das ist es. Ich bin ausgegeben.


2

Dieser geradlinige Panzer findet den nächsten feindlichen Panzer und schießt darauf. Wäre schön, wenn find, distanceund angleeingebaut wären, und wenn createShootActionein Double im Bogenmaß (dh das Ergebnis von angle) akzeptiert würde

Bearbeiten: Die Klasse wurde umgeschrieben, um neue Dienstprogrammmethoden aufzunehmen

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)- Tolle Idee, ich werde es hinzufügen.
DankMemes

1

Ich bin nicht sehr gut darin, aber ich dachte, ich würde es trotzdem versuchen, weißt du, üben und so.

Mein Panzer wird nach dem Zufallsprinzip entscheiden, ob er sich bewegt oder schießt. Wenn es beschließt zu schießen, wird es versuchen, auf das nächste verfügbare Ziel zu schießen.

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

Den vollständigen Code einschließlich des Steuerungsprogramms finden Sie hier .


Versuchen SieDirection.getRandom()
DankMemes

@ZoveGames Bearbeitet, danke für den Tipp.
MisterBla

1

Dodge Tank

Dieser Panzer schießt auf den nächsten Panzer. Abhängig von seiner Gesundheit und der letzten Bewegung versucht er von Zeit zu Zeit, sich senkrecht zum nächstgelegenen Panzer zu bewegen, um seinen Lasern auszuweichen.

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

Das war viel komplizierter als ich dachte ..

Dies ist mein Eintrag in Groovy, Sie müssen Groovy installiert und kompilieren

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

Um es aufzurufen, müssen Sie $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (oder welche Version auch immer) zum Klassenpfad hinzufügen.

Ich könnte Ihnen eine kompilierte .class-Datei und die Bibliothek senden, wenn Sie sie nicht installieren möchten.

Es scheint eine Situation zu geben, in der Panzer es nicht sehen können. Ich weiß nicht, ob das beabsichtigt ist. Das verursachte Deadlocks beim Testen.

Wie auch immer, hier ist RunTank: RunTank bewegt sich mutig in die entgegengesetzte Richtung des nächstgelegenen Panzers, wenn es sich um den nächstgelegenen Panzer handelt oder mehr als ein Panzer sich in FIELD_SIZE / 3 befindet. Ich hoffe, das macht Sinn, ich bin betrunken :)

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

Ich habe einen Vorschlag: Hinzufügen von Farben zum Tank und eine Methode, um es zu implementieren. Auch Labels wären nett in der GUI :)


def RandomMoveTank() {}- soll das da sein? (Ich weiß nicht, groovy)
DankMemes

Nein, ich habe den RandomMoveTank kopiert und vergessen, den Konstruktor zu entfernen, danke :)
Fels

Ich habe Ihren Code kompiliert und den Ordner mit den .class-Dateien und dem groovigen Jar zu meinem Projektklassenpfad hinzugefügt. Reflexion hat funktioniert! Ich habe die aktualisierten Ergebnisse veröffentlicht. Dein Panzer hat sich ganz gut geschlagen :)
DankMemes

1
Nett! Und verdammt du DodgeTank!
Fels

1

Dies ist eine Variante des Shoot-Closest in dem es sich in jeder zweiten Runde in eine Richtung bewegt, bis es nicht mehr kann. Es schießt jede zweite Runde.

Es hat ein praktisches Hilfsprogramm, pathmit dem alle Punkte (und damit Objekte) zwischen zwei Punkten identifiziert werden können.

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";
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.