Scriptbot Warz!


14

Scriptbot Warz!


Kết quả là và Assassin là nhà vô địch của chúng tôi, chiến thắng 2/3 trận đấu! Cảm ơn tất cả những người đã gửi Scriptbots của họ! Đặc biệt cảm ơn sừng cho BestOpportunityBot đã hiển thị đường dẫn tuyệt vời và sử dụng đầy đủ tất cả các tùy chọn hành động.

Bản đồ 1

Assassin đã loại BestOpportunityBot từ rất sớm và phần còn lại của trận đấu khá nhàm chán. Chơi chi tiết tại đây.

  1. Sát thủ: 10 HP, 10 sát thương, 3 sát thương
  2. Bộ tránh v3: 10 HP, 0 sát thương, 0 sát thương
  3. Ăn xong: 10 HP, 0 sát thương, 0 sát thương
  4. BestOpportunityBot: 0 HP, 3 sát thương, 10 sát thương

Bản đồ 2

BestOpportunityBot đã làm hầu hết các công việc trong trận đấu này, nhưng Assassin cuối cùng đã có thể đưa anh ta ra ngoài. Chơi chi tiết tại đây.

  1. Sát thủ: 2 HP, 10 sát thương, 9 sát thương
  2. BestOpportunityBot: 0 HP, 32 sát thương, 10 sát thương
  3. Công cụ tránh v3: 0 HP, 0 sát thương, 12 sát thương
  4. Ăn xong: 0 HP, 0 sát thương, 11 sát thương

Bản đồ 3

BestOpportunityBot đã đẩy mọi người vào bẫy trong trận đấu này. Rất tuyệt. Chơi chi tiết tại đây.

  1. BestOpportunityBot: 10 HP, 30 sát thương, 0 sát thương
  2. Sát thủ: 0 HP, 0 sát thương gây ra, 0 sát thương gây ra
  3. Ăn xong: 0 HP, 0 sát thương, 0 sát thương
  4. Bộ tránh v3: 0 HP, 0 sát thương, 0 sát thương

Cảm ơn câu trả lời của bạn! Vì chỉ có 4 Scriptbots, chúng tôi đang từ bỏ các kế hoạch giải đấu cho ba trận đấu miễn phí cho tất cả các trận đấu, một trận đấu trên mỗi bản đồ bên dưới. Các scriptbot với kỷ lục chiến thắng cao nhất chiến thắng. Trong trường hợp hòa, chúng ta sẽ rơi vào cái chết bất ngờ, trong đó scriptbot phá vỡ cà vạt trước sẽ thắng.


Nhiệm vụ của bạn, nếu bạn chọn chấp nhận nó, là viết mã Scriptbot có thể đi qua bản đồ ASCII và tiêu diệt đối thủ của nó. Mỗi trận chiến sẽ có hình thức của một trò chơi theo lượt bắt đầu theo thứ tự ngẫu nhiên, trong đó mỗi Scriptbot có cơ hội sử dụng điểm năng lượng (EP) để hành động. Tập lệnh GameMaster sẽ cung cấp đầu vào và giải thích đầu ra từ mỗi Scriptbot.

Môi trường

Mỗi Scriptbot được chứa trong thư mục riêng của nó, nơi nó có thể đọc từ mapstatscác tệp và đọc / ghi vào datatệp. Các datatập tin có thể được sử dụng để lưu trữ bất kỳ thông tin liên tục mà bạn có thể thấy hữu ích.

Tập tin thống kê

Các statstập tin chứa thông tin về đối thủ của bạn và được định dạng như sau. Mỗi người chơi được đại diện trên một hàng riêng biệt. Cột đầu tiên là ID người chơi ( @có nghĩa là bạn). Cột thứ hai là sức khỏe của người chơi đó.

1,9HP
@,10HP
3,9HP
4,2HP

Tệp bản đồ

Các maptập tin có thể trông giống như thế này ...

####################
#   #          #   #
# 1 #          # 2 #
#                  #
###              ###
#                  #
#      #           #
#       #     !    #
#        #         #
#       !####      #
#      ####!       #
#         #        #
#    !     #       #
#           #      #
#                  #
###              ###
#                  #
# 3 #          # @ #
#   #          #   #
####################

... hoặc cái này ...

######################################
#       # 1        #   @             #
#       #          #          #!     #
#       #          #          ####   #
#  #    #  #       #         !#!     #
#  #    #  #       #      #####      #
#  ###     #    ####    #         #  #
#          #    #!      ###       #  #
#  ######  #    #       #     #####  #
#  #!      #    ######  #        !#  #
#  ###     #            #         #  #
#  #    2  #            #   4     #  #
######################################

