Hãy có một cuộc chiến xe tăng!


18

Hãy có một cuộc chiến xe tăng!

Lấy cảm hứng một phần từ Tiêu diệt chúng với Lazers

Mục tiêu

Nhiệm vụ của bạn là điều khiển một chiếc xe tăng. Di chuyển xung quanh và bắn các xe tăng và chướng ngại vật khác trong chiến trường 2D. Người đứng cuối xe tăng sẽ là người chiến thắng!

Định dạng bản đồ

Xe tăng của bạn sẽ được trên một lĩnh vực 2D dựa trên một nbằng nmạng lưới các ô vuông đơn vị. Tôi sẽ quyết định những gì ndựa trên số lượng bài nộp. Mỗi ô vuông chỉ có thể chứa một trong số:

  • Một chiếc xe tăng
  • Một cái cây
  • Một hòn đá
  • Bức tường
  • Không có gì

Tất cả các chướng ngại vật và xe tăng hoàn toàn lấp đầy không gian của chúng, và chúng chặn tất cả các phát bắn trúng chúng làm hỏng mọi thứ hơn nữa.

Dưới đây là một ví dụ về trường có #= tank; T= cây; R= đá; W= tường; .= không có gì với n= 10

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

Các tọa độ có định dạng x, ytrong đó xtăng từ trái sang phải và ytăng từ dưới lên trên. Không gian phía dưới bên trái có tọa độ 0, 0. Mỗi xe tăng có thể di chuyển đến bất kỳ không gian trống nào và bắn theo bất kỳ hướng nào.

Bản đồ động

Xe tăng của bạn không cần phải bắn các xe tăng khác! Nếu nó bắn một cái gì đó trên bản đồ, mọi thứ có thể xảy ra.

  • Nếu một bức tường bị bắn vào, nó sẽ bị phá hủy sau một số lần bắn, từ 1 đến 4
  • Nếu một cây bị bắn vào, nó sẽ bị phá hủy ngay lập tức
  • Nếu một hòn đá bị bắn vào, cú bắn sẽ vượt qua nó và làm hỏng thứ tiếp theo nó bắn trúng

Một khi một cái gì đó bị phá hủy, nó không còn trên bản đồ nữa (nó sẽ được thay thế bằng không có gì). Nếu một phát bắn phá hủy một chướng ngại vật, nó sẽ bị chặn và sẽ không gây sát thương gì thêm trên đường đi của nó.

Động lực học xe tăng

Mỗi bể bắt đầu bằng life= 100. Mỗi phát bắn vào một chiếc xe tăng sẽ giảm 20-30 lifedựa trên khoảng cách. Điều này có thể được tính bằng delta_life=-30+(shot_distance*10/diagonal_map_length)(nơi diagonal_map_length(n-1)*sqrt(2)). Ngoài ra, mỗi bể tái sinh 1 lifemỗi lượt.

Biến

Một số vòng sẽ được chạy (tôi sẽ quyết định khi tôi có bài dự thi). Vào đầu mỗi vòng, một bản đồ sẽ được tạo ngẫu nhiên và xe tăng sẽ được đặt trên đó ở các vị trí trống ngẫu nhiên. Trong mỗi vòng, mỗi xe tăng sẽ được trao một lượt, theo bất kỳ thứ tự tùy ý nào. Sau khi mỗi chiếc xe tăng được đưa ra một lượt, chúng sẽ được quay lại theo thứ tự tương tự. Vòng đấu tiếp tục cho đến khi chỉ còn một chiếc xe tăng. Chiếc xe tăng đó sẽ là người chiến thắng, và họ sẽ nhận được 1 điểm. Trò chơi sau đó sẽ đi vào vòng tiếp theo.

Khi tất cả các vòng đã được chạy, tôi sẽ đăng điểm cho câu hỏi này.

Trong lượt của một chiếc xe tăng, nó có thể làm một trong những điều sau đây

  • Di chuyển tối đa 3 không gian theo một hướng, theo chiều ngang hoặc chiều dọc. Nếu xe tăng bị chặn bởi một chướng ngại vật hoặc xe tăng khác, nó sẽ được di chuyển càng xa càng tốt mà không đi qua chướng ngại vật hoặc xe tăng.
  • Chụp theo một số hướng, được biểu thị bằng một góc điểm nổi theo độ. Trục x của không gian cục bộ của bể của bạn (theo chiều ngang từ trái sang phải, còn gọi là hướng đông hoặc TurnAction.Direction.EAST) là 0deg và các góc tăng ngược chiều kim đồng hồ. Ảnh chụp không chính xác và góc chụp thực tế có thể lớn hơn hoặc 5 độ so với góc bạn chọn.
  • Không làm gì cả.

Lần lượt không bị giới hạn về thời gian, nhưng điều này không có nghĩa là bạn có thể cố tình lãng phí thời gian để treo mọi thứ lên.

