Scriptbot Warz!


14

Scriptbot Warz!


結果は内にあり、暗殺者が私たちのチャンピオンであり、3試合中2試合に勝ちました!Scriptbotを送信したすべての人に感謝します!優れたパスを表示し、すべてのアクションオプションを最大限に活用したBestOpportunityBotのホーンに感謝します。

地図1

暗殺者は早い段階でBestOpportunityBotを使用しましたが、残りの試合はかなり退屈でした。詳細なプレイバイプレイはこちら。

  1. 暗殺者:HP 10、ダメージ10、ダメージ3
  2. The Avoider v3:10 HP、0ダメージ、0ダメージ
  3. 食べ終わった:10 HP、0ダメージ、0ダメージ
  4. BestOpportunityBot:HP 0、ダメージ3、ダメージ10

地図2

BestOpportunityBotはこの試合でほとんどの作業を行いましたが、アサシンは最終的に彼を連れ去ることができました。詳細なプレイバイプレイはこちら。

  1. 暗殺者:2 HP、10ダメージ、9ダメージ
  2. BestOpportunityBot:HP 0、ダメージ32、ダメージ10
  3. The Preventer v3:0 HP、0ダメージ、12ダメージ
  4. 食べ終わる:0 HP、0ダメージ、11ダメージ

地図3

BestOpportunityBotは、この試合で全員をtrapに追い込みました。とてもかっこいい。詳細なプレイバイプレイはこちら。

  1. BestOpportunityBot:HP 10、ダメージ30、ダメージ0
  2. 暗殺者:0 HP、0ダメージ、0ダメージ
  3. 食べ終わる:0 HP、0ダメージ、0ダメージ
  4. The Avoider v3:HP 0、ダメージ0、ダメージ0

ご回答ありがとうございます!Scriptbotが4つしかないため、以下の各マップに1つずつ、3つの完全無料試合のトーナメントプランを放棄します。最高の勝利記録を持つスクリプトボットが勝利します。ネクタイが発生した場合、ネクタイを破ったスクリプトボットが最初に勝つという突然の死になります。


あなたがそれを受け入れることを選択した場合、あなたの仕事は、ASCIIマップを横断してその敵を破壊できるScriptbotをコーディングすることです。各バトルは、ランダムなスタート順のターンベースのゲームの形をとり、各スクリプトボットはアクションを起こすためにエネルギーポイント(EP)を費やす機会があります。GameMasterスクリプトは、各Scriptbotに入力を送り、出力を解釈します。

環境

各Scriptbotは独自のディレクトリ内に含まれており、そこでファイルとファイルから読み取りmapstatsファイルへの読み取り/書き込みがdataできます。このdataファイルは、役に立つと思われる永続的な情報を保存するために使用できます。

統計ファイル

statsファイルには、あなたの対戦相手についての情報が含まれており、次のようにフォーマットされます。各プレイヤーは別々の行に表示されます。最初の列はプレーヤーIDです(@あなたを意味します)。2番目の列は、そのプレーヤーの健康状態です。

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

マップファイル

mapファイルには、次のようになります...

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

...またはこれ...

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

...またはこれ...

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

...またはまったく異なるように見えるかもしれません。いずれにしても、使用される文字とその意味は同じままです。

  • # 通行不能で侵入できない壁。
  • 123...敵プレイヤーを表す数値。これらの番号は、statsファイル内のプレーヤーIDに対応しています。
  • !トラップ。これらの場所に移動したScriptbotはすぐに死にます。
  • @ Scriptbotの場所。
  • 自由に移動できる広場

ゲームプレイ

GameMasterスクリプトは、Scriptbotにランダムな開始順序を割り当てます。スクリプトボットは、生きている間にこの順序で呼び出されます。Scriptbotには10のヘルスポイント(HP)があり、各ターンに10のエネルギーポイント(EP)から始まり、移動または攻撃に使用できます。各ターンの開始時に、Scriptbotは1 HPを回復するか、すでに10 HPである場合は1 EPを追加で付与されます(したがって、実行することが実行可能な戦略である場合があります)。