... hoặc cái này ...

###################
###!!!!!!#!!!!!!###
##!             !##
#! 1     !     2 !#
#!       !       !#
#!               !#
#!               !#
#!      !!!      !#
## !!   !!!   !! ##
#!      !!!      !#
#!               !#
#!               !#
#!       !       !#
#! 3     !     @ !#
##!             !##
###!!!!!!#!!!!!!###
###################

... Hoặc nó có thể trông hoàn toàn khác nhau. Dù bằng cách nào, các ký tự được sử dụng và ý nghĩa của chúng sẽ vẫn giống nhau:

  • # Một bức tường, không thể vượt qua và không thể xuyên thủng.
  • 1, 2, 3... Một số đại diện cho một cầu thủ đối phương. Những con số này tương ứng với ID người chơi trong statstệp.
  • !Một cái bẫy. Scriptbots di chuyển đến các địa điểm này sẽ chết ngay lập tức.
  • @ Vị trí Scriptbot của bạn.
  • Không gian mở mà bạn có thể tự do di chuyển trong.

Trò chơi

Tập lệnh GameMaster sẽ chỉ định một thứ tự bắt đầu ngẫu nhiên cho Scriptbots. Các Scriptbots sau đó được gọi theo thứ tự này trong khi chúng vẫn còn sống. Scriptbots có 10 Điểm Sức khỏe (HP) và bắt đầu với 10 Điểm Năng lượng (EP) mỗi lượt, chúng có thể sử dụng để di chuyển hoặc tấn công. Khi bắt đầu mỗi lượt, Scriptbot sẽ hồi máu cho một HP hoặc được cấp thêm một EP nếu đã ở mức 10 HP (do đó, việc chạy có thể là chiến lược khả thi vào các thời điểm).

Trận chiến kết thúc khi chỉ có một Scriptbot sống sót hoặc khi 100 lượt đã trôi qua. Nếu nhiều Scriptbots còn sống khi kết thúc trận chiến, vị trí của chúng được xác định dựa trên các tiêu chí sau:

  1. Sức khỏe nhất.
  2. Hầu hết các thiệt hại gây ra.
  3. Hầu hết thiệt hại thực hiện.

Đầu vào Scriptbot

GameMaster sẽ in bản đồ chiến đấu thành một tệp có tên mapScriptbot sẽ có quyền truy cập để đọc từ đó. Bản đồ có thể có bất kỳ hình thức nào, vì vậy điều quan trọng là Scriptbot có thể diễn giải nó. Scriptbot của bạn sẽ được gọi với một tham số cho biết EP. Ví dụ...

:> example_scriptbot.py 3

Scriptbot sẽ được gọi cho đến khi nó dành tất cả EP hoặc tối đa 10 11 lần. Các tập tin bản đồ và số liệu thống kê được cập nhật trước mỗi lần gọi.

Đầu ra Scriptbot

Scriptbots nên xuất hành động của họ thành mập mạp. Một danh sách các hành động có thể như sau:

  • MOVE <DIRECTION> <DISTANCE>

    Chi phí 1 EP mỗi DISTANCE. Các MOVElệnh di chuyển Scriptbot của bạn xung quanh bản đồ. Nếu có thứ gì đó cản đường như một bức tường hoặc một Scriptbot khác, GameMaster sẽ di chuyển Scriptbot của bạn càng xa càng tốt. Nếu DISTANCEsố EP lớn hơn của Scriptbot được đưa ra, GameMaster sẽ di chuyển Scriptbot cho đến khi hết EP. DIRECTIONcó thể là bất kỳ hướng la bàn của N, E, S, hoặc W.

  • PUSH <DIRECTION> <DISTANCE>

    Chi phí 1 EP mỗi DISTANCE. Các PUSHlệnh cho phép một Scriptbot để di chuyển Scriptbot khác. Scriptbot ban hành lệnh phải được đặt trực tiếp bên cạnh Scriptbot đang được đẩy. Cả hai Scriptbots sẽ di chuyển theo hướng được chỉ định nếu không có đối tượng chặn Scriptbot bị đẩy. DIRECTIONDISTANCEgiống như đối với MOVElệnh.

  • ATTACK <DIRECTION>

    Chi phí một EP. Các ATTACKlệnh giao dịch 1 thiệt hại cho bất kỳ Scriptbot trực tiếp bên cạnh Scriptbot ban hành và theo hướng quy định. DIRECTIONcũng giống như đối với MOVElệnh.

  • PASS

    Kết thúc lượt của bạn.

