脚本机器人沃兹!


14

脚本机器人沃兹!


结果已经公布,刺客是我们的冠军,赢得了3场比赛中的2场!感谢所有提交脚本机器人的人!特别感谢BestOpportunityBot的号角,它显示了出色的路径并充分利用了所有操作选项。

地图1

刺客很早就拿出BestOpportunityBot,其余的比赛很无聊。详细的按此播放。

  1. 刺客:10 HP,造成10点伤害,受到3点伤害
  2. 避免者v3:10点生命值,造成0点伤害,受到0点伤害
  3. 完成吞噬:10马力,造成0点伤害,受到0点伤害
  4. 最佳机会竞猜:0 HP,造成3点伤害,受到10点伤害

地图2

BestOpportunityBot完成了本场比赛的大部分工作,但刺客最终将他带走。详细的按此播放。

  1. 刺客:2点生命值,造成10点伤害,受到9点伤害
  2. 最佳机会竞猜:0 HP,造成32点伤害,受到10点伤害
  3. 避免者v3:0点生命值,造成0点伤害,受到12点伤害
  4. 完成吞噬:0 HP,造成0点伤害,受到11点伤害

地图3

BestOpportunityBot将所有人推入了这场比赛的陷阱。很酷。详细的按此播放。

  1. 最佳机会竞猜:10 HP,造成30点伤害,受到0点伤害
  2. 刺客:0 HP,造成0点伤害,受到0点伤害
  3. 完成吞噬:0 HP,0造成的伤害,0受到的伤害
  4. 避免者v3:0 HP,0造成的伤害,0受到的伤害

感谢您的回答!由于只有4个脚本机器人,我们将放弃三场免费比赛的比赛计划,以下每张地图上都有一场比赛。获胜记录最高的脚本机器人获胜。如果出现平局,我们将突然死亡,其中打破平局的脚本机器人将首先获胜。


您应该选择接受的任务是编写一个脚本机器人,该脚本机器人可以遍历ASCII映射并销毁其对手。每场战斗都将采取基于随机启动顺序回合制游戏的形式,其中每个Scriptbot都有机会花费自己的能量点(EP)采取行动。GameMaster脚本将输入并解释每个Scriptbot的输出。

环境

每个Scriptbot都包含在其自己的目录中,在该目录中可以从mapstats文件中读取文件并对其进行读/写data操作。该data文件可用于存储任何可能有用的持久性信息。

统计文件

stats文件包含有关您的对手的信息,其格式如下。每个玩家代表在单独的行上。第一列是玩家ID(@表示您)。第二列是该玩家的健康状况。

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

地图文件

map文件可能看起来像这样...

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

... 或这个...

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

... 或这个...

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

...或者看起来可能完全不同。无论哪种方式,使用的字符及其含义都将保持不变:

  • # 一堵墙,不可逾越,不可穿透。
  • 123...表示敌对玩家的数字。这些数字对应于stats文件中的播放器ID 。
  • !陷阱。移到这些位置的脚本机器人将立即死亡。
  • @ 您的脚本机器人的位置。
  • 您可以自由移动的开放空间。

游戏玩法

GameMaster脚本将为Scriptbot分配随机的启动顺序。然后,Scriptbot仍在运行时按此顺序调用。脚本机器人具有10个生命值(HP),每回合以10个能量点(EP)开头,它们可以用来移动或攻击。在每个回合开始时,Scriptbot会治愈1点生命值,或者如果已经达到10点生命值,则会被授予一张额外的EP(因此有时运行策略可能是可行的)。

当只有一个Scriptbot幸存下来或经过100转后,战斗就结束了。如果在战斗结束时还存在多个Scriptbot,则根据以下条件确定它们的位置:

  1. 最健康。
  2. 造成的伤害最多。
  3. 受到的伤害最多。

脚本机器人输入

GameMaster会将战斗地图打印到一个名为mapScriptbot可以读取的文件。地图可以采用任何形式,因此Scriptbot能够解释它很重要。您的Scriptbot将使用一个指示EP的参数来调用。例如...

:> example_scriptbot.py 3

脚本机器人将一直被调用,直到它花费所有EP或最多花费10 11次。每次调用前都会更新map和stats文件。

脚本机器人输出

脚本机器人应将其动作输出到sout。可能的操作列表如下:

  • MOVE <DIRECTION> <DISTANCE>

    每则收费1 EP DISTANCE。该MOVE命令将您的Scriptbot在地图上移动。如果有障碍物,例如墙壁或其他Scriptbot,GameMaster会尽可能移动您的Scriptbot。如果给定的DISTANCE值大于Scriptbot的剩余EP,则GameMaster将移动Scriptbot,直到其EP用完。DIRECTION可以是任何罗盘方向NES,或W

  • PUSH <DIRECTION> <DISTANCE>

    每则收费1 EP DISTANCE。该PUSH命令使一个脚本机器人可以移动另一个脚本机器人。发出命令的Scriptbot必须直接位于要推送的Scriptbot的旁边。如果没有阻止脚本机器人被推动的对象,则两个脚本机器人都将沿指示的方向移动。DIRECTION并且DISTANCEMOVE命令相同。

  • ATTACK <DIRECTION>

    花费一张EP。该ATTACK命令直接在发出Scriptbot的旁边并按指定的方向对任何Scriptbot造成1点伤害。DIRECTIONMOVE命令相同。

  • PASS

    轮到你了。

