탱크 전쟁을하자!


18

탱크 전쟁을하자!

Lazer와 함께 파괴하는 데 부분적으로 영향을 받음

객관적인

당신의 임무는 탱크를 제어하는 ​​것입니다. 2D 전장에서 다른 탱크와 장애물을 이동하고 쏴라. 마지막 탱크 순위가 승자가 될 것입니다!

지도 형식

탱크는 단위 제곱 격자를 기준으로 2D 필드에 배치 n됩니다 n. n제출 횟수를 기준으로 결정합니다 . 각 사각형은 다음 중 하나만 포함 할 수 있습니다.

  • 탱크
  • 나무
  • 바위
  • 아무것도

모든 장애물과 탱크는 자신의 공간을 완전히 채우고, 충돌하는 모든 사격을 차단하여 물건을 더 손상시키지 않습니다.

다음은 #= tank 인 필드의 예입니다 . T= 트리; R= 바위; W= 벽; .= n10이없는 것

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

좌표의 형식은 증가 오른쪽 왼쪽 상단에 증가 하단. 왼쪽 하단 공간에는 좌표가 있습니다. 각 탱크는 빈 공간으로 이동하여 어떤 방향으로도 발사 할 수 있습니다.x, yxy0, 0

지도 역학

탱크는 다른 탱크를 쏠 필요가 없습니다! 지도에서 무언가를 촬영하면 상황이 발생할 수 있습니다.

  • 벽이 맞으면 1 ~ 4 범위의 몇 번의 발사 후에 벽이 파괴됩니다.
  • 나무가 맞으면 즉시 파괴됩니다
  • 바위가 총에 맞으면, 그 총은 그것을 지나쳐 다음에 닿는 것을 손상시킵니다.

무언가가 파괴되면 더 이상 맵에 표시되지 않습니다 (아무것도 대체되지 않습니다). 샷이 장애물을 파괴하면 막히고 경로를 따라 더 이상 손상되지 않습니다.

탱크 역학

각 탱크는 life= 100으로 시작합니다 . 탱크에서 각 샷은 life거리에 따라 20-30을 줄 입니다. 이는 계산 될 수있다 delta_life=-30+(shot_distance*10/diagonal_map_length)(여기서, diagonal_map_length이다 (n-1)*sqrt(2)). 또한, 각 탱크 life는 한 턴에 1을 재생 합니다.

회전

몇 차례의 라운드가 진행됩니다 (제출 후 결정합니다). 매 라운드가 시작될 때마다 맵이 무작위로 생성되고 탱크가 무작위로 빈 위치에 배치됩니다. 매 라운드마다, 각 탱크는 임의의 순서로 차례가 주어집니다. 모든 탱크에 턴이 주어진 후에는 같은 순서로 다시 턴이 주어집니다. 탱크가 하나만 남을 때까지 라운드가 계속됩니다. 그 전차가 승자가되고 1 점을받습니다. 게임은 다음 라운드로 진행됩니다.

모든 라운드가 완료되면이 질문에 점수를 게시합니다.

전차가 회전하는 동안 다음 중 하나를 수행 할 수 있습니다.

  • 수평 또는 수직으로 한 방향으로 최대 3 칸 이동합니다. 탱크가 장애물이나 다른 탱크에 의해 막히면 장애물이나 탱크를 통하지 않고 가능한 멀리 이동합니다.
  • 부동 소수점 각도 (도)로 표시되는 특정 방향으로 촬영합니다. 탱크의 로컬 공간 (가로로 왼쪽에서 오른쪽, 일명 동쪽 또는 TurnAction.Direction.EAST) 의 x 축은 0 도이고 각도는 시계 반대 방향으로 증가합니다. 샷이 정확하지 않으며 실제 샷 각도는 선택한 각도보다 5도 크거나 작을 수 있습니다.
  • 아무것도하지 마세요.

턴은 시간에 제한이 없지만 모든 것을 끊는 데 의도적으로 시간을 낭비 할 수있는 것은 아닙니다.

제출물 / 프로토콜

제출 된 각 프로그램은 현장에서 한 탱크를 통제합니다. 제어 프로그램은 Java로되어 있으므로 지금은 프로그램이 Java로되어 있어야합니다 (어쩌면 다른 언어의 래퍼를 작성하거나 직접 작성할 수도 있습니다).

프로그램은 Tank다음과 같은 방법으로 인터페이스 를 구현합니다 .

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

