มาสงครามรถถังกันเถอะ!


18

มาสงครามรถถังกันเถอะ!

แรงบันดาลใจบางส่วนจากDestroy พวกเขาด้วย Lazers

วัตถุประสงค์

งานของคุณคือการควบคุมรถถัง ย้ายไปรอบ ๆ และยิงรถถังและอุปสรรคอื่น ๆ ในสนามรบ 2D รถถังสุดท้ายจะเป็นผู้ชนะ!

รูปแบบแผนที่

รถถังของคุณจะอยู่ในสนาม 2D บนพื้นฐานnจากnตารางของสี่เหลี่ยมหน่วย ฉันจะตัดสินใจว่าอะไรnจะขึ้นอยู่กับจำนวนของการส่ง แต่ละตารางสามารถมีเพียงหนึ่งใน:

  • รถถัง
  • ต้นไม้หนึ่งต้น
  • ร็อค
  • ผนัง
  • ไม่มีอะไร

อุปสรรคและรถถังทั้งหมดเติมเต็มช่องว่างของพวกเขาอย่างสมบูรณ์และพวกเขาปิดกั้นทุกช็อตที่โดนพวกมันจากการทำลายสิ่งต่าง ๆ ลงไปอีก

นี่คือตัวอย่างของฟิลด์ที่มี#= tank; T= ต้นไม้; R= หิน W= ผนัง; .= ไม่มีอะไรกับn= 10

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

พิกัดอยู่ในรูปแบบx, yที่xเพิ่มจากซ้ายไปขวาและyเพิ่มจากล่างขึ้นบน 0, 0พื้นที่ด้านล่างซ้ายมีการประสานงาน แต่ละถังอาจเคลื่อนที่ไปยังพื้นที่ว่างและยิงไปในทิศทางใดก็ได้

แผนที่พลวัต

รถถังของคุณไม่เพียงแค่ต้องยิงรถถังอื่น! ถ้ามันยิงบางอย่างบนแผนที่สิ่งต่าง ๆ ก็สามารถเกิดขึ้นได้

  • หากกำแพงถูกยิงมันจะถูกทำลายหลังจากยิงไปหลายนัดตั้งแต่ 1 ถึง 4
  • ถ้าต้นไม้ถูกยิงมันจะถูกทำลายทันที
  • หากหินถูกยิงที่การยิงจะผ่านมันและสร้างความเสียหายต่อสิ่งที่มันฮิต

เมื่อมีบางสิ่งถูกทำลายมันจะไม่อยู่บนแผนที่อีกต่อไป (จะถูกแทนที่ด้วยไม่มีอะไร) หากการยิงทำลายสิ่งกีดขวางมันจะถูกบล็อกและจะไม่สร้างความเสียหายใด ๆ เพิ่มเติมตามเส้นทางของมัน

พลศาสตร์รถถัง

แต่ละถังเริ่มต้นด้วยlife= 100 แต่ละนัดที่รถถังจะลดลง 20-30 lifeตามระยะทาง นี้สามารถคำนวณได้ด้วยdelta_life=-30+(shot_distance*10/diagonal_map_length)(ที่diagonal_map_lengthเป็น(n-1)*sqrt(2)) นอกจากนี้แต่ละรถถังจะสร้าง 1 lifeเทิร์นละครั้ง

ย้อนกลับ

รอบจำนวนหนึ่งจะถูกเรียกใช้ (ฉันจะตัดสินใจเมื่อมีการส่ง) ในตอนต้นของทุกรอบแผนที่จะสร้างแบบสุ่มและรถถังจะถูกวางไว้ในตำแหน่งที่ว่างเปล่าแบบสุ่ม ในทุกรอบรถถังแต่ละคันจะได้รับเทิร์นตามลำดับโดยพลการใด ๆ หลังจากรถถังทุกคันได้รับเทิร์นพวกเขาจะได้รับผลัดกันในลำดับเดียวกัน รอบจะดำเนินต่อไปจนกว่าจะมีเพียงหนึ่งถังที่เหลือ รถถังนั้นจะเป็นผู้ชนะและพวกเขาจะได้รับ 1 คะแนน เกมจะดำเนินต่อไปในรอบต่อไป