支持的语言

为了使这对我来说合理,我将接受以下语言:

  • 爪哇
  • Node.js
  • 蟒蛇
  • 的PHP

您只能使用通常随语言一起打包的库。请不要让我找到晦涩的库来使您的代码正常工作。

提交与评审

在下面发布您的Scriptbot源代码,并给它一个好名字!还请列出您使用的语言版本。所有Scriptbot都会被审查为伪造,因此请做好评论,不要混淆您的代码。

您可以提交多个条目,但是请使它们完全唯一,而不是同一条目的版本。例如,您可能想要编写Zerg Rush机器人和Gorilla Warfare机器人的代码。没关系。请勿发布Zerg Rush v1,Zerg Rush v2等。

在11月7日,我将收集所有答案,那些通过初步审查的答案将添加到比赛括号中。冠军将获得公认的答案。理想的支架如下所示。由于可能不完全是16个条目,因此某些括号最终可能只是三个甚至两个机器人。我会尽力使支架尽可能公平。任何必要的偏爱(例如,如果需要再见一周)将给予首先提交的机器人。

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

问答环节

我确定我错过了一些细节,请随时提问!

我们是否可以相信地图文件始终被#符号包围?如果没有,如果机器人试图从地图上走开,会发生什么?-BrainSteel

是的,地图将始终以#为边界,并且您的Scriptbot将在这些边界内启动。

如果在PUSH命令中指定的方向上没有机器人,该命令如何起作用?-BrainSteel

GameMaster将不执行任何操作,将花费零EP,并再次调用Scriptbot。

未使用的EP会累积吗?-feersum

否。每个Scriptbot都会以10 EP开始回合/回合。任何未花费的EP都会浪费掉。

我想我已经明白了,但只是要澄清一下:对于机器人A和B,事件的顺序是A @ 10EP-> MOVE MAP_UPDATE B @ 10EP-> PUSH MAP_UPDATE A @ 9EP->攻击MAP_UPDATE B @ 9EP->攻击...或A @ 10EP->移动A @ 9EP->攻击... MAP_UPDATE B @ 10EP->推B @ 9EP->攻击... MAP_UPDATE吗?换句话说,一个控制器-机器人查询循环中的所有动作都是原子的吗?如果是这样,为什么循环?为什么不返回包含所有要完成的操作的单个文件?否则,机器人将不得不写出自己的状态文件来跟踪多动作序列。map / stats文件仅在第一个操作之前有效。-COTO

您的第二个示例很接近,但不太正确。在回合中,将反复调用Scriptbot,直到用完它们的EP为止,或最多11次。每次调用前都会更新map和stats文件。如果机器人发出无效的输出,则循环很有用。GameMaster将处理无效的输出并再次介入该机器人,从而使该机器人有机会纠正其错误。

您将发布GameMaster脚本进行测试吗?-IchBinKeinBaum

GameMaster脚本将不会发布。我鼓励您创建一个地图和统计文件来测试您的机器人行为。

如果机器人A将机器人B推入陷阱,机器人A的“损坏处理”点是否等于机器人B当前的健康状况?-迈克·斯威尼(Mike Sweeney)

是的,那是个好主意。僵尸将获得等于其被推入陷阱的任何僵尸的生命值的伤害点。


我们是否可以相信map文件始终被#符号包围?如果没有,如果机器人试图离开地图,会发生什么?
BrainSteel 2014年

@BrainSteel是的,地图将始终#受其限制,并且您的Scriptbot将在这些范围内开始。
里普·里伯

3
如果您确定错过了什么,为什么不将其发布到沙箱中呢?
马丁·恩德2014年

2
@MartinBüttner我已经很彻底地考虑了这一点。我只是想保持友好,并明确表示欢迎提出问题。
Rip Leeb

1
如果机器人A将机器人B推入陷阱,机器人A的“损坏处理”点是否等于机器人B当前的健康状况?
逻辑骑士

Answers:


1

刺客(Java 1.7)

尝试杀死敌人,否则移动一个领域。在找到通往敌人的道路上,这非常好,但对隐藏其他机器人没有任何作用。

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

这是用什么版本的Java编写的?
里普·里伯

@Nate我使用1.7。
CommonGuy 2014年

3

避免者v3

一个简单的机器人。它害怕其他机器人和陷阱。它不会攻击。它忽略stats文件,根本不考虑。

这主要是测试以了解规则的工作方式,并且是其他竞争对手的愚蠢对手。

编辑:当没有更好的移动时,现在将通过。

Edit2:机械手可以是“ 1234”而不是“ 123”

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':你想寻找至少1234如果你是机器人3,对手名单将是124
瑞普里氏

1
@Nate谢谢。我改变了机器人。您可能需要在规则中阐明这一点。我可能不是唯一一个对此有误解的人。
逻辑骑士

3

最佳机会奖

最终比我预期的要长一点。。。我不确定我是否完全理解转弯规则,所以我们将看看它是如何做到的。

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))

您在哪个版本的python中编写了此代码?
里普·里布

@nate 3.4.1 on win32

感谢您提交这个@horns。看真的很有趣!
里普·里布

1

必须完成饮食

蟒蛇:

import sys
print 'PASS'

1
我大笑-为什么下地狱import sys
tomsmeding

1
@tomsmeding好,我不得不复制一些东西。而且我想,以防万一我吃完饭后需要阅读一些参数:)。
Timtech
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.