KOTH:击沉


12

介绍

对于我的第5个KOTH,我向您提出了一个基于著名游戏《战舰》的挑战,但有一些曲折。您将只指挥一艘船,您可以在5个“传统”级中进行选择,但是您每回合就可以采取多种行动,包括移动!这是一个FFA(全民免费)计划,您的目标将是成为最后一名战舰。

原理

该游戏是回合制的。在游戏开始时,您必须选择飞船的等级。然后,每回合,玩家将能够根据自己的飞船执行几个动作。

游戏在2D网格(X,Y)上进行,该网格的边以这种方式定义:
X = 30 + numberOfPlayer
Y = 30 + numberOfPlayer
每艘船的起始位置都是随机的。

播放顺序在每个回合中都是随机的,您将不知道自己在“队列”中的位置或玩家数量。游戏持续100转或直到只有一艘船还活着。

每当您击中敌舰或被击中时,您将获得或失去积分。得分最高的玩家获胜。优胜者将获得赏金(价值取决于参与者的数量)。

控制器通过命令参数为您提供输入,而程序必须通过stdout输出。

句法

第一回合

您的程序将被调用一次,没有任何参数。您必须输入1到5(含)之间的整数来选择您的飞船:

1:驱逐舰[长度:2,移动/转弯:3,射击/转弯:1,射程:9,地雷:4]
技能:自由轮旋转(无冷却)

2:潜艇[长度:3,移动/转弯:2,射门/转弯:1,射程:5,地雷:4]
技能:可以俯冲/浮出水面(见输出)。在水下时,您只能使用“运动”动作,并且只能通过扫描才能看到。您不会被射击击中,但是会受到地雷的伤害。

3:巡洋舰[长度:3,移动/转弯:1,射击/转弯:2,射程:9,地雷:2]
技能:可以修理(参见输出)

4:战舰[长度:4,移动/转弯:1,射击/转弯:3,射程:7,地雷:1]
技能:可以屏蔽(见输出)

5:运载工具[长度:5,移动/转弯:1,射击/转弯:1,射程:7,地雷:3]
技能:射门对目标造成AOE(作用范围)伤害(1点飞溅伤害)。如果目标被击中出手,高达2个细胞船也将被破坏。

转弯

输入值

每次调用程序时,它将收到以下格式的参数:

Round;YourPlayerId;X,Y,Direction;Hull;Moves,Shots,Mines,Cooldown;Hits,Sunken,Damage;Underwater,Shield,Scan;Map

轮次按1分索引。

输入示例

1;8;1,12,0;111;1,2,2,0;0,0,0;0,0,0;UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.O.....UUUUUUUUXXXX.O.....UUUUUUUUXXXX.O.....UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUXXXX.......UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU

在这里,这是第一轮,您是玩家8。
您的船位于(X = 1,Y = 12)上,您的方向朝顶部(0 =顶部,1 =右,2 =底部,3 =左) )。
您的船体没有损坏(您的船长为3,每一位都为真[1 =正常,0 =损坏])。您可以移动1次,射击2次,还剩下2枚地雷,并且您的“技能”可用(冷却时间= 0)。
您没有击中任何东西,也没有击沉任何船只,您也没有被击中。
您不在水下,没有激活防护罩(如果有),也没有扫描。
稍后在地图上查看更多...

输出量

您必须输出一个String,描述您将在本回合中执行的动作。输出字符串中字符的顺序将定义操作的顺序。如果不超过飞船的限制,您可以多次输出相同的动作。如果一个或多个动作无效,则每个动作将分别视为W。以下是可用操作的列表:

M:沿您所面对的方向移动1个像元(消耗1个动作)
B:沿您所面对的方向向后移动1个像元(消耗1个动作)
C:顺时针旋转您的船(消耗1个移动/驱逐舰自由移动)
K:旋转您的船逆时针(消耗1步/驱逐舰免费)
A:沿您所面对的方向夯实您的船(仅当另一艘船在您所面对的方向上占据了牢房/不移动您的船/消耗了所有移动方可工作)
F:向范围内的一个单元射击1张(消耗1张)。必须遵循的作为目标的细胞以这种格式([+ - ] X [+ - ])Y /例如:F+2-3
N:将1个矿井到邻近你的船的小区(消耗所有镜头和1个矿)。必须紧跟此格式([+-] X [+-])Y /示例的目标单元格:N+0+1
S:激活您的扫描以进行下一轮扫描(消耗所有镜头)
R:修复损坏的船体,使其最接近船的“头部”(消耗所有镜头,冷却时间= 3圈/仅巡洋舰)
P:俯冲/表面(消耗所有镜头,冷却时间= 3回合,最大持续时间= 5回合/仅潜水艇)
D:激活盾牌,防止在下一回合中造成下一次伤害(消耗所有射击,冷却时间= 3 /仅战舰)
W:等待(不执行任何操作)