Battlefield클래스에는 전장의 사물을 나타내는 2D 객체 배열 ( Battlefield.FIELD_SIZEby Battlefield.FIELD_SIZE)이 포함되어 있습니다 . Battlefield.getObjectTypeAt(...)를 제공 FieldObjectType지정된 좌표에 대한 오브젝트 (중 하나를 FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, 또는 FieldObjectType.NOTHING). 맵 범위를 벗어난 객체 (좌표 <0 또는> = Battlefield.FIELD_SIZE) 를 얻으려고 IllegalArgumentException하면가 발생합니다.

MapPoint지도에서 점을 지정하기위한 클래스입니다. 사용 MapPoint.getX()MapPoint.getY()좌표에 액세스 할 수 있습니다.

편집 : 일부 유틸리티 메소드가 추가되었습니다 : MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType),과 TurnAction.createShootActionRadians(double)에 의해 제안 Wasmoo .

자세한 정보는 javadocs에서 찾을 수 있습니다 (아래 섹션 참조).

모든 (공용 API) 클래스는 패키지 아래에 zove.ppcg.tankwar있습니다.

제어 프로그램

제어 프로그램 및 탱크 API의 전체 소스 및 javadoc은 내 GitHub 저장소에서 찾을 수 있습니다 : https://github.com/Hungary-Dude/TankWarControl

버그가 있거나 개선을 원할 경우 풀 요청 및 / 또는 의견을 보내주십시오.

나는 두 개의 샘플 탱크 프로그램을 작성 RandomMoveTank했으며 RandomShootTank(이름이 다 말해줍니다).

탱크를 가동하려면 정규화 된 (패키지 이름 + 클래스 이름) 탱크 클래스를 tanks.list(한 줄에 한 클래스)에 추가하고, 필요에 따라 설정을 편집하십시오 zove.ppcg.tankwar.Control(턴 지연, 필드의 GUI 표시 표시 여부 등), 실행합니다 zove.ppcg.tankwar.Control. 목록에 탱크가 2 개 이상 있거나 결과가 정의되어 있지 않은지 확인하십시오. (필요한 경우 샘플 탱크를 사용하십시오).

귀하의 프로그램은이 제어 프로그램에 따라 내 컴퓨터에서 실행됩니다. 소스를 작성하면 소스에 대한 링크를 포함시킵니다. 소스 편집을 제안하십시오.

규칙

  • 제출물은 위의 지침을 따라야합니다
  • 귀하의 프로그램은 파일 시스템, 네트워크에 액세스하거나 어떤 방식 으로든 내 컴퓨터를 공격하려고 시도 할 수 없습니다
  • 귀하의 프로그램이 내 제어 프로그램을 악용하여 시도하지 않을 수 있습니다
  • 트롤링 금지 (예 : 의도적으로 모든 것을 끊기 위해 프로그램을 낭비하는 시간)
  • 제출이 둘 이상있을 수 있습니다
  • 제출물로 창의력을 발휘하십시오!
  • 프로그램을 임의로 허용 할 수있는 권리를 보유합니다

행운을 빕니다!

업데이트 : 벽 순간 이동 버그를 수정하고 재생성을 구현 한 후 100 라운드 동안 현재 제출물을 실행했습니다.Battlefield.FIELD_SIZE = 30

업데이트 2 : Groovy에 약간의 속임수를 쓴 후 새로운 제출 인 RunTank를 추가했습니다 ...

업데이트 된 결과 :

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

현재 전차는 1 턴마다 1의 생명력을 재생합니다. 그 증가해야합니까?


1
MapPoint이야 ' x하고 y floats? 그들이해서는 안 ints됩니까?
IchBinKeinBaum

좋은 지적. 왜 내가 수레로하기로 결정했는지 모르겠습니다. 나는 그것들을 정수로 바꿀 것이다. 편집 : int로 업데이트하고 repo를 확인하십시오.
DankMemes

1,1 지점에 서서 0도 각도로 발사하면 발사체가 동쪽 방향으로갑니다.
CommonGuy

@ 마누 예. 명확하지 않은 경우 죄송합니다.
DankMemes

몇 가지 오류가 발견되었습니다. Battlefield.java:88 때때로 obj가 null입니다 (이동 동작이 보류 중일 때 탱크가 죽을 때) Control.java:151 탱크가 동시에 서로를 죽일 때 get (0)이 유효하지 않습니다
Wasmoo

답변:


2

헌터 킬러

이 지능형 사냥꾼은 정확히 하나의 목표물을 깨끗하게 쏠 수있는 안전한 위치를 찾으려고 시도합니다. (따라서 하나의 대상 만 쏠 수 있습니다)

