小山上的复活节彩蛋狩猎


17

寻找复活节彩蛋活动

机器人在兔子找到鸡蛋之前先找到鸡蛋。机器人高兴。

总览

这是纪念复活节和追捕复活节彩蛋的传统之挑战!

您的机器人在每个方向上都有两个空间的视野,包括对角线,在您周围创建了一个5x5的正方形,您可以看到。它正在寻找鸡蛋,发现鸡蛋最多的人将获胜!

董事会

棋盘将由os(代表复活节彩蛋),#s(代表墙壁),*s(代表其他玩家)和s(代表空白)组成。

  • 这将是一个具有边长的正方形(number of entries) * 3
  • 它将被墙壁包围。
  • 墙的内部将是各种随机放置的,的直线墙#,其随机长度在2到10之间(含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#
#                   #
#####################

生成棋盘后,将每个玩家放置在随机的正方形(空白空间)上。

输入值

您将需要六行输入。前五行是您的视野(板边界之外的X空间将由表示,中间空间将始终是*,您),而第六行将为空(最初)。

输出量

您将输出三行。首先,您要移动的方向:

1  2  3
8 YOU 4
7  6  5

(9是一个空操作,如果你不想动),第二,一个Attack,Counter,或Nothing(这将在不久的深度来解释),以及THRID线将是最长的任何字符串到1024这将是您的机器人的记忆。您可以将其用于任何用途,也可以保留为空白。然后,该内存将成为下一次运行时程序输入的第六行。

输出的所有其他行都将被忽略,如果只有一行,则第二行被认为是空白。

移动