澄清:“使用所有动作/镜头”表示只有在此回合之前没有使用任何动作/镜头的情况下,才能使用此操作。

输出示例

MF+9-8CM :移动1个格,然后向相对于您船首的相对位置为(targetX = X + 9,targetY = Y-8)的格上射击,顺时针旋转,最后再次移动1个格。

游戏玩法

网格

这是一个示例网格(33 x 13),其中放置了3个玩家:

███████████████████████████████████
█                                 █
█       00                        █
█   2                             █
█   2                             █
█   2                             █
█                                 █
█       11111                     █
█        M                        █
█                                 █
█                                 █
█                                 █
█                                 █
█                                 █
███████████████████████████████████

我们可以看到,还有一个矿井 M玩家1旁边。

让玩家2了解位置和方向:

玩家2的位置是X = 3,Y = 4,方向=3。由于其方向是“底部”,因此他的其他“飞船单元”都位于其“头部”“上方”(X = 3,Y = 3) &(X = 3,Y = 2)

玩家地图

每个玩家收到的最后一个参数是他们的“自己的”地图。默认情况下,飞船会检测到5个单元格范围内的所有物体,但是它可以激活“ 扫描”以将此范围增加到9个

参数始终为361(19 x 19)个字符长。它代表以您的船的“头”为中心的正方形,其中每个字符对应于这样定义的元素:

.:空的牢房
O:您的船
M:地雷
X:墙壁(不在地图上的牢房)
U:未知(将通过扫描显示)
A:敌人的船未损坏的牢房
B:敌人的船损坏的牢房
C:敌人的船的水下未损坏的牢房(仅在扫描时可见)
D:敌舰水下破格(仅在扫描时可见)
W:残骸(死船)

该字符串由第一行的19个字符组成,后跟第二行的19个字符,直到第19行。

让我们看一下玩家2在进行扫描和不进行扫描的情况下所收到的内容(为了更好地理解而中断了行,但并未发送给玩家):

XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXX
XXXXXX.............
XXXXXX.......AA....
XXXXXX...O.........
XXXXXX...O.........
XXXXXX...O.........
XXXXXX.............
XXXXXX.......AAAAA.
XXXXXX........M....
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXX.............
XXXXXXXXXXXXXXXXXXX

UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUXXXXXXXXXXXUUUU
UUUUXX.........UUUU
UUUUXX.......AAUUUU
UUUUXX...O.....UUUU
UUUUXX...O.....UUUU
UUUUXX...O.....UUUU
UUUUXX.........UUUU
UUUUXX.......AAUUUU
UUUUXX........MUUUU
UUUUXX.........UUUU
UUUUXX.........UUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU
UUUUUUUUUUUUUUUUUUU

地雷

当船只移至地雷占据的牢房或向地雷开火时,就会触发地雷。行动“ Ram”无法触发地雷。

地雷对所有人,甚至对放置地雷的人,均造成AOE伤害(1点溅射伤害)。如果在爆炸半径范围内有另一个地雷,则地雷会触发“连锁”爆炸。

轮换