Ngôn ngữ được hỗ trợ

Để giữ điều này hợp lý với tôi, tôi sẽ chấp nhận các ngôn ngữ sau:

  • Java
  • Node.js
  • Con trăn
  • PHP

Bạn bị giới hạn trong các thư viện thường được đóng gói với các ngôn ngữ của bạn. Vui lòng không làm cho tôi xác định vị trí các thư viện tối nghĩa để làm cho mã của bạn hoạt động.

Trình và đánh giá

Gửi mã nguồn Scriptbot của bạn dưới đây và đặt cho nó một cái tên hay! Vui lòng liệt kê phiên bản của ngôn ngữ bạn đã sử dụng. Tất cả các Scriptbots sẽ được xem xét cho tomfoolery, vì vậy vui lòng bình luận tốt và không làm xáo trộn mã của bạn.

Bạn có thể gửi nhiều mục, nhưng vui lòng tạo chúng cho các mục hoàn toàn độc đáo và không phải là phiên bản của cùng một mục. Chẳng hạn, bạn có thể muốn mã hóa bot Zerg Rush và bot Gorilla Warfare. Tốt rồi. Không đăng Zerg Rush v1, Zerg Rush v2, v.v.

Vào ngày 7 tháng 11, tôi sẽ thu thập tất cả các câu trả lời và những câu trả lời đánh giá ban đầu sẽ được thêm vào khung giải đấu. Nhà vô địch nhận được câu trả lời được chấp nhận. Khung lý tưởng được hiển thị dưới đây. Vì có thể sẽ không có chính xác 16 mục, một số dấu ngoặc có thể chỉ có ba hoặc thậm chí hai bot. Tôi sẽ cố gắng làm cho khung càng công bằng càng tốt. Bất kỳ sự thiên vị cần thiết nào (trong trường hợp cần một tuần tạm biệt) sẽ được trao cho các bot đã được gửi trước.

BOT01_
BOT02_|
BOT03_|____
BOT04_|    |
           |
BOT05_     |
BOT06_|___ |
BOT07_|  | |
BOT08_|  | |_BOT ?_
         |___BOT ?_|
BOT09_    ___BOT ?_|___CHAMPION!
BOT10_|  |  _BOT ?_|
BOT11_|__| |
BOT12_|    |
           |
BOT13_     |
BOT14_|____|
BOT15_|
BOT16_|

Hỏi và đáp

Tôi chắc chắn tôi đã bỏ lỡ một số chi tiết, vì vậy hãy đặt câu hỏi!

Chúng tôi có thể tin tưởng rằng một tệp bản đồ luôn được bao quanh bởi các ký hiệu # không? Nếu không, điều gì xảy ra trong trường hợp bot cố gắng rời khỏi bản đồ? - BrainSteel

Có, bản đồ sẽ luôn bị giới hạn bởi # và Scriptbot của bạn sẽ bắt đầu bên trong các giới hạn này.

Nếu không có bot hiện diện theo hướng được chỉ định trong lệnh PUSH, thì lệnh này hoạt động như thế nào? - BrainSteel

GameMaster sẽ không làm gì cả, zero EP sẽ được chi tiêu và Scriptbot sẽ được gọi lại.

EP không sử dụng tích lũy? - frageum

Không. Mỗi Scriptbot sẽ bắt đầu vòng / lượt với 10 EP. Bất kỳ EP không chi tiêu sẽ lãng phí.

Tôi nghĩ rằng tôi đã có nó, nhưng chỉ cần làm rõ: với bot A và B, là thứ tự của các sự kiện A @ 10EP-> DI CHUYỂN MAP_UPDATE B @ 10EP-> PUSH MAP_UPDATE A @ 9EP-> ATTACK MAP_UPDATE B @ 9EP-> ATTACK ... hoặc A @ 10EP-> DI CHUYỂN A @ 9EP-> ATTACK ... MAP_UPDATE B @ 10EP-> PUSH B @ 9EP-> ATTACK ... MAP_UPDATE? Nói cách khác, tất cả hành động trong một nguyên tử vòng truy vấn bộ điều khiển-bot? Nếu vậy, tại sao vòng lặp? Tại sao không trả lại một tệp duy nhất với tất cả các hành động sẽ được hoàn thành? Nếu không, các bot sẽ phải viết ra các tệp trạng thái của riêng chúng để theo dõi các chuỗi đa hành động. Tệp bản đồ / số liệu thống kê sẽ chỉ có hiệu lực trước hành động đầu tiên. - COTO