เมื่อรอบหมดแล้วฉันจะโพสต์คะแนนในคำถามนี้

ในช่วงเทิร์นของรถถังมันอาจทำอย่างใดอย่างหนึ่งดังต่อไปนี้

  • เลื่อนได้ถึง 3 ช่องว่างในทิศทางเดียวไม่ว่าจะเป็นแนวนอนหรือแนวตั้ง หากรถถังถูกกีดขวางโดยสิ่งกีดขวางหรือรถถังอื่นมันจะถูกเคลื่อนย้ายไปให้ไกลที่สุดโดยไม่ต้องผ่านสิ่งกีดขวางหรือรถถัง
  • ถ่ายภาพในบางทิศทางโดยมีจุดลอยตัวเป็นองศา แกน x ของพื้นที่ท้องถิ่นของรถถังของคุณ (จากซ้ายไปขวาในแนวนอน, ตะวันออกหรือTurnAction.Direction.EAST ) คือ 0deg และมุมเพิ่มขึ้นทวนเข็มนาฬิกา ภาพไม่ถูกต้องและมุมจริงของภาพอาจสูงกว่าหรือน้อยกว่ามุมที่คุณเลือก 5 องศา
  • ไม่ทำอะไร.

การเปลี่ยนนั้นไม่ จำกัด ในเวลา แต่นี่ไม่ได้หมายความว่าคุณสามารถเสียเวลาในการแขวนทุกอย่าง

การส่ง / พิธีสาร

แต่ละโปรแกรมที่ส่งจะควบคุมรถถังหนึ่งคันบนสนาม โปรแกรมควบคุมอยู่ใน Java ดังนั้นโปรแกรมของคุณต้องอยู่ใน Java ตอนนี้ (ฉันอาจจะเขียน wrapper สำหรับภาษาอื่น ๆ ในบางจุดหรือคุณอาจเขียนของคุณเอง)

