Facciamo una guerra di carri armati!


18

Facciamo una guerra di carri armati!

Parzialmente ispirato a Distruggili con lacci

Obbiettivo

Il tuo compito è controllare un carro armato. Muoviti e spara ad altri carri armati e ostacoli nel campo di battaglia 2D. L'ultimo carro armato in piedi sarà il vincitore!

Formato mappa

Il tank sarà su un campo 2D basato sulla ndalla ngriglia di quadrati unitari. Deciderò cosa nsi basa sul numero di invii. Ogni quadrato può contenere solo uno di:

  • Un serbatoio
  • Un albero
  • Una pietra
  • Un muro
  • Niente

Tutti gli ostacoli e i carri armati riempiono completamente i loro spazi e bloccano tutti i colpi che li colpiscono danneggiando le cose più in basso.

Ecco un esempio di un campo con #= serbatoio; T= albero; R= roccia; W= parete; .= niente con n= 10

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

Le coordinate sono nel formato in x, ycui xaumenta da sinistra a destra e yaumenta dal basso verso l'alto. Lo spazio in basso a sinistra ha le coordinate 0, 0. Ogni carro armato può spostarsi in qualsiasi spazio vuoto e sparare in qualsiasi direzione.

Dinamica delle mappe

Il tuo carro armato non deve solo sparare ad altri carri armati! Se spara qualcosa sulla mappa, le cose possono succedere.

  • Se un muro viene colpito, verrà distrutto dopo un certo numero di colpi, che vanno da 1 a 4
  • Se un albero viene colpito, verrà immediatamente distrutto
  • Se viene colpito a un sasso, il colpo lo passerà sopra e danneggerà la prossima cosa che colpisce

Una volta che qualcosa viene distrutto, non è più sulla mappa (verrà sostituito con nulla). Se un tiro distrugge un ostacolo, verrà bloccato e non danneggerà più nulla lungo il suo cammino.

Dinamica del serbatoio

Ogni serbatoio inizia con life= 100. Ogni colpo in un carro armato ridurrà di 20-30 in lifebase alla distanza. Questo può essere calcolato con delta_life=-30+(shot_distance*10/diagonal_map_length)(dove si diagonal_map_lengthtrova (n-1)*sqrt(2)). Inoltre, ogni carro armato rigenera 1 lifeper turno.

Giri

Verrà eseguito un numero di round (deciderò una volta che avrò invii). All'inizio di ogni round, una mappa verrà generata casualmente e i carri armati verranno posizionati su di essa in posizioni vuote casuali. Durante ogni round, a ogni carro armato verrà dato un turno, in qualsiasi ordine arbitrario. Dopo che a ogni carro armato è stato dato un turno, gli verranno dati di nuovo turni nello stesso ordine. Il round continua fino a quando rimane solo un serbatoio. Quel carro armato sarà il vincitore e riceveranno 1 punto. Il gioco passerà quindi al turno successivo.

Una volta che tutti i round sono stati eseguiti, posterò i punteggi su questa domanda.

Durante il turno di un carro armato, può effettuare una delle seguenti operazioni

  • Spostare fino a 3 spazi in una sola direzione, sia in orizzontale che in verticale. Se il serbatoio viene bloccato da un ostacolo o da un altro serbatoio, verrà spostato il più lontano possibile senza attraversare l'ostacolo o il serbatoio.
  • Scatta in una direzione, rappresentata da un angolo in virgola mobile in gradi. L'asse x dello spazio locale del tuo carro armato (orizzontalmente da sinistra a destra, aka est o TurnAction.Direction.EAST) è 0 gradi, e gli angoli aumentano in senso antiorario. I colpi non sono accurati e l'angolo effettivo del tiro può essere maggiore o minore di 5 gradi rispetto all'angolo scelto.
  • Fare niente.

I turni non sono limitati nel tempo, ma ciò non significa che puoi intenzionalmente perdere tempo per appendere tutto.

Inseriti / Protocollo

Ogni programma presentato controllerà un serbatoio sul campo. Il programma di controllo è in Java, quindi per ora i tuoi programmi devono essere in Java (probabilmente a un certo punto scriverò un wrapper per altre lingue, oppure potresti scriverne uno tuo).

I tuoi programmi implementeranno l' Tankinterfaccia, che ha i seguenti metodi:

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

La Battlefieldclasse contiene una matrice 2D di oggetti ( Battlefield.FIELD_SIZEby Battlefield.FIELD_SIZE) che rappresenta le cose sul campo di battaglia. Battlefield.getObjectTypeAt(...)darà una FieldObjectTypeper l'oggetto alle coordinate specificate (uno FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, o FieldObjectType.NOTHING). Se si tenta di ottenere un oggetto fuori dal raggio della mappa (coordinate <0 o> = Battlefield.FIELD_SIZE), IllegalArgumentExceptionverrà lanciato un oggetto.

MapPointè una classe per specificare i punti sulla mappa. Utilizzare MapPoint.getX()e MapPoint.getY()per accedere alle coordinate.

EDIT: Sono stati aggiunti alcuni metodi di utilità: MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType), e TurnAction.createShootActionRadians(double)come suggerito da Wasmoo .

Ulteriori informazioni sono disponibili nei javadocs, vedere la sezione seguente.

Tutte le classi (API pubbliche) sono incluse nel pacchetto zove.ppcg.tankwar.

Programma di controllo

La fonte completa e javadocs del programma di controllo e dell'API del serbatoio sono disponibili sul mio repository GitHub: https://github.com/Hungary-Dude/TankWarControl

Sentiti libero di inviare richieste pull e / o commenti se vedi un bug o desideri un miglioramento.

Ho scritto due programmi di esempio per il serbatoio RandomMoveTanke RandomShootTank(il nome dice tutto).