Đệ trình / Giao thức

Mỗi chương trình được gửi sẽ kiểm soát một xe tăng trên sân. Chương trình điều khiển là bằng Java, vì vậy bây giờ các chương trình của bạn cần phải ở trong Java (tôi có thể sẽ viết một trình bao bọc cho các ngôn ngữ khác tại một số điểm hoặc bạn có thể tự viết).

Các chương trình của bạn sẽ thực hiện Tankgiao diện, có các phương pháp sau:

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

Các Battlefieldlớp có chứa một mảng 2D của các đối tượng ( Battlefield.FIELD_SIZEbằng Battlefield.FIELD_SIZE) đại diện cho những thứ trên chiến trường. Battlefield.getObjectTypeAt(...)sẽ cung cấp một FieldObjectTypecho đối tượng tại các tọa độ quy định (một trong những FieldObjectType.ROCK, FieldObjectType.TREE, FieldObjectType.TANK, FieldObjectType.WALL, hoặc FieldObjectType.NOTHING). Nếu bạn cố gắng đưa một đối tượng ra khỏi phạm vi của bản đồ (tọa độ <0 hoặc> = Battlefield.FIELD_SIZE) thì IllegalArgumentExceptionsẽ bị ném.

MapPointlà một lớp để chỉ định các điểm trên bản đồ. Sử dụng MapPoint.getX()MapPoint.getY()truy cập tọa độ.

EDIT: Một số phương pháp hữu ích đã được thêm vào: MapPoint.distanceTo(MapPoint), MapPoint.angleBetween(MapPoint), Battlefield.find(FieldObjectType), và TurnAction.createShootActionRadians(double)theo đề nghị của Wasmoo .

Thông tin thêm có thể được tìm thấy trong javadocs, xem phần bên dưới.

Tất cả các lớp (API công khai) đều nằm dưới gói zove.ppcg.tankwar.

Chương trình điều khiển

Có thể tìm thấy toàn bộ nguồn và javadocs của chương trình điều khiển và API xe tăng trên repo GitHub của tôi: https://github.com/Hungary-Dude/TankWarControl

Vui lòng gửi yêu cầu kéo và / hoặc nhận xét nếu bạn thấy lỗi hoặc muốn cải thiện.

Tôi đã viết hai chương trình xe tăng mẫu, RandomMoveTankRandomShootTank(cái tên nói lên tất cả).

Để chạy xe tăng của bạn, hãy thêm lớp xe tăng (tên gói + tên lớp) đủ điều kiện của bạn vào tanks.list(một lớp trên mỗi dòng), chỉnh sửa cài đặt nếu cần trong zove.ppcg.tankwar.Control(bật trễ, có hay không hiển thị đại diện GUI của trường, v.v.), và chạy zove.ppcg.tankwar.Control. Đảm bảo có ít nhất 2 xe tăng trong danh sách, hoặc kết quả không được xác định. (Sử dụng các bể mẫu nếu cần thiết).

Các chương trình của bạn sẽ được chạy trên máy của tôi theo chương trình điều khiển này. Tôi sẽ bao gồm một liên kết đến nguồn khi tôi viết nó. Hãy đề nghị chỉnh sửa nguồn.

Quy tắc

  • Bài dự thi của bạn phải tuân theo các nguyên tắc trên
  • Các chương trình của bạn không được truy cập hệ thống tập tin, mạng hoặc cố gắng tấn công máy của tôi dưới bất kỳ hình thức nào
  • Các chương trình của bạn không được phép khai thác chương trình kiểm soát của tôi để gian lận
  • Không trolling (chẳng hạn như cố ý làm cho chương trình của bạn lãng phí thời gian để treo mọi thứ lên)
  • Bạn có thể có nhiều hơn một lần gửi
  • Hãy cố gắng sáng tạo với các bài nộp!
  • Tôi bảo lưu quyền cho phép hoặc không cho phép các chương trình tùy ý

Chúc may mắn!

CẬP NHẬT: Sau khi sửa lỗi dịch chuyển tức thời trên tường và thực hiện tái tạo, tôi đã chạy các bài nộp hiện tại trong 100 vòng vớiBattlefield.FIELD_SIZE = 30

CẬP NHẬT 2: Tôi đã thêm bài đăng mới, RunTank, sau khi đánh lừa Groovy một chút ...

Kết quả cập nhật:

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

Hiện tại xe tăng tái sinh 1 cuộc sống mỗi lượt. Có nên tăng không?


1
Tại sao MapPoint's xy floats? Họ không nênints ?
IchBinKeinBaum

Điểm tốt. Tôi không chắc tại sao tôi quyết định làm cho chúng nổi. Tôi sẽ thay đổi chúng thành ints. Chỉnh sửa : cập nhật chúng thành ints, kiểm tra repo
DankMeme