Ví dụ thứ hai của bạn là gần, nhưng không hoàn toàn đúng. Trong một lượt chơi, Scriptbot được gọi liên tục cho đến khi EP của họ được sử dụng, hoặc tối đa là 11 lần. Các tập tin bản đồ và số liệu thống kê được cập nhật trước mỗi lần gọi. Vòng lặp rất hữu ích trong trường hợp bot cung cấp đầu ra không hợp lệ. GameMaster sẽ xử lý đầu ra không hợp lệ và liên kết lại bot, tạo cơ hội cho bot sửa lỗi.

bạn sẽ phát hành tập lệnh GameMaster để thử nghiệm chứ? - IchBinKeinBaum

Kịch bản GameMaster sẽ không được phát hành. Tôi sẽ khuyến khích bạn tạo một tệp bản đồ và số liệu thống kê để kiểm tra hành vi của bot.

Nếu robotA đẩy robotB vào bẫy, robotA có được ghi điểm "sát thương" bằng với sức khỏe hiện tại của robotB không? - Mike Sweeney

Vâng đó là một ý tưởng tốt. Một bot sẽ được cấp điểm sát thương tương đương với sức khỏe của bất kỳ bot nào mà nó đẩy vào bẫy.


Chúng tôi có thể tin tưởng rằng một maptập tin luôn được bao quanh bởi #các biểu tượng? Nếu không, điều gì xảy ra trong trường hợp bot cố gắng rời khỏi bản đồ?
BrainSteel

@BrainSteel Có, bản đồ sẽ luôn bị giới hạn #và Scriptbot của bạn sẽ bắt đầu bên trong các giới hạn này.
Rip Leeb

3
Nếu bạn chắc chắn mình đã bỏ lỡ điều gì đó, tại sao không đăng nó vào hộp cát ?
Martin Ender

2
@ MartinBüttner Tôi đã nghĩ điều này khá kỹ lưỡng. Tôi chỉ cố gắng thân thiện và làm rõ rằng các câu hỏi đều được chào đón.
Rip Leeb

1
Nếu robotA đẩy robotB vào bẫy, robotA có được ghi điểm "sát thương" bằng với sức khỏe hiện tại của robotB không?
Logic Knight

Câu trả lời:


1

Sát thủ (Java 1.7)