1つのScriptbotのみが生き残るか、100ターンが経過すると、戦闘は終了します。戦闘の終了時に複数のScriptbotが生きている場合、その場所は次の基準に基づいて決定されます。

  1. ほとんどの健康。
  2. ほとんどのダメージが与えられました。
  3. ほとんどのダメージ。

Scriptbot入力

GameMasterは、バトルマップをmapScriptbotが読み取りアクセスできる名前のファイルに印刷します。マップは任意の形式をとる可能性があるため、Scriptbotがマップを解釈できることが重要です。Scriptbotは、EPを示す1つのパラメーターで呼び出されます。例えば...

:> example_scriptbot.py 3

Scriptbotは、EPをすべて使用するか、最大10 11回使用するまで呼び出されます。マップおよび統計ファイルは、各呼び出しの前に更新されます。

Scriptbotの出力

Scriptbotは、アクションをスタウトに出力する必要があります。可能なアクションのリストは次のとおりです。

  • MOVE <DIRECTION> <DISTANCE>

    あたり1枚のEPコストDISTANCE。このMOVEコマンドは、Scriptbotをマップ上で移動します。壁や他のScriptbotなどの障害物がある場合、GameMasterはScriptbotを可能な限り移動します。場合DISTANCEScriptbotの残りのEPよりも大きいが与えられ、そのEPがなくなるまで、ゲームマスターはScriptbotを移動します。DIRECTION任意のコンパス方向であってもよいNES、またはW

  • PUSH <DIRECTION> <DISTANCE>

    あたり1枚のEPコストDISTANCE。このPUSHコマンドにより、Scriptbotは別のScriptbotを移動できます。コマンドを発行するScriptbotは、プッシュされるScriptbotのすぐ隣になければなりません。プッシュされるScriptbotをブロックするオブジェクトが存在しない場合、両方のScriptbotは指定された方向に移動します。DIRECTIONまたDISTANCEMOVEコマンドの場合と同じです。

  • ATTACK <DIRECTION>

    EPが1つかかります。このATTACKコマンドは、発行するScriptbotのすぐ隣で、指定された方向にあるScriptbotに1ダメージを与えます。コマンドの場合DIRECTIONと同じMOVEです。

  • PASS

    ターンを終了します。

サポートされている言語

これを合理的に保つために、次の言語を受け入れます。

  • Java
  • Node.js
  • Python
  • PHP

すぐに使用できる言語で一般的にパッケージ化されるライブラリに制限されます。コードを機能させるためにあいまいなライブラリを見つけさせないでください。

提出と審査

以下にScriptbotのソースコードを投稿して、クールな名前を付けてください!使用した言語のバージョンもリストしてください。すべてのScriptbotsは、おかしな点がないかどうか確認されますので、コメントして、コードをわかりにくくしないでください。

複数のエントリを送信できますが、同じエントリのバージョンではなく、完全に一意のエントリにしてください。たとえば、Zerg RushボットとGorilla Warfareボットをコーディングできます。それはいいです。Zerg Rush v1、Zerg Rush v2などを投稿しないでください。

11月7日にすべての回答を収集し、最初のレビューに合格した回答をトーナメントブラケットに追加します。チャンピオンは受け入れられた答えを受け取ります。理想的なブラケットを以下に示します。16個のエントリが存在する可能性は低いため、一部のブラケットは3つまたは2つのボットにさえなる可能性があります。ブラケットをできるだけ公平にするようにします。最初に送信されたボットには、(たとえば、1週間が必要な場合に)必要な支持が与えられます。

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

Q&A

私はいくつかの詳細を見逃していると確信しているので、質問してください!

マップファイルは常に#記号で囲まれていると信頼できますか?そうでない場合、ボットがマップから歩き出そうとした場合はどうなりますか?-BrainSteel

はい。マップは常に#で区切られ、Scriptbotはこれらの範囲内で開始されます。

PUSHコマンドで指定された方向にボットが存在しない場合、コマンドはどのように機能しますか?-BrainSteel

GameMasterは何もせず、ゼロEPが使用され、Scriptbotが再度呼び出されます。

未使用のEPは蓄積されますか?-フェールサム

いいえ。各Scriptbotは10 EPでラウンド/ターンを開始します。費やしていないEPはすべて無駄になります。