旋转是以船舶“头部”为中心的中心对称。如果将旋转放置在“目标位置”,则旋转只会触发地雷(您不会沿弧线触发地雷。

影响范围

通过以初始射击/爆炸(x,y)为中心的3x3(9个像元)正方形定义1个范围的飞溅伤害(用于地雷和舰载机的射击)。它命中那些坐标:[x - 1; y - 1],[x - 1; y],[x - 1; y + 1],[x; y - 1],[x; y],[x; y + 1],[x + 1; y - 1],[x + 1; y],[x + 1; y + 1]

计分

得分由以下公式定义:
Score = Hits + (Sunken x 5) - Damage taken - (Alive ? 0 : 10)

其中::
hits被Ram,Shot或Mine爆炸击中敌舰的次数(被击伤的敌方舰只细胞击中1次,包括链条爆炸)
sunken:导致其下沉的敌舰“最后击中”的
damage次数:收到的命中(没有被修理降低,但是被盾牌阻止了)
alive:检查船尾是否还活着(至少1个船体单元未损坏)

控制者

您可以在GitHub上找到控制器。它还包含两个用Java编写的samplebot。要使其运行,请签出项目并在Java IDE中将其打开。类Game的main方法中的入口点。需要Java 8。

要添加机器人,首先需要Java的编译版本(.class文件)或解释语言的源。将它们放置在项目的根文件夹中。然后,在players包中创建一个新的Java类(您可以在已经存在的机器人上举个例子)。此类必须实现Player才能重写方法String getCmd()。返回的字符串是运行您的机器人的shell命令。例如,您可以使用以下命令使Ruby机器人工作:返回“ C:\ Ruby \ bin \ ruby​​.exe MyBot.rb”;。最后,将Bot添加到Game类顶部的玩家数组中。

规则

  • 不得将Bot写为击败或支持其他特定的Bot。
  • 允许写入文件。请写入“ yoursubmissionname.txt”,游戏开始前该文件夹将被清空。禁止使用其他外部资源。
  • 您的提交有1秒的回复时间。
  • 提供命令以编译和运行提交。
  • 您可以撰写多个意见书

支持的语言

我会尽力支持每种语言,但需要在线免费提供。如果您不使用“主流”语言,请提供安装说明。

到目前为止,我可以运行:Java 6-7-8,PHP,Ruby,Perl,Python 2-3,Lua,R,node.js,Haskell,Kotlin,C ++ 11。


有趣的KotH,我只想问几个问题:我们可以写多个提交的内容吗(例如,每种类型的船只一个)?当您谈论AoE时,它在正确的位置周围是一个正方形(命中[x + 1; y + 1])?
Katenkyo

@Katenkyo是的,您可以编写多个意见书。是的,它命中了9个单元格:[x - 1; y - 1],[x - 1; y],[x - 1; y + 1],[x; y - 1],[x; y],[x; y + 1],[x + 1; y - 1],[x + 1; y],[x + 1; y + 1]
Thrax

那么,潜艇会自动浮出水面吗?在哪转?
破坏的柠檬

还同时转弯吗?
破坏的柠檬

撞球能力还有什么用?(为什么不开枪?)
破坏的柠檬

Answers:


3

RandomBot

这是一个示例机器人。它随机选择一艘船,一个动作和一个目标单元(如果需要)。

import java.util.Random;

public class RandomBot {

    int round;
    int playerID;

    public static void main(String[] args) {

        if (args.length == 0) {
            Random random = new Random();
            int ship = random.nextInt(5);
            String[] ships = { "1", "2", "3", "4", "5" };
            System.out.println(ships[ship]);
        } else {
            new RandomBot().play(args[0].split(";"));
        }
    }

    private void play(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        String[] actions = { "M", "B", "C", "K", "F", "S", "N", "A" };
        Random random = new Random();
        int action = random.nextInt(8);

        int rangeX = random.nextInt(5);
        int rangeY = random.nextInt(5);
        int mineX = random.nextInt(1);
        int mineY = random.nextInt(1);

        String signX = random.nextInt(1) == 1 ? "+" : "-";
        String signY = random.nextInt(1) == 1 ? "+" : "-";

        System.out.println(actions[action] + (action == 4 ? signX + rangeX + signY + rangeY : "") + (action == 6 ? signX + mineX + signY + mineY : ""));
    }

}

被动式

这是一个示例机器人。它什么也没做。

public class PassiveBot {

    int round;
    int playerID;

    public static void main(String[] args) {

        if (args.length == 0) {
            System.out.println("5");
        } else {
            new PassiveBot().play(args[0].split(";"));
        }
    }

    private void play(String[] args) {

        round = Integer.parseInt(args[0]);
        playerID = Integer.parseInt(args[1]);

        System.out.println("W");
    }

}

3

PeaceMaker,Python 2(战舰)

PeaceMaker向最近的敌人射击3次(螺旋距离),并在一条直线上来回移动,同时与地雷保持至少2个格。

from os import sys

def reversedSpiralOrder(length):

    #Initialize our four indexes
    top = 0
    down = length - 1
    left = 0
    right = length - 1
    result = ""

    while 1:

        # Print top row
        for j in range(left, right + 1):
            result += str(top * length + j) + ";"
        top += 1
        if top > down or left > right:
            break

        # Print the rightmost column
        for i in range(top, down + 1):
            result += str(i * length + right) + ";"
        right -= 1
        if top > down or left > right:
            break

        # Print the bottom row
        for j in range(right, left + 1, -1):
            result += str(down * length + j) + ";"
        down -= 1
        if top > down or left > right:
            break

        # Print the leftmost column
        for i in range(down, top + 1, -1):
            result += str(i * length + left) + ";"
        left += 1
        if top > down or left > right:
            break

    result = result.split(";")
    del result[-1]
    return result[::-1]

def canMove(x, y, direction, hull, map):

    # M = 1, B = 2
    moves = 0

    if direction == 0:
        y1 = -1
        y2 = -2
        hx1 = hx2 = x1 = x2 = 0
        hy1 = -y1 + hull
        hy2 = -y2 + hull
    elif direction == 1:
        x1 = 1
        x2 = 2
        hy1 = hy2 = y1 = y2 = 0
        hx1 = -x1 - hull
        hx2 = -x2 - hull
    elif direction == 2:
        y1 = 1
        y2 = 2
        hx1 = hx2 = x1 = x2 = 0
        hy1 = -y1 - hull
        hy2 = -y2 - hull
    elif direction == 3:
        x1 = -1
        x2 = -2
        hy1 = hy2 = y1 = y2 = 0
        hx1 = -x1 + hull
        hx2 = -x2 + hull

    if map[y + y1][x + x1] == "." and map[y + y2][x + x2] != "M":
        moves += 1

    if map[y + hy1][x + hx1] == "." and map[y + hy2][x + hx2] != "M":
        moves += 2

    return moves

if len(sys.argv) <= 1:
    f = open("PeaceMaker.txt","w")
    f.write("")
    print "4"
else:
    arguments = sys.argv[1].split(";")
    sight = 19

    round = int(arguments[0])
    playerID = int(arguments[1])
    x = int(arguments[2].split(",")[0])
    y = int(arguments[2].split(",")[1])
    direction = int(arguments[2].split(",")[2])
    hull = arguments[3]
    moves = int(arguments[4].split(",")[0])
    shots = int(arguments[4].split(",")[1])
    mines = int(arguments[4].split(",")[2])
    cooldown = int(arguments[4].split(",")[3])
    hits = int(arguments[5].split(",")[0])
    kills = int(arguments[5].split(",")[0])
    taken = int(arguments[5].split(",")[0])
    underwater = int(arguments[6].split(",")[0])
    shield = int(arguments[6].split(",")[1])
    scan = int(arguments[6].split(",")[2])
    map = [[list(arguments[7])[j * sight + i] for i in xrange(sight)] for j in xrange(sight)]

    initialShots = shots


    priorities = reversedSpiralOrder(sight)

    actions = ""
    sighted = 0
    for priority in priorities:
        pX = int(priority) % sight
        pY = int(priority) / sight

        if map[pY][pX] == "A":
            sighted += 1
            if shots > 0:
                shots -= 1
                actions += "F" + ("+" if pX - 9 >= 0 else "") + str(pX - 9)  + ("+" if pY - 9 >= 0 else "") + str(pY - 9)

    if shots == initialShots and sighted > 0:
        actions += "D"
    elif shots == initialShots and sighted <= 0:
        actions += "S"
    else:
        actions += ""

    f = open("PeaceMaker.txt","r")
    fC = f.read(1)
    lastDirection = int("1" if fC == "" else fC)

    y = 9
    x = 9

    if lastDirection == 1:
        if canMove(x, y, direction, len(hull), map) == 1 or canMove(x, y, direction, len(hull), map) == 3:
            actions += "M"
        elif canMove(x, y, direction, len(hull), map) == 2:
            actions += "B"
            lastDirection = 0
    elif lastDirection == 0:
        if canMove(x, y, direction, len(hull), map) == 2 or canMove(x, y, direction, len(hull), map) == 3:
            actions += "B"
        elif canMove(x, y, direction, len(hull), map) == 1:
            actions += "M"
            lastDirection = 1

    f = open("PeaceMaker.txt","w")
    f.write(str(lastDirection))

    print actions

1
“和平制造者射击”。你在那丢了我
Okx
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.