Cố gắng để giết kẻ thù bất cứ khi nào có thể, nếu không di chuyển một lĩnh vực. Nó khá tốt trong việc tìm đường đến kẻ thù, nhưng không làm gì để trốn tránh các bot khác.

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class Assassin {
    private final Path dataPath = Paths.get("data");
    private final Path mapPath = Paths.get("map");
    private final Path statsPath = Paths.get("stats");
    private final List<Player> players = new ArrayList<>();
    private final int energy;
    private Map map = null;

    public Assassin(int energy) {
        this.energy = energy;
    }

    private void doSomething() {
        if (dataFileEmpty()) {
            calculateTurn();
        }
        printStoredOutput();
    }

    private boolean dataFileEmpty() {
        try {
            return !Files.exists(dataPath) || Files.size(dataPath)  == 0;
        } catch (IOException e) {
            return true;
        }
    }

    private void printStoredOutput() {
        try {
            List<String> lines = Files.readAllLines(dataPath, StandardCharsets.UTF_8);
            //print first line
            System.out.println(lines.get(0));           
            //delete first line
            lines.remove(0);
            Files.write(dataPath, lines, StandardCharsets.UTF_8);
        } catch (IOException e) {
            System.out.println("PASS");
        }
    }

    private void calculateTurn() {
        try {
            readStats();
            readMap();
            if (!tryKill())  {
                sneakCloser();
            }
        } catch (IOException e) {}
    }

    private void readStats() throws IOException{
        List<String> stats = Files.readAllLines(statsPath, StandardCharsets.UTF_8);
        for (String stat : stats) {
            String[] infos = stat.split(",");
            int hp = Integer.parseInt(infos[1].replace("HP", ""));
            players.add(new Player(stat.charAt(0), hp));
        }
    }

    private void readMap() throws IOException{
        List<String> lines = Files.readAllLines(mapPath, StandardCharsets.UTF_8);
        Field[][] fields = new Field[lines.size()][lines.get(0).length()];
        for (int row = 0; row < lines.size(); row++) {
            String line = lines.get(row);
            for (int col = 0; col < line.length(); col++) {
                fields[row][col] = new Field(line.charAt(col), row, col);
            }
        }
        map = new Map(fields);
    }

    private boolean tryKill() {     
        Field me = map.getMyField();
        for (Field field : map.getEnemyFields()) {
            for (Field surrField : map.surroundingFields(field)) { 
                List<Direction> dirs = map.path(me, surrField);
                if (dirs != null) {
                    for (Player player : players) {
                        //can kill this player
                        int remainderEnergy = energy - dirs.size() - player.hp;
                        if (player.id == field.content && remainderEnergy >= 0) {
                            //save future moves
                            List<String> commands = new ArrayList<>();
                            for (Direction dir : dirs) {
                                commands.add("MOVE " + dir + " 1");
                            }
                            //attacking direction
                            Direction attDir = surrField.dirsTo(field).get(0);
                            for (int i = 0; i < player.hp; i++) {
                                commands.add("ATTACK " + attDir);                               
                            }
                            if (remainderEnergy > 0) {
                                commands.add("PASS");
                            }
                            try {
                                Files.write(dataPath, commands, StandardCharsets.UTF_8);
                            } catch (IOException e) {}
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private void sneakCloser() {        
        Field me = map.getMyField();
        for (Direction dir : Direction.values()) {
            if (!map.move(me, dir).blocked()) {
                List<String> commands = new ArrayList<>();
                commands.add("MOVE " + dir + " 1");
                commands.add("PASS");
                try {
                    Files.write(dataPath, commands, StandardCharsets.UTF_8);
                } catch (IOException e) {}
                return;
            }
        }
    }

    public static void main(String[] args) {
        try {
            new Assassin(Integer.parseInt(args[0])).doSomething();
        } catch (Exception e) {
            System.out.println("PASS");
        }
    }

    class Map {
        private Field[][] fields;

        public Map(Field[][] fields) {
            this.fields = fields;
        }

        public Field getMyField() {
            for (Field[] rows : fields) {
                for (Field field : rows) {
                    if (field.isMyPos()) {
                        return field;
                    }
                }
            }
            return null; //should never happen
        }   

        public List<Field> getEnemyFields() {
            List<Field> enemyFields = new ArrayList<>();
            for (Field[] rows : fields) {
                for (Field field : rows) {
                    if (field.hasEnemy()) {
                        enemyFields.add(field);
                    }
                }
            }
            return enemyFields;
        }

        public List<Field> surroundingFields(Field field) {
            List<Field> surrFields = new ArrayList<>();
            for (Direction dir : Direction.values()) {
                surrFields.add(move(field, dir));
            }
            return surrFields;
        }

        public Field move(Field field, Direction dir) {
            return fields[field.row + dir.rowOffset][field.col + dir.colOffset];
        }

        public List<Direction> path(Field from, Field to) {
            List<Direction> dirs = new ArrayList<>();
            Field lastField = from;
            boolean changed = false;

            for (int i = 0; i < energy && lastField != to; i++) {
                List<Direction> possibleDirs = lastField.dirsTo(to);
                changed = false;
                for (Direction dir : possibleDirs) {
                    Field nextField = move(lastField, dir);
                    if (!nextField.blocked()) {
                        lastField = nextField;
                        changed = true;
                        dirs.add(dir);
                        break;
                    }
                }
                if (!changed) {
                    return null; //not possible
                }
            }
            if (lastField != to) {
                return null; //not enough energy
            }           
            return dirs;
        }
    }

    class Field {
        private char content;
        private int row;
        private int col;

        public Field(char content, int row, int col) {
            this.content = content;
            this.row = row;
            this.col = col;
        }

        public boolean hasEnemy() {
            return content == '1' || content == '2' || content == '3' || content == '4';
        }

        public List<Direction> dirsTo(Field field) {
            List<Direction> dirs = new ArrayList<>();
            int distance = Math.abs(row - field.row) + Math.abs(col - field.col);

            for (Direction dir : Direction.values()) {
                int dirDistance = Math.abs(row - field.row + dir.rowOffset) + 
                        Math.abs(col - field.col + dir.colOffset);
                if (dirDistance < distance) {
                    dirs.add(dir);
                }
            }
            if (dirs.size() == 1) { //add near directions
                for (Direction dir : dirs.get(0).nearDirections()) {
                    dirs.add(dir);
                }
            }
            return dirs;
        }

        public boolean isMyPos() {
            return content == '@';
        }

        public boolean blocked() {
            return content != ' ';
        }
    }

    class Player {
        private char id;
        private int hp;

        public Player(char id, int hp) {
            this.id = id;
            this.hp = hp;
        }
    }

    enum Direction {
        N(-1, 0),S(1, 0),E(0, 1),W(0, -1);

        private final int rowOffset;
        private final int colOffset;

        Direction(int rowOffset, int colOffset) {
            this.rowOffset = rowOffset;
            this.colOffset = colOffset;
        }

        public Direction[] nearDirections() {
            Direction[] dirs = new Direction[2];
            for (int i = 0; i < Direction.values().length; i++) {
                Direction currentDir = Direction.values()[i];
                if (Math.abs(currentDir.rowOffset) != Math.abs(this.rowOffset)) {
                    dirs[i%2] = currentDir;
                }
            }
            return dirs;
        }
    }
}

Phiên bản Java này được viết bằng gì?
Rip Leeb

@Nate Tôi đã sử dụng 1.7.
CommonGuy

3

Bộ tránh v3

Một bot có đầu óc đơn giản. Nó sợ robot và bẫy khác. Nó sẽ không tấn công. Nó bỏ qua các tập tin thống kê và không suy nghĩ trước.

Đây chủ yếu là một thử nghiệm để xem các quy tắc sẽ hoạt động như thế nào và là một đối thủ ngu ngốc cho các đối thủ khác.

Chỉnh sửa: Bây giờ sẽ PASS khi không có M CHUYỂN tốt hơn.

Chỉnh sửa2: Robot có thể là '1234' thay vì '123'

Mã Python:

import sys
maxmoves = int(sys.argv[1])

ORTH = [(-1,0), (1,0), (0,-1), (0,1)]
def orth(p):
    return [(p[0]+dx, p[1]+dy) for dx,dy in ORTH]

mapfile = open('map').readlines()[::-1]
arena = dict( ((x,y),ch) 
    for y,row in enumerate(mapfile)
    for x,ch in enumerate(row.strip()) )
loc = [loc for loc,ch in arena.items() if ch == '@'][0]

options = []
for direc in ORTH:
    for dist in range(maxmoves+1):
        newloc = (loc[0]+direc[0]*dist, loc[1]+direc[1]*dist)
        if arena.get(newloc) in '#!1234':
            break
        penalty = dist * 10  # try not to use moves too fast
        if newloc == loc:
            penalty += 32   # incentive to move
        for nextto in orth(newloc):
            ch = arena.get(nextto)
            if ch == '#':
                penalty += 17  # don't get caught againt a wall
            elif ch in '1234':
                penalty += 26  # we are afraid of other robots
            elif ch == '!':
                penalty += 38  # we are very afraid of deadly traps
        options.append( [penalty, dist, direc] )

penalty, dist, direc = min(options)
if dist > 0:
    print 'MOVE', 'WESN'[ORTH.index(direc)], dist
else:
    print 'PASS'  # stay still?

elif ch in '123':Bạn muốn tìm kiếm ít nhất 1234. Nếu bạn là bot 3, danh sách đối thủ sẽ là 124.
Rip Leeb

1
@Nate Cảm ơn. Tôi đã thay đổi bot. Bạn có thể muốn làm rõ điều này trong các quy tắc. Tôi có thể không phải là người duy nhất đã hiểu lầm điều này.
Logic Knight

3

BestOpportunityBot

Điều này cuối cùng đã dài hơn một chút so với dự định của tôi ... và tôi không chắc liệu tôi có hiểu hoàn toàn các quy tắc cho lượt chơi hay không, vì vậy chúng ta sẽ xem điều này diễn ra như thế nào.

from sys import argv
from enum import IntEnum

with open("map") as map_file:
    map_lines = map_file.read().splitlines()

with open("stats") as stats_file:
    stats_data = stats_file.read().splitlines()

ep = argv[1]

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(lhs, rhs):
        if (type(rhs) == tuple or type(rhs) == list):
            return Point(lhs.x + rhs[0], lhs.y + rhs[1])
        return Point(lhs.x + rhs.x, lhs.y + rhs.y)

    def __sub__(lhs, rhs):
        if (type(rhs) == tuple or type(rhs) == list):
            return Point(lhs.x - rhs[0], lhs.y - rhs[1])
        return Point(lhs.x - rhs.x, lhs.y - rhs.y)

    def __mul__(lhs, rhs):
        return Point(lhs.x * rhs, lhs.y * rhs)

    def __str__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __repr__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __eq__(lhs, rhs):
        return lhs.x == rhs.x and lhs.y == rhs.y

    def __hash__(self):
        return hash(self.x) * 2**32 + hash(self.y)

    def reverse(self):
        return Point(self.y, self.x)

dirs = (Point(0, 1), Point(1, 0), Point(0, -1), Point(-1, 0))

class Bot:
    def __init__(self, pos, ch, hp):
        self.pos = pos
        self.ch = ch
        self.hp = hp

    def __str__(self):
        return str(self.pos) + " " + str(self.ch) + " " + str(self.hp)

    def __repr__(self):
        return str(self.pos) + " " + str(self.ch) + " " + str(self.hp)

class MyBot(Bot):
    def __init__(self, pos, ch, hp, ep):
        self.ep = ep
        super().__init__(pos, ch, hp)

    def __str__(self):
        return super().__str__() + " " + self.ep

    def __repr__(self):
        return super().__repr__() + " " + self.ep

class Arena:
    def __init__(self, orig_map):
        self._map = list(zip(*orig_map[::-1]))
        self.bots = []
        self.me = None

    def __getitem__(self, indexes):
        if (type(indexes) == Point):
            return self._map[indexes.x][indexes.y]
        return self._map[indexes[0]][indexes[1]]

    def __str__(self):
        output = ""
        for i in range(len(self._map[0]) - 1, -1, -1):
            for j in range(len(self._map)):
                output += self._map[j][i]
            output += "\n"
        output = output[:-1]
        return output

    def set_bot_loc(self, bot):
        for x in range(len(self._map)):
            for y in range(len(self._map[x])):
                if self._map[x][y] == bot.ch:
                    bot.pos = Point(x, y)

    def set_bots_locs(self):
        self.set_bot_loc(self.me)
        for bot in self.bots:
            self.set_bot_loc(bot)

    def adjacent_bots(self, pos=None):
        if type(pos) == None:
            pos = self.me.pos
        output = []
        for bot in self.bots:
            for d in dirs:
                if bot.pos == pos + d:
                    output.append(bot)
                    break
        return output

    def adjacent_bots_and_dirs(self):
        output = {}
        for bot in self.bots:
            for d in dirs:
                if bot.pos == self.me.pos + d:
                    yield bot, d
                    break
        return output

    def look(self, position, direction, distance, ignore='', stopchars='#1234'):
        current = position + direction
        output = []
        for i in range(distance):
            if (0 <= current.x < len(self._map) and
                0 <= current.y < len(self._map[current.x]) and
                (self[current] not in stopchars or self[current] in ignore)):
                output += self[current]
                current = current + direction
            else:
                break
        return output

    def moveable(self, position, direction, distance):
        current = position + direction
        output = []
        for i in range(distance):
            if (0 <= current.x < len(self._map) and
                0 <= current.y < len(self._map[current.x]) and
                self[current] == ' '):
                output += self[current]
            else:
                break
        return output


    def danger(self, pos, ignore=None): # damage that can be inflicted on me
        output = 0
        adjacents = self.adjacent_bots(pos)
        hps = [bot.hp for bot in adjacents if bot != ignore]
        if len(hps) == 0:
            return output

        while max(hps) > 0:
            if 0 in hps:
                hps.remove(0)

            for i in range(len(hps)):
                if hps[i] == min(hps):
                    hps[i] -= 1
                output += 1
        return output

    def path(self, pos): # Dijkstra's algorithm adapted from https://gist.github.com/econchick/4666413
        visited = {pos: 0}
        path = {}

        nodes = set()

        for i in range(len(self._map)):
            for j in range(len(self._map[0])):
                nodes.add(Point(i, j))

        while nodes:
            min_node = None
            for node in nodes:
                if node in visited:
                    if min_node is None:
                        min_node = node
                    elif visited[node] < visited[min_node]:
                        min_node = node

            if min_node is None:
                break

            nodes.remove(min_node)
            current_weight = visited[min_node]

            for _dir in dirs:
                new_node = min_node + _dir
                if self[new_node] in ' 1234':
                    weight = current_weight + 1
                    if new_node not in visited or weight < visited[new_node]:
                        visited[new_node] = weight
                        path[new_node] = min_node

        return visited, path

class MoveEnum(IntEnum):
    Null = 0
    Pass = 1
    Attack = 2
    Move = 3
    Push = 4

class Move:
    def __init__(self, move=MoveEnum.Null, direction=Point(0, 0), distance=0):
        self.move = move
        self.dir = direction
        self.dis = distance

    def __repr__(self):
        if self.move == MoveEnum.Null:
            return "NULL"
        elif self.move == MoveEnum.Pass:
            return "PASS"
        elif self.move == MoveEnum.Attack:
            return "ATTACK " + "NESW"[dirs.index(self.dir)]
        elif self.move == MoveEnum.Move:
            return "MOVE " + "NESW"[dirs.index(self.dir)] + " " + str(self.dis)
        elif self.move == MoveEnum.Push:
            return "PUSH " + "NESW"[dirs.index(self.dir)] + " " + str(self.dis)

arena = Arena(map_lines)
arena.me = MyBot(Point(0, 0), '@', 0, ep)

for line in stats_data:
    if line[0] == '@':
        arena.me.hp = int(line[2:-2])
    else:
        arena.bots.append(Bot(Point(0, 0), line[0], int(line[2:-2])))

arena.set_bots_locs()

current_danger = arena.danger(arena.me.pos)

moves = [] # format (move, damage done, danger, energy)

if arena.me.ep == 0:
    print(Move(MoveEnum.Pass))
    exit()

for bot, direction in arena.adjacent_bots_and_dirs():
    # Push to damage
    pushable_to = arena.look(arena.me.pos, direction,
                             arena.me.ep + 1, ignore=bot.ch)
    if '!' in pushable_to:
        distance = pushable_to.index('!')
        danger = arena.danger(arena.me.pos + (direction * distance), bot)
        danger -= current_danger
        moves.append((Move(MoveEnum.Push, direction, distance),
                      bot.hp, danger, distance))

    # Push to escape
    pushable_to = arena.look(arena.me.pos, direction,
                             arena.me.ep + 1, ignore=bot.ch, stopchars='#1234!')
    for distance in range(1, len(pushable_to)):
        danger = arena.danger(arena.me.pos + (direction * distance), bot)
        danger += bot.hp
        danger -= current_danger
        moves.append((Move(MoveEnum.Push, direction, distance),
                      0, danger, distance))

    # Attack
    bot.hp -= 1
    danger = arena.danger(arena.me.pos) - current_danger
    moves.append((Move(MoveEnum.Attack, direction), 1, danger, 1))
    bot.hp += 1

culled_moves = []

for move in moves: # Cull out attacks and pushes that result in certain death
    if current_danger + move[2] < arena.me.hp:
        culled_moves.append(move)

if len(culled_moves) == 1:
    print(culled_moves[0][0])
    exit()
elif len(culled_moves) > 1:
    best_move = culled_moves[0]

    for move in culled_moves:
        if move[1] - move[2] > best_move[1] - best_move[2]:
            best_move = move
        if move[1] - move[2] == best_move[1] - best_move[2] and move[3] < best_move[3]:
            best_move = move

    print (best_move[0])
    exit()

# Can't attack or push without dying, time to move

moves = []

if current_danger > 0: # Try to escape
    for direction in dirs:
        moveable_to = arena.moveable(arena.me.pos, direction, ep)

        i = 1

        for space in moveable_to:
            danger = arena.danger(arena.me.pos + (direction * i))
            danger -= current_danger
            moves.append((Move(MoveEnum.Move, direction, i), 0, danger, i))
            i += 1

    if len(moves) == 0: # Trapped and in mortal danger, attack biggest guy
        adjacents = arena.adjacent_bots()
        biggest = adjacents[0]
        for bot in adjacents:
            if biggest.hp < bot.hp:
                biggest = bot
        print (Move(MoveEnum.Attack, biggest.pos - arena.me.pos))
        exit()

    best_move = moves[0]
    for move in moves:
        if ((move[2] < best_move[2] and best_move[2] >= arena.me.hp) or
            (move[2] == best_move[2] and move[3] < best_move[3])):
            best_move = move

    print(best_move[0])
    exit()

else: # Seek out closest target with lower health
    distances, path = arena.path(arena.me.pos)

    bot_dists = list((bot, distances[bot.pos]) for bot in arena.bots)

    bot_dists.sort(key=lambda x: x[1])

    target = bot_dists[0]

    for i in range(len(bot_dists)):
        if bot_dists[i][0].hp <= arena.me.hp:
            target = bot_dists[i]
            break

    pos = target[0].pos
    for i in range(target[1] - 1):
        pos = path[pos]

    print (Move(MoveEnum.Move, pos - arena.me.pos, 1))
    exit()

# Shouldn't get here, but I might as well do something
print (Move(MoveEnum.Pass))

Phiên bản nào của python bạn đã viết này trong?
Rip Leeb

@Nate 3.4.1 trên win32

Cảm ơn bạn đã gửi cái này @horns. Nó thực sự thú vị để xem!
Rip Leeb

1

Ăn xong

Con trăn

import sys
print 'PASS'

1
Tôi đã ở đó - và tại sao địa ngục import sys?
tomsmeding

1
@tomsmeding À tôi phải sao chép vài thứ. Và tôi nghĩ trong trường hợp tôi cần đọc một số lý lẽ :) khi tôi ăn xong, dĩ nhiên.
Timtech
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.