私はそれを持っていると思いますが、ただ明確にするために:ボットAとBでは、イベントの順序はA @ 10EP-> MOVE MAP_UPDATE B @ 10EP-> PUSH MAP_UPDATE A @ 9EP-> ATTACK MAP_UPDATE B @ 9EP->攻撃...、またはA @ 10EP-> MOVE A @ 9EP->攻撃... MAP_UPDATE B @ 10EP-> PUSH B @ 9EP->攻撃... MAP_UPDATE?つまり、1つのコントローラーボットクエリループ内のすべてのアクションはアトミックですか?もしそうなら、なぜループですか?完了すべきすべてのアクションを含む単一のファイルを返してみませんか?それ以外の場合、ボットは独自の状態ファイルを書き出して、マルチアクションシーケンスを追跡する必要があります。マップ/統計ファイルは、最初のアクションの前にのみ有効です。-COTO

2番目の例は近いですが、完全に正しくありません。ターン中、Scriptbotは、EPが使用されるまで繰り返し、または最大11回呼び出されます。マップおよび統計ファイルは、各呼び出しの前に更新されます。このループは、ボットが無効な出力を行う場合に役立ちます。GameMasterは無効な出力を処理し、再びボットを関与させ、ボットに間違いを修正する機会を与えます。

テスト用にGameMasterスクリプトをリリースしますか?-IchBinKeinBaum

GameMasterスクリプトはリリースされません。ボットの動作をテストするために、マップと統計ファイルを作成することをお勧めします。

robotAがrobotBをトラップにプッシュした場合、robotAはrobotBの現在のヘルスに等しい「ダメージを与えられた」ポイントと見なされますか?-マイク・スウィーニー

はい、それは良い考えです。ボットには、トラップにプッシュしたボットのヘルスに等しいダメージポイントが付与されます。


mapファイルは常に#シンボルで囲まれていると信頼できますか?そうでない場合、ボットがマップから歩き出そうとした場合はどうなりますか?
BrainSteel 14年

@BrainSteelはい、マップは常に境界で囲まれ#、Scriptbotはこれらの境界内で開始されます。
リップリープ14年

3
あなたが何かを見逃していると確信しているなら、サンドボックスに投稿してみませんか?
マーティンエンダー14年

2
@MartinBüttner私はこれを非常に徹底的に考えてきました。私はただ友好的になり、質問を歓迎することを明確にしようとしていました。
リップリープ14年

1
robotAがrobotBをトラップにプッシュした場合、robotAはrobotBの現在のヘルスに等しい「ダメージを与えられた」ポイントと見なされますか?
ロジックナイト14年

回答:


1

暗殺者(Java 1.7)

可能な限り敵を殺そうとします。それ以外の場合は1フィールド移動します。敵への道を見つけることはかなり良いですが、他のボットから隠れることは何もしません。

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で書かれていますか?
リップリープ14年

@ネイト1.7を使用しました。
CommonGuy 14年

3

回避v3

シンプルなマインドボット。他のロボットやトラップが怖いです。攻撃しません。statsファイルを無視し、まったく先を見ていません。

これは主に、ルールがどのように機能するかを確認するためのテストであり、他の競合他社の愚かな相手としてです。

編集:MOVEの方が良い場合に合格します。

Edit2:ロボットは「123」ではなく「1234」にすることができます

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
@ネイトありがとう。ボットを変更しました。これをルールで明確にしたい場合があります。これを誤解したのは私だけではないかもしれません。
ロジックナイト14年

3

BestOpportunityBot

これは最終的に私が意図したよりも少し長くなりました...そして、ターンのルールを完全に理解しているかどうかはわかりませんので、これがどうなるか見ていきます。

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で記述しましたか?
リップリープ14年

win32の@Nate 3.4.1

これを@hornsに送信していただきありがとうございます。見るのは本当に楽しかったです!
リップリープ14年

1

食べ終わった

Python:

import sys
print 'PASS'

1
私はそれを笑った-そしてなぜ地獄import sys
14年

1
@tomsmedingまあ私はいくつかのものをコピーする必要がありました。もちろん、食べ終わったら引数を読む必要があると思いました。
ティムテック14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.