丘の上のイースターエッグハント


17

イースターエッグハント

バニーが卵を見つける前にボットが卵を見つけます。ボットは幸せ。

概要

これは、イースターとイースターエッグハンティングの伝統に敬意を表して、挑戦です!

ボットは、対角線を含むあらゆる方向に2つのスペースのビジョンを持ち、周囲に5x5の正方形を作成して見ることができます。卵を探しており、最も卵を見つけた人が勝ちます!

ボード

ボードはo、イースターエッグである#s、壁である*s、他のプレイヤーであるs、および空のスペースであるsで構成されます。

  • 辺の長さの正方形になります(number of entries) * 3
  • 壁に囲まれます。
  • 壁の内側には、のランダムに配置された直線の壁の品揃えが#あり、2〜10のランダムな長さを持ちます。(number of entries) * 3それらがあります。
  • 卵はランダムに配置されます。(number of entries) * 4それらが存在し、空白()の正方形でのみ生成されます。
  • ボード生成プロセスが適切に機能するには、少なくとも7つの全体が必要です。

以下に、テスト用のランダムボード生成するJSFiddleを示します。以下に例を示し(number of entries) = 7ます。

#####################
#        o         ##
#    #    o        ##
#    #o    ######  ##
######     #       ##
## o #     #       ##
##  o#   #o#    o o##
##   #o  # # o  #   #
##   # o # #    #   #
##  ##   # #  o #   #
##  #    #  o   # # #
##  # o  #   ## # # #
##  #           # # #
# o #          ## # #
# o oo         ##o  #
#o  ####### oo ##   #
#        #      #   #
#   o o o#          #
#   o ####   o     o#
#                   #
#####################

ボードが生成された後、各プレイヤーはランダムな正方形(空のスペース)に配置されます。

入力

入力は6行になります。最初の5行は視野です(ボードの境界外のスペースはで表されX、中央のスペースは常に*、あなたです)。6行目は空です(最初は)。

出力

3行を出力します。まず、移動したい方向:

1  2  3
8 YOU 4
7  6  5

(移動したくない場合、9はノーオペレーションです)、2番目、Aタック、Cオウンター、またはNオシングのいずれか(これについてはすぐに説明します)、3番目の行は最大1024までの文字列ですこれがボットの記憶になります。任意の目的に使用することも、空白のままにすることもできます。このメモリは、次回の実行時にプログラムへの入力の6行目になります。

出力のそれ以降の行はすべて無視され、1行しかない場合、2行目は空白と見なされます。

引越し