엄폐물이 많을 때 가장 잘 작동합니다.

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

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

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

    //A description of a move
    private class Move {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

그리고 그게 다야. 나는 보냈다.


2

이 직설 형 전차는 가장 가까운 적 전차를 찾아 사격합니다. 좋은 것인지 find, distanceangle내장되었고, 경우 createShootAction라디안 (의 예 결과를 이중 가능 angle)

편집 : 새로운 유틸리티 메소드를 포함하도록 클래스가 재 작성되었습니다.

public final class ShootClosestTank implements Tank {

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

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

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

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

    }

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

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

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

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

Would be nice if find, distance, and angle were built in, and if createShootAction accepted a double in radians (i.e. the result of angle)-좋은 생각입니다. 추가하겠습니다.
DankMemes

1

나는 이것에 능숙하지는 않지만 나는 아직도 그것을 쏴 줄 알았는데 연습과 물건.

내 전차가 무작위로 움직이거나 쏠 것입니다. 촬영을 결정하면 가장 가까운 대상을 향해 촬영을 시도합니다.

package com.richarda.tankwar;

import zove.ppcg.tankwar.*;

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


public class RichardATank implements Tank
{
    private String name;

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

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

    }

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

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

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

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

    }

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

    }

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

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

        double firingAngle = curTankLocation.angleBetween(nearestTankLoc);

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

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

        MapPoint nearestTankLoc = null;

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

        return nearestTankLoc;
    }

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

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

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

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

        return tanks;
    }
}

제어 프로그램을 포함한 전체 코드는 여기 에서 찾을 수 있습니다 .


시도Direction.getRandom()
DankMemes

@ZoveGames 팁을 주셔서 감사합니다.
MisterBla

1

닷지 탱크

이 탱크는 가장 가까운 탱크에서 발사됩니다. 건강과 마지막 이동 시간에 따라 레이저를 피하려고 가장 가까운 탱크에 수직으로 이동하려고 시도하는 경우가 종종 있습니다.

public class DodgeTank implements Tank {

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

1

생각보다 복잡했습니다 ..

이것은 groovy의 내 항목입니다. groovy를 설치하고 컴파일해야합니다.

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

이를 호출하려면 $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (또는 모든 버전)을 클래스 경로에 추가해야합니다.

컴파일하고 싶지 않은 경우 컴파일 된 .class 파일과 라이브러리를 보낼 수 있습니다.

탱크가 다른 탱크를 볼 수없는 상황이있는 것 같습니다. 그것이 의도 된 것인지 모르겠습니다. 테스트하는 동안 교착 상태가 발생했습니다.

어쨌든 여기에 RunTank가 있습니다 : RunTank는 가장 가까운 탱크에 가장 가까운 탱크이거나 하나 이상의 탱크가 FIELD_SIZE / 3 내에 있으면 가장 가까운 탱크의 반대 방향으로 전진합니다.

package zove.ppcg.tankwar.felsspat

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

public class RunTank implements Tank {  

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

        if (targets) {

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

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

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

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

    }

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

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

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

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

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

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

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

            default:
            println "I'm stupid :("

        }
    }

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

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

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

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

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

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

탱크에 색상을 추가하고 구현 방법을 제안합니다. 또한 레이블은 GUI에서 좋을 것입니다 :)


def RandomMoveTank() {}-거기에 있어야합니까? (나는 그루비를 모른다)
DankMemes

아니요, RandomMoveTank를 복사하고 생성자를 제거하는 것을 잊었습니다. 덕분에 :)
Fels

코드를 컴파일하고 .class 파일과 groovy jar가 들어있는 폴더를 프로젝트 클래스 경로에 추가했습니다. 반사가 효과가 있었다! 업데이트 된 점수를 게시했습니다. 당신의 탱크는 꽤 잘했습니다 :)
DankMemes

1
좋은! 그리고 당신은 DodgeTank를 젠장!
Fels

1

이것은 매번 다른 턴마다 더 이상 할 수 없을 때까지 한 방향으로 움직인다는 점에서 Shoot-Closest의 변형입니다. 그것은 모든 차례를 쏜다.

이 유틸리티에는 편리한 유틸리티 path가 있습니다.이 유틸리티 는 두 점 사이의 모든 점 (따라서 객체)을 식별하는 데 사용할 수 있습니다.

public final class BouncingTank implements Tank {

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

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

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

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

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

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

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

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

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

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


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

    public void onDestroyed(Battlefield field, boolean won) {}
    public void onSpawn(Battlefield field, MapPoint position) {}
    public void turnFeedback(MapPoint newPosition, FieldObjectType hit) {}
    public String getName() {
        return "Bouncing Tank";
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.