โปรแกรมของคุณจะใช้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ชั้นมีอาร์เรย์ 2 มิติของวัตถุ ( Battlefield.FIELD_SIZEโดย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เรียนอยู่ภายใต้แพคเกจ

โปรแกรมควบคุม

แหล่งที่มาเต็มรูปแบบและ javadocs ของโปรแกรมควบคุมและถัง API สามารถพบได้บน repo GitHub ของฉัน: https://github.com/Hungary-Dude/TankWarControl

อย่าลังเลที่จะส่งคำขอดึงและ / หรือแสดงความคิดเห็นหากคุณเห็นข้อผิดพลาดหรือต้องการการปรับปรุง

ฉันได้เขียนโปรแกรมตัวอย่างสองโปรแกรมแล้วRandomMoveTankและRandomShootTank(ชื่อบอกมันทั้งหมด)

ในการใช้งานรถถังของคุณให้เพิ่มคลาสที่มีคุณสมบัติครบถ้วน (ชื่อแพ็คเกจ + ชื่อคลาส) ไปยังtanks.list(หนึ่งคลาสต่อบรรทัด) แก้ไขการตั้งค่าตามที่จำเป็นในzove.ppcg.tankwar.Control(เปิดการหน่วงเวลาไม่ว่าจะแสดงการเป็นตัวแทน GUI ของฟิลด์หรือไม่) และเรียกใช้zove.ppcg.tankwar.Controlและเรียกใช้ ตรวจสอบให้แน่ใจว่ามีรถถังอย่างน้อย 2 คันในรายการหรือผลลัพธ์ไม่ได้กำหนด (ใช้ถังตัวอย่างถ้าจำเป็น)

โปรแกรมของคุณจะทำงานบนเครื่องของฉันภายใต้โปรแกรมควบคุมนี้ ฉันจะรวมลิงก์ไปยังแหล่งข้อมูลเมื่อฉันเขียน อย่าลังเลที่จะแนะนำการแก้ไขแหล่งที่มา

กฎระเบียบ

  • ข้อมูลที่คุณส่งจะต้องเป็นไปตามหลักเกณฑ์ด้านบน
  • โปรแกรมของคุณไม่สามารถเข้าถึงระบบไฟล์เครือข่ายหรือพยายามโจมตีเครื่องของฉันในทางใดทางหนึ่ง
  • โปรแกรมของคุณอาจไม่พยายามใช้ประโยชน์จากโปรแกรมควบคุมของฉันในการโกง
  • ไม่มีการหมุนรอบ (เช่นทำให้โปรแกรมของคุณเสียเวลาในการแขวนทุกอย่าง)
  • คุณอาจมีมากกว่าหนึ่งการส่ง
  • พยายามที่จะสร้างสรรค์ด้วยการส่ง!
  • ฉันขอสงวนสิทธิ์ในการอนุญาตหรือไม่อนุญาตโปรแกรมโดยพลการ

โชคดี!

UPDATE:หลังจากแก้ไขข้อผิดพลาดทางเทเลโฟโต้และติดตั้งระบบฟื้นฟูฉันได้ทำการส่งข้อมูลปัจจุบัน 100 รอบด้วยBattlefield.FIELD_SIZE = 30

อัปเดต 2:ฉันเพิ่มการส่งใหม่ RunTank หลังจากหลอก Groovy สักหน่อย ...

อัปเดตผลลัพธ์:

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

รถถังปัจจุบันสร้างใหม่ 1 ชีวิตต่อเทิร์น ที่ควรจะเพิ่มขึ้น?


1
ทำไมMapPoint's xและy floats? พวกเขาไม่ควรจะเป็นints?
IchBinKeinBaum

จุดดี. ฉันไม่แน่ใจว่าทำไมฉันตัดสินใจที่จะทำให้พวกเขาลอย ฉันจะเปลี่ยนพวกเขาเป็น ints แก้ไข : อัปเดตเป็น ints ตรวจสอบ repo
DankMemes

หากคุณยืนอยู่บนจุด 1,1 และยิงด้วยมุม 0 องศากระสุนปืนจะไปในทิศทาง EAST ใช่ไหม
CommonGuy

@ มนูใช่ ฉันขอโทษถ้าไม่ชัดเจน
DankMemes

ฉันพบข้อผิดพลาดเล็กน้อย: Battlefield.java:88 บางครั้ง obj เป็นโมฆะ (ฉันคิดว่าเมื่อรถถังตายเมื่อการเคลื่อนไหวของมันค้างอยู่) Control.java:151 เมื่อรถถังฆ่ากันพร้อมกันรับ (0) ไม่ถูกต้อง
Wasmoo

คำตอบ:


2

HunterKiller

นักล่าอัจฉริยะนี้จะพยายามค้นหาตำแหน่งที่ปลอดภัยที่สามารถยิงเป้าหมายได้อย่างสะอาดตา (และเป้าหมายเดียวเท่านั้นที่สามารถยิงได้)

ทำงานได้ดีที่สุดเมื่อมีความคุ้มครองมากมาย

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, distanceและangleถูกสร้างขึ้นและถ้า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 (หรือรุ่นใดก็ตาม) ลงใน classpath

ฉันสามารถส่งไฟล์. 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() {}- นั่นหมายความว่าจะมีหรือไม่ (ฉันไม่ทราบว่ามันเป็น Groovy)
DankMemes

ไม่ฉันคัดลอก RandomMoveTank และลืมที่จะเอาตัวสร้างขอบคุณ :)
Fels

ฉันรวบรวมรหัสของคุณและเพิ่มโฟลเดอร์ที่มีไฟล์. class และ jar groovy ใน classpath โครงการของฉัน การสะท้อนกลับใช้งานได้! ฉันโพสต์คะแนนที่อัปเดตแล้ว รถถังของคุณทำได้ค่อนข้างดี :)
DankMemes

1
ดี! และเจ้าดอดจ์ถัง!
Fels

1

อันนี้เป็นรูปแบบที่แตกต่างกันในการถ่ายภาพที่ใกล้ที่สุดในทุก ๆ ทางมันจะเคลื่อนที่ไปในทิศทางเดียวจนกว่ามันจะไม่สามารถทำได้อีกต่อไป มันยิงกันทุกตา

มันมียูทิลิตี้ที่มีประโยชน์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.