次のプロセスを使用して、移動した場所を判別します。

  • 移動したときに空のスペース()になった場合、プレイヤーはそのスペースに配置されます。
  • 壁(#)になった場合、移動は無視され、ターンを失います。
  • 卵(o)またはプレイヤー(*)になった場合、この情報は保存され、全員が移動した後に使用されます。

全員が動いた後、あいまいさが解決されます。

同じスペースに着地した2人のプレイヤーがいる場合、戦いが発生します!ここで、A/ C/ Nが再生されます。攻撃はA他のものを打つN(通常の攻撃)、N他のものはCウンターを打つ(何もC打ち消せない)、そしてウンターが攻撃を打つA(逆襲)。この戦いに勝ったプレイヤーは自分の広場に残り、負けたプレイヤーは元の広場に戻ります。同点の場合、両方のプレイヤーは元の場所に戻ります。

負けたプレイヤーまたは結ばれたプレイヤーが元の場所に戻り、そこに別のプレイヤーがいる場合、戦闘はなく、他のプレイヤーも元のスペースに戻ります。このスペースに別のプレーヤーがいる場合、そのプレーヤーは戻り、すべてのプレーヤーが異なるスペースに移動するまでこれが続きます。

1つのスペースに3人以上のプレイヤーがいる場合、それらはすべて元の位置に戻ります。

プレイヤーがまだ卵の上に立っている場合...

  • プレイヤーがを選択したA場合、卵は破壊されます。
  • プレーヤーがを選択した場合C、何も起こらず、プレーヤーは元のスペースに戻ります。
  • プレイヤーがを選択した場合、プレイヤーはN卵を拾います!プレイヤーのスコアが1つ増加し、卵が削除されます。

言語

Windows、OSX、およびLinuxで自由に利用できる任意の言語を使用して、各参加者間の公平性を確保できます。コードを自由に実行できないが、ある形式にコンパイルまたはパッケージ化できる場合は、この形式も回答に含めてください。理想的には、コードをより一般的な言語(CoffeeScript-> JavaScript)にコンパイルできる場合は、そうしてください。

得点

スコアは、10回の実行から収集した卵の平均数になります。すべての卵が収集されるか、(number of entries * 25)ターンが終了すると、実行は終了します。各マップのすべての卵に到達できることを手動で確認します(すべての卵に到達するまでマップを継続的に生成します)。

スコアボード

次のすべての条件が満たされると、スコアボードが追加されます。

  • 正のスコアまたはゼロのスコア(ダウンボットされていない)を持つ少なくとも7つの有効なエントリが送信されました
  • このチャレンジの作成から少なくとも48時間が経過しました(UTC 14:23)

ルールが不明確だった場合に明確化を追加する場合を除き、このコンテスト前の期間中、ルールは変更されません。スコアボードが作成されると、テストプログラムもここに投稿されるので、エントリをテストできます。このためのテストコードはまだ進行中ですが、再生可能で動作します。これがGitHubリポジトリです。


4
7つのエントリが投稿される前にテストプログラムを入手できますか?「ダム」テストボットに対するものであっても、投稿するにテストするのが好きです。これは、他の数人が投稿するまで投稿しないことで大きな利点が得られるようです。
ジオビット

1
戻る選手について。だから私は潜在的に不運で対戦相手に勝つ可能性がありますが、彼は別のプレイヤーに戻って、私たちの戦いの場所にループバックするカスケードを開始します。(それが明確でない場合、githubの要点を例とともに投稿します)
マーティンエンダー14

1
ここでは、サンプルの制御プログラムが非常に役立ちます。
starbeamrainbowlabs 14

3
記憶線のアイデアが好き
Einacio 14

2
また、ルールの意味を知っていますか?プレイヤー(A)がを選択した場合、9意味のある攻撃を受けることはありません。別のプレイヤー(B)がそのプレイヤーのスクエアに足を踏み入れて勝った場合、Aは元のスクエア(同じ)に戻ります。しかし、AとBの両方が存在するため、衝突が発生したため、Bは自分の広場に戻らなければなりません。したがって、結果は実際の戦闘とは無関係であり、Bは常に最初の正方形に戻り、Aは常に配置されたままです。それは私が他の皆のためのパスをブロックすることによって別の提出を助けることができる両方を書くことを可能にします。
マーティンエンダー14

回答:


3

Cart'o'Gophers

ここに別の提出物があります-そして、これは実際に競争力があるように意図されています。繰り返しますが、それはRubyです。で実行しruby cartogophers.rbます。これには予想以上に時間がかかりました...

require 'zlib'
require 'base64'

def encode map, coords
    zipped = Zlib::Deflate.deflate map.join
    encoded = Base64.encode64(zipped).gsub("\n",'')
    "#{coords[:x]}|#{coords[:y]}|#{map.length}|#{encoded}"
end

def decode memory
    memory =~ /^(\d+)[|](\d+)[|](\d+)[|](.*)$/
    coords = {x: $1.to_i, y: $2.to_i}
    n_rows = $3.to_i
    encoded = $4
    decoded = Base64.decode64 encoded
    unzipped = Zlib::Inflate.inflate decoded
    n_cols = unzipped.length / n_rows;
    return unzipped.scan(/.{#{n_cols}}/), coords
end

def update map, fov, coords
    if coords[:x] < 2
        map.map! { |row| '?' << row }
        coords[:x] += 1
    elsif coords[:x] >= map[0].length - 2
        map.map! { |row| row << '?' }
    end

    if coords[:y] < 2
        map.unshift '?' * map[0].length
        coords[:y] += 1
    elsif coords[:y] >= map.length - 2
        map.push '?' * map[0].length
    end

    fov.each_index do |i|
        map[coords[:y]-2+i][coords[:x]-2, 5] = fov[i]
    end

    return map, coords
end

def clean_up map
    map.each do |row|
        row.gsub!('*', ' ')
    end
end

DIRECTIONS = [
    [],
    [-1,-1],
    [ 0,-1],
    [ 1,-1],
    [ 1, 0],
    [ 1, 1],
    [ 0, 1],
    [-1, 1],
    [-1, 0],
    [ 0, 0]
]

def move_to dir, coords
    {
        x: coords[:x] + DIRECTIONS[dir][0],
        y: coords[:y] + DIRECTIONS[dir][1]
    }
end

def get map, coords
    if coords[:x] < 0 || coords[:x] >= map[0].length ||
       coords[:y] < 0 || coords[:y] >= map.length
        return '?'
    end
    map[coords[:y]][coords[:x]]
end

def set map, coords, value
    unless coords[:x] < 0 || coords[:x] >= map[0].length ||
       coords[:y] < 0 || coords[:y] >= map.length
        map[coords[:y]][coords[:x]] = value
    end
    map[coords[:y]][coords[:x]]
end

def scan_surroundings map, coords
    not_checked = [coords]
    checked = []
    cost = { coords => 0 }
    direction = { coords => 9 }
    edges = {}

    while not_checked.length > 0
        current = not_checked.pop

        checked.push current
        (-1..1).each do |i|
            (-1..1).each do |j|
                c = { x: current[:x]+i, y: current[:y]+j }
                unless not_checked.include?(c) || checked.include?(c)
                    if get(map, c) == '#'
                        checked.push c
                    elsif get(map, c) == '?'
                        checked.push c
                        edges[current] = { cost: cost[current], move: direction[current] }
                    else
                        not_checked.unshift c

                        cost[c] = cost[current] + 1
                        if direction[current] == 9 # assign initial direction
                            direction[c] = DIRECTIONS.find_index([i,j])
                        else
                            direction[c] = direction[current]
                        end

                        if get(map, c) == 'o'
                            return direction[c], if cost[c] == 1 then 'N' else 'A' end
                        end

                        edges[c] = { cost: cost[c], move: direction[c] } if c[:x] == 0 || c[:x] == map[0].length - 1 ||
                                                                            c[:y] == 0 || c[:y] == map.length - 1
                    end
                end
            end
        end
    end

    # If no egg has been found return the move to the closest edge
    nearest_edge = edges.keys.sort_by { |c| edges[c][:cost] }.first
    if edges.length > 0
        return edges[nearest_edge][:move], 'A'
    else
        # The entire map has been scanned and no more eggs are left.
        # Wait for the game to end.
        return 9, 'N'
    end
end

input = $<.read.split "\n"
fov = input[0..4]
memory = input[5]

if memory
    map, coords = decode memory
    map, coords = update(map, fov, coords)
else
    map = fov
    coords = {x: 2, y: 2}
end
clean_up map

move, attack = scan_surroundings(map, coords)

memory = encode map, move_to(move, coords)

puts "#{move}
#{attack}
#{memory}"

このボットは以前に見たものを記憶し、各ターンでより大きなマップを構築しようとします。次に、最も近い卵と幅方向の幅優先検索を使用します。現在のマップに到達できる卵がない場合、ボットはマップの最も近いオープンエッジに向かって移動します(これにより、マップを移動可能な方向にすばやく拡大できます)。

このボットには、他のボットの概念も戦闘戦略もありません。私の移動が成功したかどうかを判断する信頼できる方法を見つけていないので、これはいくつかの問題を引き起こす可能性があります。私は常に移動が成功したと常に仮定しています-もしそうでなければ、新しいパッチが間違った場所でマップにロードされます。

ボットはメモリを使用して、マップとマップ上の新しい位置を保存します(移動が成功すると仮定します)。マップは、改行なしで格納され、圧縮され、base64でエンコードされます(マップの行数とともに、改行を再挿入できます)。この圧縮により、サイズが圧縮されていないマップの約3分の1になります。したがって、1000バイト以上のシェードを使用して、約3000セルのマップを保存できます。そのような多くの提出物に近いところがない限り、私はそのケースの修正を見つけ出すのに悩むことができるとは思わない。

5 dumbbotsと1 に対していくつかのテストを実行した後naivebot(他の提出)、それは非常に悪いパフォーマンス(1個または2個の卵など)か、他のパフォーマンスよりもかなりのマージン(7個から9個の卵)でした。より良い戦闘戦略と、実際に動いたかどうかをどのように判断できるかを考えるかもしれません。どちらもスコアをいくらか改善できるかもしれません。

ああ、ボットの名前が気になる場合は、The Order of The Stick(このコミックの最後のパネル)を読んでください。

編集:発見されたマップのエッジを検出するといくつかのバグがありました。私はそれらを修正したので、このボットは常に205 dumbbot秒と1 に対して約のスコアを取得しnaivebotます。それはもっと似ています!$stderr.puts map私のボットに今すぐ追加すると、彼がどのように体系的にマップを明らかにし、その間にすべての卵を収集するかを本当に見ることができます。また、ボットの元のセル(地図の一部がめちゃくちゃになっている)に戻る可能性を減らすために、卵に足を踏み入れないときANいつでも選択することにしました。

naivebot特に、6 秒間はあまりパフォーマンスがよくありません。特に、両方が繰り返し卵をつかんで選択したいときに、別のボットと「デッドロック」状態になる可能性が非常に高いためNです。 )


3

ジャババニー

このバニーはまだ成長を終えていません(私はまだ変更を加える予定です)が、それは今の出発点です。彼は最も近い卵を探してそれに向かって行きます。壁の検出または境界外の検出はありません(まだ)。彼は着地した卵を拾いに行きますが、そうでなければ彼は彼を押しのけて他のものを攻撃しようとします。近くに卵がない場合、彼は最も近いバニーを追い始めます。彼らは彼が知らないことを知っているかもしれません。それ以外の場合、彼は歩く方向をランダムに選択します。そして、彼はかなり忘れっぽいです(メモリ変数を使用しません)。

今後の計画:

  • 壁/境界外に基づいて決定を下す
  • ランダムではなく、目的のあるパスを選ぶ
  • メモリを使用して、前に行った方向を判断します

更新1 バニーは卵が見えない場合、他のバニーをフォローします。また、「最も近い卵を見つける」コードを独自のメソッドにリファクタリングしました。

import java.util.*;

public class EasterEggHunt {

    // board chars
    public static final char EGG = 'o';
    public static final char WALL = '#';
    public static final char BUNNY = '*';
    public static final char SPACE = ' ';
    public static final char OUT_OF_BOUNDS = 'X';

    // player moves
    public static final char ATTACK = 'A';
    public static final char COUNTER = 'C';
    public static final char NOTHING = 'N';

    // directions
    public static final int UPPER_LEFT = 1;
    public static final int UP = 2;
    public static final int UPPER_RIGHT = 3;
    public static final int RIGHT = 4;
    public static final int LOWER_RIGHT = 5;
    public static final int DOWN = 6;
    public static final int LOWER_LEFT = 7;
    public static final int LEFT = 8;
    public static final int STAY = 9;


    // the size of the immediate area
    // (I'll be at the center)
    public static final int VISION_RANGE = 5;

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

        char[][] immediateArea = new char[VISION_RANGE][VISION_RANGE];

        for (int i = 0; i < VISION_RANGE; ++i) {
            String line = input.nextLine();
            for (int j = 0; j < VISION_RANGE; ++j) {
                immediateArea[i][j] = line.charAt(j);
            }
        }

        String memory = input.nextLine();

        int moveDirection = decideMoveDirection(immediateArea, memory);
        System.out.println(moveDirection);

        char action = decideAction(immediateArea, memory, moveDirection);
        System.out.println(action);

        // change the memory?
        System.out.println(memory);

    }

    private static int decideMoveDirection(char[][] immediateArea, String memory) {

        // if there's a nearby egg, go towards it
        int direction = nearestBoardObject(immediateArea, EGG);

        // if we didn't find an egg, look for a bunny
        // (maybe he knows where to find eggs)
        if (direction == STAY)
            direction = nearestBoardObject(immediateArea, BUNNY);

        // otherwise, pick a random direction and go
        // we want to also have the chance to stop and catch our breath
        if (direction == STAY)
            direction = new Random().nextInt(STAY + 1);

        return direction;
    }

    private static int nearestBoardObject(char[][] immediateArea, char boardObject) {

        // start at the center and go outward (pick a closer target over a farther one)
        int spacesAway = 1;
        int meX = immediateArea.length / 2;
        int meY = immediateArea[meX].length / 2;

        while (spacesAway <= immediateArea.length / 2) {

            // I like to look right, and go clockwise
            if (immediateArea[meX][meY + spacesAway] == boardObject)
                return RIGHT;
            if (immediateArea[meX + spacesAway][meY + spacesAway] == boardObject)
                return LOWER_RIGHT;
            if (immediateArea[meX + spacesAway][meY] == boardObject)
                return DOWN;
            if (immediateArea[meX + spacesAway][meY - spacesAway] == boardObject)
                return LOWER_LEFT;
            if (immediateArea[meX][meY - spacesAway] == boardObject)
                return LEFT;
            if (immediateArea[meX - spacesAway][meY - spacesAway] == boardObject)
                return UPPER_LEFT;
            if (immediateArea[meX - spacesAway][meY] == boardObject)
                return UP;
            if (immediateArea[meX - spacesAway][meY + spacesAway] == boardObject)
                return UPPER_RIGHT;

            ++spacesAway;
        }

        // if the target object isn't in the immediate area, stay put
        return STAY;

    }

    private static char decideAction(char[][] immediateArea, String memory, int moveDirection) {

        char destinationObject = getDestinationObject(immediateArea, moveDirection);

        switch (destinationObject) {

            case EGG:
                // don't break the egg
                return NOTHING;
            default:
                // get really aggressive on everything else
                // other players, walls, doesn't matter
                return ATTACK;

        }

    }

    private static char getDestinationObject(char[][] immediateArea, int moveDirection) {

        // start at my spot (middle of the board) and figure out which direction I'm going
        int targetX = immediateArea.length / 2;
        int targetY = immediateArea[targetX].length / 2;

        switch (moveDirection) {

            case RIGHT:
                ++targetY;
                break;
            case LOWER_RIGHT:
                ++targetX;
                ++targetY;
                break;
            case DOWN:
                ++targetX;
                break;
            case LOWER_LEFT:
                ++targetX;
                --targetY;
                break;
            case LEFT:
                --targetY;
                break;
            case UPPER_LEFT:
                --targetX;
                --targetY;
                break;
            case UP:
                --targetX;
                break;
            case UPPER_RIGHT:
                --targetX;
                ++targetY;
                break;
            // otherwise we aren't moving

        }

        return immediateArea[targetX][targetY];

    }

}

また、Java列挙型はクラスでほとんどいっぱいであり、.NET列挙型の方がはるかに優れていることも学びました。
ブライアンJ 14年

0

NaiveBot(Ruby)

これは非常に単純なボットで、卵を動かします(これらの7つのサブミッションをすばやくヒットさせたいのですか?)。私のRubyはあまり慣用的ではないので、このコードは適切なルビイストを苦痛に陥らせるかもしれません。ご自身の責任で読んでください。

input = $<.read
$data = input.split("\n")

def is_egg x, y
    $data[y+2][x+2] == 'o'
end

def is_wall x, y
    $data[y+2][x+2] == '#'
end

def is_empty x, y
    $data[y+2][x+2] == ' '
end

def is_player x, y
    $data[y+2][x+2] == '*'
end

if (is_egg(-2,-2) || is_egg(-2,-1) || is_egg(-1,-2)) && !is_wall(-1,-1) || is_egg(-1,-1)
    dir = 1
elsif is_egg(0,-2) && !is_wall(0,-1) || is_egg(0,-1)
    dir = 2
elsif (is_egg(2,-2) || is_egg(2,-1) || is_egg(1,-2)) && !is_wall(1,-1) || is_egg(1,-1)
    dir = 3
elsif is_egg(2,0) && !is_wall(1,0) || is_egg(1,0)
    dir = 4
elsif (is_egg(2,2) || is_egg(2,1) || is_egg(1,2)) && !is_wall(1,1) || is_egg(1,1)
    dir = 5
elsif is_egg(0,2) && !is_wall(0,1) || is_egg(0,1)
    dir = 6
elsif (is_egg(-2,2) || is_egg(-2,1) || is_egg(-1,2)) && !is_wall(-1,1) || is_egg(-1,1)
    dir = 7
elsif is_egg(-2,0) && !is_wall(-1,0) || is_egg(-1,0)
    dir = 8
else
    dir = rand(8) + 1
end

attack = 'N'
puts "#{dir}
#{attack}
"

で実行しruby naivebot.rbます。

卵が見えて壁に遮られないいくつかのケースを単にハードコーディングしています。それは最も近い卵にさえ行かないが、理にかなっている最初の動きを選ぶ。そのような卵が見つからない場合、ボットはランダムに移動します。それは他のすべてのプレイヤーを無視し、攻撃やカウンターはしません。


0

WallFolower

(意図的なしゃれ)Python 3の場合

import sys
import random

#functions I will use
dist       = lambda p1,p2: max(abs(p2[1] - p1[1]), abs(p2[0] - p1[0]))
distTo     = lambda p    : dist((2,2), p)
cmp        = lambda x,y  : (x > y) - (x < y)
sgn        = lambda x    : (-1,0,1)[(x>0)+(x>=0)]
move       = lambda p    : (sgn(p[0] - 2), sgn(p[1] - 2))
unmove     = lambda p    : (p[0] * 2 + 2, p[1] * 2 + 2)
outputmove = lambda p    : (1,2,3,8,9,4,7,6,5)[(sgn(p[0] - 2) + 1) + 3*(sgn(p[1]-2) + 1)]
def noeggfinish(move):
    print(outputmove(unmove(move)))
    print('ACN'[random.randint(0, 2)])
    print("1"+move)
    sys.exit(0)

#beginning of main body
view    = [list(l) for l in map(input, ('',)*5)] #5 line input, all at once.
memory  = input() #currently used only as last direction moved in a tuple
eggs    = []
enemies = []
for y in range(5):
    for x in range(5):
        if   view[y][x] == 'o': eggs    += [(x,y)]
        elif view[y][x] == '*': enemies += [(x,y)]

eggs.sort(key = lambda p:distTo(p)) #sort by how close to me they are.

tiedeggs = []
end = 0
for egg in eggs[:]:
    if end:break
    for enemy in enemies:
        exec({
            -1: 'eggs.remove(egg)',
             0: 'tiedeggs += egg',
             1: 'end=1'
        }[cmp(dist(enemy, egg), distTo(egg))])
        if end:break
if eggs:
    print(outputmove(eggs[0]))
    print('N')              #no attack here
    print("0"+move(eggs[0]))
    sys.exit(0)
elif tiedeggs:
    print(outputmove(tiedeggs[0]))
    print('N')              #no attack here
    print("0"+move(tiedeggs[0]))
    sys.exit(0) 
#now there are no eggs worth going for
#do a LH wall follow

lastmove = eval(memory[1:]) #used to resolve ambiguity
if lastmove[0] and lastmove[1]:
    lastmove[random.randint(0,1)] = 0 #disregard diagonal moves
if eval(memory[0]):
    exec("check=view[%n][%n]"%{(0,-1):(0,0),(1,0):(4,0),(0,1):(4,4),(-1,0):(0,4)}[lastmove])
    if check == '#':
        noeggfinish(lastmove)
    else:pass
#currently unimplemented
#move randomly
noeggfinish(tuple([(x,y) for x in [-1,0,1] for y in [-1,0,1] if (x,y) != (0,0)))

卵が見える場合は、別のロボットよりもその卵に近い場合にのみ、卵に移動します。距離にタイがある場合、それはとにかくそれのために行きます。それ以外の場合、LHウォールが続きます(現在は適切に実装されていません)。

引き続きウォールの作業が必要ですが、とにかくここに投稿します。


1
私はtester.pyであなたのボットを実行すると、私はコンソールにこのエラーが表示されます。pastebin.com/cT5xGdSWを
starbeamrainbowlabs

こっちも一緒。調べていただけますか?これに対して私の新しいボットをテストしたいと思います。
マーティンエンダー14

@ m.buettnerに変更sys.exit(0)するとexit(0)どうなりますか?また、私はこれに取り組む必要があります(今のところ、それ自体が ``であると仮定しています)が、実際には時間がありません。時間があれば、これを修正します。
ジャスティン14

残念ながら、@ Quincunxは何も変更しませんでした。
マーティンエンダー14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.