以下过程用于确定您的移动位置:

  • 如果在移动时最终进入空白处(),则将播放器放置在该空白处。
  • 如果您碰壁(#),您的举动将被忽略,并且您掉头了。
  • 如果您遇到的是鸡蛋(o)或玩家(*),该信息将被存储并在每个人移动后使用。

每个人都搬走后,歧义得到解决。

如果有两个玩家降落在同一空间上,则会发生战斗!这就是A/ C/ N也发挥了作用。Attack次Nothing(普通攻击),Nothing次Counter(你不能计数器无),以及Counter次Attack(反击)。赢得这场战斗的玩家将留在自己的方块上,失败的玩家将回到他们开始时的原始方块。如果出现平局,两个玩家都回到原处。

如果失去或受束缚的玩家返回原来的位置,并且那里有另一位玩家,则不会打架,另一位玩家也将恢复到原来的位置。如果空间中有其他玩家,则玩家将返回,然后继续进行,直到所有玩家都位于不同的空间中。

如果在一个空间中有三个或三个以上的玩家,他们都将恢复到原始位置。

如果还有玩家站在蛋上...

  • 如果玩家选择A,鸡蛋将被销毁。
  • 如果玩家选择C,则什么也不会发生,并且玩家将恢复到其原始空间。
  • 如果玩家选择N,则玩家拿起鸡蛋!玩家的分数增加一,鸡蛋被移除。

语言能力

您可以使用Windows,OSX和Linux上免费提供的任何语言,以确保每个参赛者之间的公平。如果该代码不是可自由运行的,但可以编译或打包成一种格式,请在答案中也包含此格式。理想情况下,如果您可以将代码编译成更通用的语言(例如CoffeeScript-> JavaScript),请这样做。

计分

您的得分将是您在十次运行中平均收集到的卵数。当收集到所有卵或(number of entries * 25)轮流过去时,运行结束。我将手动确保可以到达每个地图的所有鸡蛋(通过不断生成地图直到所有鸡蛋都可以到达)。

计分板

满足以下所有条件时,将添加一个记分板:

  • 至少提交了七个得分为正或为零(未投票)的有效条目
  • 自发起挑战以来,至少已经过去了48小时(UTC 14:23)

在比赛前的这段时间内,规则将不会更改,除非在规则不清楚的地方增加说明。一旦记分牌成立,测试程序也将发布在此处,以便您可以测试您的条目。测试代码仍在进行中,但是它是可播放的并且有效。这是GitHub仓库。


4
我们可以在发布7个条目之前获得测试程序吗?我喜欢发布之前进行测试,即使它是针对“愚蠢”的测试机器人。看来这给出了一个显著的优势没有发布,直到几个人有。
Geobits

1
关于玩家回去。因此,我可能会很不幸,可能会击败对手,但他又退回到另一位玩家并开始级联,并绕回我们的战斗地点,这样从那里开始的玩家也会向我退一步?(如果不清楚,我将发布一个示例的github要点)
Martin Ender 2014年

1
这里的样本控制程序将非常有用。
starbeamrainbowlabs 2014年

3
我喜欢记忆线的想法
Einacio

2
您还知道规则的含义:如果玩家(A)选择9,那么他就永远不会受到有意义的攻击。如果另一个玩家(B)踩到该玩家广场并获胜,则A将移回其原始广场(相同)。但是现在发生了冲突,因为A和B都在那儿,所以B必须回到自己的正方形。因此,结果与实际战斗无关,B始终移回初始方格,而A始终保持原状。这样一来,我就可以编写这两者,通过阻塞其他人的路径来帮助其他提交。
Martin Ender 2014年

Answers:


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编码(以及映射的行数,因此可以重新插入换行符)。这种压缩使大小减小到未压缩地图的约三分之一,因此,阴影超过1000字节时,我可以存储约3000个单元的地图,这大致相当于使用18个漫游器完全探索地图。只要没有那么多的提交,我认为我不会费心找出解决该问题的方法。

经过5 dumbbots和1 s 的几次测试naivebot(我提交的其他文件)后,它要么表现很差(例如1或2个鸡蛋),要么表现出色(7到9个鸡蛋)。我可能会考虑一种更好的战斗策略,以及如何确定自己是否真正动弹了。两者都可以在某种程度上提高分数。

哦,如果您想知道机器人的名称,则应阅读The Order of The Stick(该漫画的最后一个面板)。

编辑:检测到发现的地图的边缘有一些错误。现在,我已经修复了它们,该机器人20在5 dumbbots和1的总得分中始终保持大约naivebot。这还差不多!如果您$stderr.puts map现在添加到我的漫游器中,则可以真正看到他如何系统地发现地图并同时收集所有鸡蛋。我还决定选择A不使用N任何不踩鸡蛋的方法,以减少返回到机器人原始单元的可能性(这会部分破坏地图)。

(它在6 naivebots内表现不佳,特别是因为当他们俩都反复想抓鸡蛋并选择​​鸡蛋时,很可能最终与另一个机器人陷入“僵局” N。我需要考虑一下……) )


3

爪哇兔

这个兔子还没有长大(我仍然打算进行更改),但这只是现在的起点。他寻找最接近的鸡蛋并朝着它前进。还没有墙面检测或边界检测。他会去捡拾落在地上的鸡蛋,否则他会试图推动他前进并攻击其他任何东西。如果附近没有鸡蛋,他将开始追踪最近的兔子。他们可能知道一些他不知道的东西。否则,他只会选择一个随机的方向行走。而且他非常健忘(不使用memory变量)。

前进的计划:

  • 根据墙/越界做出决策
  • 有目的地选择路径,而不是随机选择
  • 使用内存确定我之前的方向

更新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枚举要好得多。
Brian J

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

墙追随者

(故意双关语)在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 2014年

同样在这里。你能调查一下吗?我想以此测试我的新机器人。
Martin Ender 2014年

@ m.buettner如果更改sys.exit(0)为,该exit(0)怎么办?另外,我确实需要进行处理(现在,它假设自己是一个``),但是我真的没有时间。当我有时间的时候,我会来解决这个问题。
贾斯汀

不幸的是,@ Quincunx并没有改变任何东西。
Martin Ender 2014年
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.