Per far funzionare il tuo carro armato, aggiungi la tua classe di carro armato (nome del pacchetto + nome della classe) pienamente qualificata a tanks.list(una classe per riga), modifica le impostazioni come necessario in zove.ppcg.tankwar.Control(ritardo di virata, se mostrare o meno una rappresentazione GUI del campo, ecc.), e corri zove.ppcg.tankwar.Control. Assicurati che ci siano almeno 2 serbatoi nell'elenco o che i risultati non siano definiti. (Utilizzare i serbatoi di campionamento se necessario).

I tuoi programmi verranno eseguiti sulla mia macchina con questo programma di controllo. Includerò un link alla fonte una volta che lo scrivo. Sentiti libero di suggerire modifiche alla fonte.

Regole

  • I tuoi invii devono seguire le linee guida sopra
  • I tuoi programmi non possono accedere al filesystem, alla rete o tentare di attaccare la mia macchina in alcun modo
  • I tuoi programmi potrebbero non tentare di sfruttare il mio programma di controllo per imbrogliare
  • Nessuna pesca a traina (come ad esempio perdere intenzionalmente il tuo programma per appendere tutto)
  • Potresti avere più di un invio
  • Cerca di essere creativo con i contributi!
  • Mi riservo il diritto di autorizzare o meno i programmi arbitrariamente

In bocca al lupo!

AGGIORNAMENTO: Dopo aver corretto il bug di teletrasporto a muro e aver implementato la rigenerazione, ho eseguito gli invii correnti per 100 round conBattlefield.FIELD_SIZE = 30

AGGIORNAMENTO 2: Ho aggiunto il nuovo invio, RunTank, dopo aver preso in giro un po 'con Groovy ...

Risultati aggiornati:

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

Attualmente i carri armati rigenerano 1 punto vita per turno. Dovrebbe essere aumentato?


1
Perché MapPoint's xe y floats? Non dovrebbero essere ints?
IchBinKeinBaum

Buon punto. Non sono sicuro del motivo per cui ho deciso di farli galleggiare. Li cambierò in ints. Modifica : li aggiorna a Ints, controlla il repository
DankMemes

Se ti trovi sul punto 1,1 e spari con un angolo di 0 gradi, il proiettile va in direzione EST, giusto?
CommonGuy

@Manu Sì. Mi dispiace se non fosse chiaro.
DankMemes,

Ho trovato alcuni errori: Battlefield.java:88 A volte obj è nullo (penso che quando un carro armato muore quando è in corso l'azione di movimento) Control.java:151 Quando i carri armati si uccidono contemporaneamente, get (0) non è valido
Wasmoo

Risposte:


2

Cacciatore assassino

Questo cacciatore intelligente tenterà di trovare una posizione sicura in cui possa sparare in modo pulito esattamente a un bersaglio. (E quindi, solo un bersaglio può spararlo)

Funziona meglio quando c'è molta copertura.

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

}

E questo è tutto. Sono speso.


2

Questo carro armato diretto trova il carro nemico più vicino e gli spara. Sarebbe bello se find, distancee anglefosse incorporato, e se createShootActionaccettato un doppio in radianti (cioè il risultato di angle)

Modifica: classe riscritta per includere nuovi metodi di utilità

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)- Ottima idea, la aggiungerò.
DankMemes,

1

Non sono molto bravo in questo, ma pensavo che avrei ancora provato, sai, pratica e cose del genere.

Il mio carro armato deciderà casualmente di muoversi o sparare. Quando decide di sparare, proverà a sparare al bersaglio disponibile più vicino.

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

Il codice completo incluso il programma di controllo è disponibile qui .


ProvaDirection.getRandom()
DankMemes,

@ZoveGames Modificato, grazie per il suggerimento.
MisterBla,

1

Dodge Tank

Questo carro armato sparerà al carro armato più vicino. Ogni tanto, a seconda della sua salute e dell'ultima volta che si è mosso, proverà a spostarsi perpendicolarmente al serbatoio più vicino nel tentativo di schivare i suoi laser.

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

Questo è stato molto più complicato di quanto pensassi ...

Questa è la mia voce in Groovy, è necessario Groovy installato e compilarlo

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

Per chiamarlo devi aggiungere $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (o qualunque versione) al percorso di classe.

Potrei inviarti un file .class compilato e la libreria se non vuoi installarlo.

Sembra esserci una situazione in cui i carri armati non riescono a vederlo altro, non so se sia previsto. Ciò ha causato deadlock durante il test.

Comunque qui è RunTank: RunTank avanza audacemente nella direzione opposta del serbatoio più vicino se è il serbatoio più vicino al serbatoio più vicino o più di un serbatoio si trova all'interno di FIELD_SIZE / 3. Spero che abbia senso, sono ubriaco :)

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

Ho un suggerimento: aggiungi colori al serbatoio e un metodo per implementarlo. Anche le etichette sarebbero belle nella GUI :)


def RandomMoveTank() {}- è pensato per essere lì? (Non conosco groovy)
DankMemes,

No, ho copiato RandomMoveTank e ho dimenticato di rimuovere il costruttore, grazie :)
Fels,

Ho compilato il tuo codice e ho aggiunto la cartella contenente i file .class e il vaso groovy al mio percorso di classe del progetto. La riflessione ha funzionato! Ho pubblicato i punteggi aggiornati. Il tuo carro armato ha funzionato abbastanza bene :)
DankMemes, il

1
Bello! E maledizione a DodgeTank!
Fels,

1

Questa è una variante dello Shoot-Closest in quanto, ogni altra curva, si muove in una direzione fino a quando non può più. Spara a turno.

Ha una comoda utility, pathche può essere utilizzata per identificare tutti i punti (e quindi gli oggetti) tra due punti.

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";
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.