Nếu bạn đứng ở điểm 1,1 và bắn với góc 0 độ, đạn sẽ đi theo hướng EAST, phải không?
CommonGuy

@Manu Vâng. Tôi xin lỗi nếu điều đó không rõ ràng.
DankMeme

Tôi đã tìm thấy một vài lỗi: Battlefield.java:88 Đôi khi obj là null (Tôi nghĩ khi một chiếc xe tăng chết khi hành động di chuyển của nó đang chờ xử lý) Control.java:151 Khi các xe tăng đồng thời giết nhau, get (0) không hợp lệ
Wasmoo

Câu trả lời:


2

Thợ săn

Thợ săn thông minh này sẽ cố gắng tìm một vị trí an toàn nơi nó có thể bắn sạch vào đúng một mục tiêu. (Và do đó, chỉ có một mục tiêu có thể bắn nó)

Hoạt động tốt nhất khi có nhiều bìa.

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

}

Và đó là nó. Tôi mệt quá rồi.


2

Xe tăng thẳng này tìm thấy xe tăng địch gần nhất và bắn vào nó. Sẽ tốt nếu find, distanceangleđược xây dựng trong, và nếucreateShootAction được chấp nhận gấp đôi số radian (tức là kết quả của angle)

Chỉnh sửa: Lớp viết lại để bao gồm các phương thức tiện ích mới

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)- Ý tưởng tuyệt vời, tôi sẽ thêm nó vào.
DankMeme

1

Tôi không giỏi trong việc này nhưng tôi nghĩ tôi vẫn sẽ thử, bạn biết đấy, luyện tập và các thứ.

Xe tăng của tôi sẽ ngẫu nhiên quyết định di chuyển hoặc bắn. Khi nó quyết định bắn, nó sẽ cố bắn vào mục tiêu khả dụng gần nhất.

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

Mã đầy đủ bao gồm cả chương trình điều khiển có thể được tìm thấy ở đây .


Hãy thửDirection.getRandom()
DankMeme

@ZoveGames Đã chỉnh sửa nó, cảm ơn vì tiền boa.
MisterBla

1

Xe tăng Dodge

Xe tăng này sẽ bắn vào xe tăng gần nhất. Mỗi lần như vậy, tùy thuộc vào sức khỏe của nó và lần cuối cùng nó di chuyển, nó sẽ cố gắng di chuyển vuông góc với chiếc xe tăng gần nhất trong nỗ lực né tránh tia laser của nó.

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

Đây là cách phức tạp hơn tôi nghĩ ..

Đây là mục nhập của tôi trong Groovy, bạn cần cài đặt Groovy và biên dịch nó với

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

Để gọi nó, bạn cần thêm $ GROOVY_HOME / Groovy / Groovy-2.3.4 / lib / groovy-2.3.4.jar (hoặc bất kỳ phiên bản nào) vào đường dẫn lớp.

Tôi có thể gửi cho bạn một tệp. Class đã biên dịch và thư viện nếu bạn không muốn cài đặt nó.

Dường như có một tình huống mà xe tăng không thể nhìn thấy nó khác, tôi không biết nếu đó là dự định. Điều đó gây ra bế tắc trong khi thử nghiệm.

Dù sao ở đây là RunTank: RunTank mạnh dạn tiến theo hướng ngược lại với chiếc xe tăng gần nhất nếu đó là chiếc xe tăng gần nhất với chiếc xe tăng gần nhất hoặc nhiều hơn một chiếc xe tăng nằm trong FIELD_SIZE / 3. Tôi hy vọng điều đó có ý nghĩa, tôi đang say :)

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

Tôi có một đề nghị: Thêm màu sắc vào bể, và một phương pháp để thực hiện nó. Ngoài ra các nhãn sẽ tốt trong GUI :)


def RandomMoveTank() {}- điều đó có nghĩa là ở đó? (Tôi không biết
Groovy

Không, tôi đã sao chép RandomMoveTank và quên xóa hàm tạo, cảm ơn :)
Fels

Tôi đã biên dịch mã của bạn và thêm thư mục chứa các tệp. Class và jar Groovy vào đường dẫn dự án của tôi. Phản ánh đã làm việc! Tôi đã đăng điểm cập nhật. Xe tăng của bạn đã hoạt động khá tốt :)
DankMeme

1
Đẹp! Và chết tiệt DodgeTank!
Fels

1

Đây là một biến thể của Shoot-Recent ở chỗ, mỗi lượt khác, nó di chuyển theo một hướng cho đến khi không còn có thể. Nó bắn mỗi lượt khác.

Nó có một tiện ích tiện dụng, pathcó thể được sử dụng để xác định tất cả các điểm (và do đó các đối tượng) giữa hai điểm.

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";
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.