Scrappers v0.1: Lập trình viên lính đánh thuê


22

Trong một thế giới hoang tàn, chiến tranh tàn phá, nơi các thành phố bị lũ côn đồ và kẻ trộm tràn ngập, nền văn minh đã tái tạo lại dưới hình thức các hợp tác xã công nghiệp nhỏ, biệt lập, nằm rải rác trong cảnh quan không có người ở trước đây. Sự tồn tại của các cộng đồng này phụ thuộc vào các nhóm công nhân lính đánh thuê được gọi là "người cạo", những người tìm kiếm lãnh thổ chưa được khai thác để tìm các vật liệu có giá trị để bán cho các chuồng. Khi các tài liệu này ngày càng khan hiếm, việc tháo dỡ đã trở thành một nghề khó khăn và nguy hiểm hơn bao giờ hết. Công nhân của con người mong manh hầu hết đã được thay thế bằng các robot độc lập từ xa, được gọi là bot bot, và một lính đánh thuê điển hình có nhiều khả năng là một lập trình viên lành nghề hơn là một thợ hàn vũ trang. Vì sự hiện diện của con người trong việc loại bỏ đã giảm, nên cũng có sự tôn trọng giữa các nhóm lính đánh thuê dành cho nhau. Bots được trang bị không chỉ để thu thập phế liệu, mà để bảo vệ nó, và trong một số trường hợp lấy nó bằng vũ lực. Các lập trình viên bot làm việc không mệt mỏi khi nghĩ ra các chiến lược mới để vượt qua các đối thủ của đối thủ, dẫn đến các bot ngày càng hung hăng và một mối nguy hiểm khác đối với những người mạo hiểm bên ngoài bức tường của cộng đồng của họ.

Mẫu trò chơi Scrappers

(vâng, logo bị cắt xén vui nhộn)

Chào mừng bạn đến với Scrappers!

Đây là phiên bản đầu tiên của Scrappers trong đó việc thu gom phế liệu và nhà máy chưa được triển khai. Về cơ bản, đó là một cảnh quay của họ.

Bạn là một lập trình viên lính đánh thuê được giao nhiệm vụ tạo ra một chương trình để điều khiển các bot của bạn từ xa để chiến thắng các nhóm cào bằng đối thủ. Các bot của bạn là những cỗ máy giống như con nhện bao gồm máy phát điện và lá chắn ở lõi của chúng, được bao quanh bởi nhiều phần phụ được trang bị kẹp, cắt và tấn công. Máy phát điện có khả năng sản xuất 12 đơn vị năng lượng (pu) mỗi tích tắc (đơn vị thời gian của máy cạo). Bạn có thể kiểm soát cách thức phân phối sức mạnh này trong ba nhu cầu chính của bot: di chuyển, khiên và hỏa lực.

Bot Scrapper là những cỗ máy đặc biệt nhanh nhẹn và có thể dễ dàng di chuyển qua, bên dưới và xung quanh bất kỳ chướng ngại vật nào chúng gặp phải. Do đó, va chạm không phải là điều mà chương trình của bạn cần xem xét. Bạn có thể tự do phân bổ tất cả, một số hoặc không có 12pu nào có sẵn cho bot của bạn để di chuyển, miễn là bạn xử lý toàn bộ số. Phân bổ 0pu cho các chức năng chuyển động của bot sẽ khiến nó bất động. Phân bổ 2pu sẽ cho phép bot di chuyển 2 đơn vị khoảng cách (du) mỗi đánh dấu, 5pu sẽ dẫn đến 5du / tick, 11pu sẽ dẫn đến 11du / tick, v.v.

Máy tạo khiên của bot của bạn chiếu một bong bóng năng lượng lệch hướng xung quanh cơ thể chúng. Một khiên có thể làm chệch hướng tới 1 sát thương trước khi bật, do đó, bot của bạn bị lộ cho đến khi bộ tạo khiên của nó tạo ra đủ sức mạnh để đưa khiên trở lại đúng vị trí. Bạn có thể tự do phân bổ tất cả, một số hoặc không có 12pu nào có sẵn cho bot của bạn theo hướng bảo vệ. Phân bổ 0pu vào khiên của bot có nghĩa là nó sẽ không bao giờ tạo ra khiên. Phân bổ 2pu sẽ cho phép bot tạo ra khiên mới 2 trong số 12 tick, hoặc cứ sau 6 tick. 5pu sẽ giúp tái tạo khiên 5 trong số 12 tích tắc, v.v.

Bằng cách xây dựng một điện tích trong laser hàn của họ, các bot của bạn có thể bắn ra các chùm tia gây sát thương trong khoảng cách ngắn với độ chính xác hợp lý. Giống như tạo khiên, tốc độ bắn của bot của bạn phụ thuộc vào năng lượng được phân bổ cho laser của chúng. Phân bổ 0pu cho laser của bot có nghĩa là nó sẽ không bao giờ bắn. Phân bổ 2pu sẽ cho phép một bot bắn 2 trong số 12 tích tắc, v.v. Tia laser của bot sẽ di chuyển cho đến khi nó gặp một vật thể hoặc phân tán thành vô dụng, vì vậy hãy chú ý đến ngọn lửa thân thiện. Mặc dù các bot của bạn khá chính xác, nhưng chúng không hoàn hảo. Bạn nên mong đợi độ chính xác +/- 2,5 độ. Khi chùm tia laser truyền đi, các hạt của nó bị lệch dần bởi bầu khí quyển cho đến khi chùm tia trở nên vô hại một cách hiệu quả với khoảng cách vừa đủ. Một tia laser gây sát thương 1 điểm ở phạm vi trống điểm và giảm 2,5% cho mỗi chiều dài bot di chuyển.

Các bot Scrapper đủ tự chủ để xử lý các chức năng cơ bản, nhưng dựa vào bạn, lập trình viên của chúng, để làm cho chúng hữu ích như một nhóm. Là một lập trình viên, bạn có thể đưa ra các lệnh sau cho từng bot riêng lẻ:

  • DI CHUYỂN: Chỉ định tọa độ mà bot sẽ di chuyển.
  • MỤC TIÊU: Xác định một bot để nhắm và bắn vào khi phân bổ năng lượng cho phép.
  • SỨC MẠNH: Phân phối lại sức mạnh giữa chuyển động, khiên và hỏa lực.

Chi tiết trò chơi kỹ thuật

Có ba chương trình mà bạn cần phải làm quen. Các Game Engine là nâng nặng và cung cấp một API TCP rằng các chương trình nghe nhạc kết nối. Chương trình trình phát là những gì bạn sẽ viết và tôi đã cung cấp một số ví dụ với mã nhị phân ở đây . Cuối cùng, Trình kết xuất xử lý đầu ra từ Game Engine để tạo ra một GIF của trận chiến.

Công cụ trò chơi

Bạn có thể tải về công cụ trò chơi từ đây . Khi trò chơi được khởi chạy, nó sẽ bắt đầu nghe trên cổng 50000 (hiện không thể định cấu hình) cho các kết nối người chơi. Khi nhận được hai kết nối người chơi, nó sẽ gửi tin nhắn SYN SÀNG cho người chơi và bắt đầu trò chơi. Các chương trình người chơi gửi lệnh đến trò chơi thông qua API TCP. Khi trò chơi kết thúc, một tệp JSON có tên scrappers.json (đồng thời, hiện không thể định cấu hình) được tạo. Đây là những gì trình kết xuất sử dụng để tạo GIF của trò chơi.

API TCP

Các chương trình người chơi và công cụ trò chơi giao tiếp bằng cách chuyển các chuỗi JSON kết thúc dòng mới trở lại và thứ tư qua kết nối TCP. Chỉ có năm thông báo JSON khác nhau có thể được gửi hoặc nhận.

Tin nhắn đã sẵn sàng

Thông báo SYN SÀNG được gửi từ trò chơi đến các chương trình của người chơi và chỉ được gửi một lần. Thông báo này cho chương trình người chơi biết ID người chơi (PID) của nó là gì và cung cấp danh sách tất cả các bot trong trò chơi. PID là cách duy nhất để xác định Bots nào thân thiện với kẻ thù. Thông tin thêm về các lĩnh vực bot dưới đây.

{
    "Type":"READY",  // Message type
    "PID":1,         // Player ID
    "Bots":[         // Array of bots
        {
            "Type":"BOT",
            "PID":1,
            "BID":1,
            "X":-592,
            ...
        },
        ...
    ]
}

Tin nhắn Bot

Thông báo BOT được gửi từ trò chơi đến các chương trình của người chơi và được gửi khi thuộc tính của bot thay đổi. Chẳng hạn, khi các lá chắn được chiếu hoặc thay đổi sức khỏe, một thông điệp BOT được gửi. Bot ID (BID) chỉ duy nhất trong một người chơi cụ thể.

{
    "Type":"BOT",   // Message type
    "PID":1,        // Player ID
    "BID":1,        // Bot ID
    "X":-592,       // Current X position
    "Y":-706,       // Current Y position
    "Health":12,    // Current health out of 12
    "Fired":false,  // If the Bot fired this tick
    "HitX":0,       // X position of where the shot landed
    "HitY":0,       // Y position of where the shot landed
    "Scrap":0,      // Future use. Ignore.
    "Shield":false  // If the Bot is currently shielded.
}

Di chuyển tin nhắn

Thông báo MOVE là một lệnh từ chương trình người chơi đến trò chơi (nhưng hãy nghĩ nó như một lệnh cho bot). Đơn giản chỉ cần xác định bot bạn muốn di chuyển và tọa độ. Giả định rằng bạn đang chỉ huy bot của riêng mình, vì vậy không cần thiết phải có PID.

{
    "Cmd":"MOVE",
    "BID":1,        // Bot ID
    "X":-592,       // Destination X coordinate
    "Y":-706,       // Destination Y coordinate
}

Thông điệp mục tiêu

Thông báo TARGET báo cho một trong các bot của bạn nhắm mục tiêu đến một số bot khác.

{
    "Cmd":"TARGET",
    "BID":1,        // Bot ID
    "TPID":0,       // The PID of the bot being targeted
    "TBID":0,       // The BID of the bot being targeted
}

Thông điệp sức mạnh

Thông báo POWER phân bổ lại 12pu có sẵn cho bot của bạn giữa chuyển động, hỏa lực và khiên.

{
    "Cmd":"POWER",
    "BID":1,        // Bot ID
    "FPow":4,       // Set fire power
    "MPow":4,       // Set move power
    "SPow":4,       // Set shield power
}

Cuộc thi

Nếu bạn đủ can đảm để khám phá những vùng đất chưa được khám phá, bạn sẽ được tham gia một giải đấu loại trừ hai lần chống lại những người bạn lính đánh thuê của bạn. Vui lòng tạo câu trả lời cho bài gửi của bạn và dán mã của bạn hoặc cung cấp liên kết đến repo git, ý chính, v.v ... Mọi ngôn ngữ đều được chấp nhận, nhưng bạn nên cho rằng tôi không biết gì về ngôn ngữ và bao gồm các hướng dẫn để chạy chương trình của bạn. Tạo nhiều bài nộp như bạn muốn và chắc chắn cung cấp tên cho họ!

Các chương trình người chơi mẫu sẽ được đưa vào giải đấu, vì vậy tôi khuyên bạn nên thử nghiệm bot của mình với chúng. Giải đấu sẽ bắt đầu khoảng hai tuần sau khi chúng tôi nhận được bốn lần gửi chương trình duy nhất. Chúc may mắn!

--- Winner's Bracket ---

** Contestants will be randomly seeded **
__________________
                  |___________
__________________|           |
                              |___________
__________________            |           |
                  |___________|           |
__________________|                       |
                                          |________________
__________________                        |                |
                  |___________            |                |
__________________|           |           |                |
                              |___________|                |
__________________            |                            |
                  |___________|                            |
__________________|                                        |
                                                           |
--- Loser's Bracket ---                                    |___________
                                                           |
___________                                                |
           |___________                                    |
___________|           |___________                        |
                       |           |                       |
            ___________|           |                       |
                                   |___________            |
___________                        |           |           |
           |___________            |           |___________|
___________|           |___________|           |
                       |                       |
            ___________|            ___________|

Thông tin quan trọng khác

  • Trò chơi chạy ở tốc độ 12 tick / giây, do đó bạn sẽ không nhận được tin nhắn thường xuyên hơn mỗi 83 mili giây hoặc lâu hơn.
  • Mỗi bot có đường kính 60du. Lá chắn chiếm không gian bổ sung. Với độ chính xác +/- 2,5%, tỷ lệ trúng bot ở một khoảng cách nhất định được biểu thị bằng biểu đồ này:

đồ thị chính xác

  • Sự phân rã của tổn thương laser trên khoảng cách được biểu thị bằng biểu đồ này:

đồ thị hư hỏng

  • Độ chính xác và phân rã laser của bot kết hợp để tính toán thiệt hại trung bình trên mỗi phát bắn. Đó là, thiệt hại trung bình mà bot sẽ gây ra khi bắn từ một khoảng cách nhất định. Thiệt hại trên mỗi phát bắn được biểu thị bằng biểu đồ này:

thiệt hại trên mỗi đồ thị bắn

  • Tia laser của bot bắt nguồn một nửa giữa trung tâm của bot và cạnh của nó. Do đó, xếp các bot của bạn sẽ dẫn đến hỏa hoạn thân thiện.
  • Các bot kẻ thù sinh sản cách nhau khoảng 1440du.
  • Trò chơi kết thúc nếu 120 tích tắc (10 giây) trôi qua mà không gây ra bất kỳ thiệt hại nào.
  • Người chiến thắng là người chơi có nhiều bot nhất, sau đó khỏe mạnh nhất khi trò chơi kết thúc.

Hiểu về hình ảnh kết xuất

  • Người chơi 1 được đại diện bởi các vòng tròn và người chơi 2 bằng các hình lục giác.
  • Màu sắc của bot thể hiện sự phân bổ sức mạnh của nó. Nhiều màu đỏ hơn có nghĩa là nhiều năng lượng hơn đã được phân bổ để bắn. Nhiều màu xanh hơn có nghĩa là nhiều lá chắn hơn. Nhiều màu xanh lá cây có nghĩa là di chuyển nhiều hơn.
  • "Lỗ hổng" trong cơ thể của bot đại diện cho thiệt hại. Lỗ càng lớn, thiệt hại đã được thực hiện.
  • Các vòng tròn màu trắng bao quanh bot là lá chắn. Nếu một bot có một lá chắn ở cuối lượt, nó sẽ được hiển thị. Nếu lá chắn được bật lên bằng cách nhận sát thương, nó sẽ không được hiển thị.
  • Các đường màu đỏ giữa các bot đại diện cho ảnh chụp.
  • Khi một bot bị giết, một "vụ nổ" lớn màu đỏ được hiển thị.

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Dennis

Câu trả lời:


4

Người cực đoan (Python 3)

Bot này sẽ luôn dành toàn bộ sức mạnh của mình cho một thứ: che chắn nếu nó không được che chắn, di chuyển nếu nó ở ngoài vị trí và bắn khác. Đánh bại tất cả các bot mẫu trừ món ăn chết.

import socket, sys, json
from types import SimpleNamespace
s=socket.socket()
s.connect(("localhost",50000))
f=s.makefile()
bots={1:{},2:{}}
def hook(obj):
    if "BID" in obj:
        try:
            bot = bots[obj["PID"]][obj["BID"]]
        except KeyError:
            bot = SimpleNamespace(**obj)
            bots[bot.PID][bot.BID] = bot
        else:
            bot.__dict__.update(obj)
        return bot
    return SimpleNamespace(**obj)
decoder = json.JSONDecoder(object_hook=hook)
PID = decoder.decode(f.readline()).PID
#side effect: .decode fills bots dictionary
def turtle(bot):
    send({"Cmd":"POWER","BID":bot.BID,"FPow":0,"MPow":0,"SPow":12})
    bot.firing = bot.moving = False
def send(msg):
    s.send(json.dumps(msg).encode("ascii")+b"\n")
for bot in bots[PID].values():
    turtle(bot)
target_bot = None
def calc_target_bot():
    ok_bots = []
    for bot2 in bots[(2-PID)+1].values():
        if bot2.Health < 12:
            ok_bots.append(bot2)
    best_bot = (None,2147483647)
    for bot2 in (ok_bots or bots[(2-PID)+1].values()):
        dist = bot_dist(bot, bot2)
        if dist < best_bot[1]:
            best_bot = bot2, dist
    return best_bot[0]
def bot_dist(bot, bot2):
    if isinstance(bot, tuple):
        bot=SimpleNamespace(X=bot[0],Y=bot[1])
    if isinstance(bot2, tuple):
        bot=SimpleNamespace(X=bot[0],Y=bot[1])
    distx = bot2.X - bot.X
    disty = bot2.Y - bot.Y
    return (distx**2+disty**2)**.5
LENGTH_Y = -80
LENGTH_X = 80
line = None
def move(bot, pos):
    bot.firing = False
    bot.moving = True
    send({"Cmd":"POWER","BID":bot.BID,"FPow":0,"MPow":12,"SPow":0})
    send({"Cmd":"MOVE","BID": bot.BID,"X":pos[0],"Y":pos[1]})
def go(bot, line):
    if line != None:
        position = (line[0]+LENGTH_X*(bot.BID-6),line[1]+LENGTH_Y*(bot.BID-6))
        if not close_pos(bot, position, 1.5):
            return True, position
    return False, None
def close_pos(bot, pos, f):
    if abs(bot.X - pos[0]) <= abs(LENGTH_X*f) or \
        abs(bot.Y - pos[1]) <= abs(LENGTH_Y*f):
        return True
def set_line(bot):
    global line
    newline = bot.X - LENGTH_X*(bot.BID - 6), bot.Y - LENGTH_Y*(bot.BID - 6)
    if line == None or bot_dist(line, target_bot) < (bot_dist(newline, target_bot) - 100):
        line = newline
def move_or_fire(bot):
    global target_bot, line
    if not target_bot:
        target_bot = calc_target_bot()
    followline, place = go(bot, line)
    if not target_bot:
        #Game should be over, but just in case ...
        return turtle(bot)
    elif bot_dist(bot, target_bot) > 2000:
        line = None
        position = (target_bot.X, target_bot.Y)
        position = (position[0]+LENGTH_X*(bot.BID-6),position[1]+LENGTH_Y*(bot.BID-6))
        move(bot, position)
    elif followline:
        set_line(bot)
        move(bot, place)
    elif any(close_pos(bot, (bot2.X, bot2.Y), .6) for bot2 in bots[PID].values() if bot != bot2):
        try:
            move(bot, place)
        except TypeError:
            turtle(bot)
        set_line(bot)
        #Let the conflicting bots resolve
    else:
        set_line(bot)
        bot.firing = True
        bot.moving = False
        send({"Cmd":"POWER","BID":bot.BID,"FPow":12,"MPow":0,"SPow":0})
        send({"Cmd":"TARGET","BID": bot.BID,
              "TPID":target_bot.PID,"TBID":target_bot.BID})
def dead(bot):
    del bots[bot.PID][bot.BID]
def parse_message():
    global target_bot
    line = f.readline()
    if not line:
        return False
    bot = decoder.decode(line)
    assert bot.Type == "BOT"
    del bot.Type
    if bot.PID == PID:
        if bot.Health <= 0:
            dead(bot)
        elif not bot.Shield:
            turtle(bot)
        else:
            move_or_fire(bot)
    elif target_bot and (bot.BID == target_bot.BID):
        target_bot = bot
        if target_bot.Health <= 0:
            target_bot = None
            dead(bot)
            for bot in bots[PID].values():
                if bot.firing or bot.moving:
                    move_or_fire(bot)
    elif bot.Health <= 0:
        dead(bot)
    assert bot.Health > 0 or bot.BID not in bots[bot.PID]
    return True
while parse_message():
    pass

Tôi không quen với python, nhưng dường như có nhiều vấn đề với việc gửi của bạn: 1) dòng 212 120 không được thụt lề chính xác và 2) target_hp không được xác định. Tôi có thể sửa (1) nhưng (2) đang ngăn tôi chạy trình của bạn. Nhưng nó có thể là thiếu kinh nghiệm của tôi với trăn.
Moogie

Toàn bộ câu lệnh if đó còn sót lại từ một số gỡ lỗi và hoàn toàn không cần thiết
pppery 16/07/17

2

Ít liều lĩnh hơn ( Đi )

go run main.go

Ban đầu, tôi dự định sửa đổi một chút chương trình mẫu Reckless Abandon để các bot không kích hoạt nếu một bot thân thiện cản đường. Tôi đã kết thúc với các bot chọn mục tiêu mới khi có một người bạn, điều mà tôi đoán là tốt hơn. Nó sẽ đánh bại hai chương trình đầu tiên.

Mã không hoàn hảo. Logic để xác định xem một phát bắn có rõ ràng sử dụng một số phỏng đoán khá ngẫu nhiên.

Dường như không có một cơ chế nào để nhắm mục tiêu "không ai". Đó có thể là một tính năng tốt để thêm.

API TCP rất hay ở bất kỳ ngôn ngữ nào cũng có thể chơi, nhưng nó cũng có nghĩa là rất nhiều mã soạn sẵn. Nếu tôi không quen thuộc với ngôn ngữ mà các bot mẫu được viết, có lẽ tôi sẽ không có động lực để chơi với nó. Một bộ sưu tập các mẫu nồi hơi trong các ngôn ngữ khác nhau sẽ là một bổ sung tuyệt vời cho các repos git khác của bạn.

(hầu hết các mã sau đây là sao chép / dán từ một trong các bot mẫu)

package main

import (
    "bufio"
    "encoding/json"
    "flag"
    "io"
    "log"
    "math"
    "math/rand"
    "net"
    "time"
)

const (
    MaxHealth int = 12
    BotSize float64 = 60
)

var (
    // TCP connection to game.
    gameConn net.Conn
    // Queue of incoming messages
    msgQueue chan MsgQueueItem
)

// MsgQueueItem is a simple vehicle for TCP
// data on the incoming message queue.
type MsgQueueItem struct {
    Msg string
    Err error
}

// Command contains all the fields that a player might
// pass as part of a command. Fill in the fields that
// matter, then marshal into JSON and send.
type Command struct {
    Cmd  string
    BID  int
    X    int
    Y    int
    TPID int
    TBID int
    FPow int
    MPow int
    SPow int
}

// Msg is used to unmarshal every message in order
// to check what type of message it is.
type Msg struct {
    Type string
}

// BotMsg is used to unmarshal a BOT representation
// sent from the game.
type BotMsg struct {
    PID, BID   int
    X, Y       int
    Health     int
    Fired      bool
    HitX, HitY int
    Scrap      int
    Shield     bool
}

// ReadyMsg is used to unmarshal the READY
// message sent from the game.
type ReadyMsg struct {
    PID  int
    Bots []BotMsg
}

// Create our game data storage location
var gdb GameDatabase

func main() {

    var err error
    gdb = GameDatabase{}
    msgQueue = make(chan MsgQueueItem, 1200)

    // What port should we connect to?
    var port string
    flag.StringVar(&port, "port", "50000", "Port that Scrappers game is listening on.")
    flag.Parse()

    // Connect to the game
    gameConn, err = net.Dial("tcp", ":"+port)
    if err != nil {
        log.Fatalf("Failed to connect to game: %v\n", err)
    }
    defer gameConn.Close()

    // Process messages off the incoming message queue
    go processMsgs()

    // Listen for message from the game, exit if connection
    // closes, add message to message queue.
    reader := bufio.NewReader(gameConn)
    for {
        msg, err := reader.ReadString('\n')
        if err == io.EOF {
            log.Println("Game over (connection closed).")
            return
        }
        msgQueue <- MsgQueueItem{msg, err}
    }
}

func runStrategy() {

    // LESS RECKLESS ABANDON
    // - For three seconds, all bots move as fast as possible in a random direction.
    // - After three seconds, split power between speed and firepower.
    // - Loop...
    //     - Identify the enemy bot with the lowest health.
    //     - If a friendly bot is in the way, pick someone else.
    //     - If there's a tie, pick the one closest to the group.
    //     - Everybody moves towards and targets the bot.

    var myBots []*GDBBot

    // Move quickly in random direction.
    // Also, might as well get a shield.
    myBots = gdb.MyBots()
    for _, bot := range myBots {
        send(bot.Power(0, 11, 1))
        radians := 2.0 * math.Pi * rand.Float64()
        x := bot.X + int(math.Cos(radians)*999)
        y := bot.Y + int(math.Sin(radians)*999)
        send(bot.Move(x, y))
    }

    // Wait three seconds
    time.Sleep(3 * time.Second)

    // Split power between speed and fire
    for _, bot := range myBots {
        send(bot.Power(6, 6, 0))
    }

    for { // Loop indefinitely

        // Find a target

        candidates := gdb.TheirBots()

        // Order by health
        reordered := true
        for reordered {
            reordered = false
            for n:=1; n<len(candidates); n++ {
                if candidates[n].Health < candidates[n-1].Health {
                    temp := candidates[n-1]
                    candidates[n-1] = candidates[n]
                    candidates[n] = temp
                    reordered = true
                }
            }
        }

        // Order by closeness

        // My swarm position is...
        ttlX, ttlY := 0, 0
        myBots = gdb.MyBots() // Refresh friendly bot list
        for _, bot := range myBots {
            ttlX += bot.X
            ttlY += bot.Y
        }
        avgX := ttlX / len(myBots)
        avgY := ttlY / len(myBots)

        // Sort
        reordered = true
        for reordered {
            reordered = false
            for n:=1; n<len(candidates); n++ {
                thisDist := distance(avgX, avgY, candidates[n].X, candidates[n].Y)
                lastDist := distance(avgX, avgY, candidates[n-1].X, candidates[n-1].Y)
                if thisDist < lastDist {
                    temp := candidates[n-1]
                    candidates[n-1] = candidates[n]
                    candidates[n] = temp
                    reordered = true
                }
            }
        }

        // For all my bots, try to find the weakest enemy that my bot has a clear shot at
        myBots = gdb.MyBots()
        for _, bot := range myBots {
            for _, enemy := range candidates {

                clear := clearShot(bot, enemy)
                if clear {

                    // Target and move towards
                    send(bot.Target(enemy))
                    send(bot.Follow(enemy))
                    break
                }
                log.Println("NO CLEAR SHOT")
            }
        }

        time.Sleep(time.Second / 24)
    }
}

func clearShot(bot, enemy *GDBBot) bool {

    deg45rad := math.Pi*45/180
    deg30rad := math.Pi*30/180
    deg15rad := math.Pi*15/180
    deg5rad := math.Pi*5/180

    myBots := gdb.MyBots()
    enmyAngle := math.Atan2(float64(enemy.Y-bot.Y), float64(enemy.X-bot.X))

    for _, friend := range myBots {

        dist := distance(bot.X, bot.Y, friend.X, friend.Y)
        angle := math.Atan2(float64(friend.Y-bot.Y), float64(friend.X-bot.X))
        safeAngle := angle

        if dist < BotSize*3 {
            safeAngle = deg45rad/2
        } else if dist < BotSize*6 {
            safeAngle = deg30rad/2
        } else if dist < BotSize*9 {
            safeAngle = deg15rad/2
        } else {
            safeAngle = deg5rad/2
        }

        if angle <= enmyAngle+safeAngle &&  angle >= enmyAngle-safeAngle {
            return false
        }
    }

    return true
}

func processMsgs() {

    for {
        queueItem := <-msgQueue
        jsonmsg := queueItem.Msg
        err := queueItem.Err

        if err != nil {
            log.Printf("Unknown error reading from connection: %v", err)
            continue
        }

        // Determine the type of message first
        var msg Msg
        err = json.Unmarshal([]byte(jsonmsg), &msg)
        if err != nil {
            log.Printf("Failed to marshal json message %v: %v\n", jsonmsg, err)
            return
        }

        // Handle the message type

        // The READY message should be the first we get. We
        // process all the data, then kick off our strategy.
        if msg.Type == "READY" {

            // Unmarshal the data
            var ready ReadyMsg
            err = json.Unmarshal([]byte(jsonmsg), &ready)
            if err != nil {
                log.Printf("Failed to marshal json message %v: %v\n", jsonmsg, err)
            }

            // Save our player ID
            gdb.PID = ready.PID
            log.Printf("My player ID is %v.\n", gdb.PID)

            // Save the bots
            for _, bot := range ready.Bots {
                gdb.InsertUpdateBot(bot)
            }

            // Kick off our strategy
            go runStrategy()

            continue
        }

        // The BOT message is sent when something about a bot changes.
        if msg.Type == "BOT" {

            // Unmarshal the data
            var bot BotMsg
            err = json.Unmarshal([]byte(jsonmsg), &bot)
            if err != nil {
                log.Printf("Failed to marshal json message %v: %v\n", jsonmsg, err)
            }

            // Update or add the bot
            gdb.InsertUpdateBot(bot)

            continue
        }

        // If we've gotten to this point, then we
        // were sent a message we don't understand.
        log.Printf("Recieved unknown message type \"%v\".", msg.Type)
    }
}

///////////////////
// GAME DATABASE //
///////////////////

// GameDatabase stores all the data
// sent to us by the game.
type GameDatabase struct {
    Bots []GDBBot
    PID  int
}

// GDBBot is the Bot struct for the Game Database.
type GDBBot struct {
    BID, PID int
    X, Y     int
    Health   int
}

// InserUpdateBot either updates a bot's info,
// deletes a dead bot, or adds a new bot.
func (gdb *GameDatabase) InsertUpdateBot(b BotMsg) {

    // If this is a dead bot, remove and ignore
    if b.Health <= 0 {

        for i := 0; i < len(gdb.Bots); i++ {
            if gdb.Bots[i].BID == b.BID && gdb.Bots[i].PID == b.PID {
                gdb.Bots = append(gdb.Bots[:i], gdb.Bots[i+1:]...)
                return
            }
        }
        return
    }

    // Otherwise, update...
    for i, bot := range gdb.Bots {
        if b.BID == bot.BID && b.PID == bot.PID {
            gdb.Bots[i].X = b.X
            gdb.Bots[i].Y = b.Y
            gdb.Bots[i].Health = b.Health
            return
        }
    }

    // ... or Add
    bot := GDBBot{}
    bot.PID = b.PID
    bot.BID = b.BID
    bot.X = b.X
    bot.Y = b.Y
    bot.Health = b.Health
    gdb.Bots = append(gdb.Bots, bot)
}

// MyBots returns a pointer array of GDBBots owned by us.
func (gdb *GameDatabase) MyBots() []*GDBBot {
    bots := make([]*GDBBot, 0)
    for i, bot := range gdb.Bots {
        if bot.PID == gdb.PID {
            bots = append(bots, &gdb.Bots[i])
        }
    }
    return bots
}

// TheirBots returns a pointer array of GDBBots NOT owned by us.
func (gdb *GameDatabase) TheirBots() []*GDBBot {
    bots := make([]*GDBBot, 0)
    for i, bot := range gdb.Bots {
        if bot.PID != gdb.PID {
            bots = append(bots, &gdb.Bots[i])
        }
    }
    return bots
}

// Move returns a command struct for movement.
func (b *GDBBot) Move(x, y int) Command {
    cmd := Command{}
    cmd.Cmd = "MOVE"
    cmd.BID = b.BID
    cmd.X = x
    cmd.Y = y
    return cmd
}

// Follow is a convenience function which returns a
// command stuct for movement using a bot as a destination.
func (b *GDBBot) Follow(bot *GDBBot) Command {
    cmd := Command{}
    cmd.Cmd = "MOVE"
    cmd.BID = b.BID
    cmd.X = bot.X
    cmd.Y = bot.Y
    return cmd
}

// Target returns a command struct for targeting a bot.
func (b *GDBBot) Target(bot *GDBBot) Command {
    cmd := Command{}
    cmd.Cmd = "TARGET"
    cmd.BID = b.BID
    cmd.TPID = bot.PID
    cmd.TBID = bot.BID
    return cmd
}

// Power returns a command struct for seting the power of a bot.
func (b *GDBBot) Power(fire, move, shield int) Command {
    cmd := Command{}
    cmd.Cmd = "POWER"
    cmd.BID = b.BID
    cmd.FPow = fire
    cmd.MPow = move
    cmd.SPow = shield
    return cmd
}

////////////////////
// MISC FUNCTIONS //
////////////////////

// Send marshals a command to JSON and sends to the game.
func send(cmd Command) {
    bytes, err := json.Marshal(cmd)
    if err != nil {
        log.Fatalf("Failed to mashal command into JSON: %v\n", err)
    }
    bytes = append(bytes, []byte("\n")...)
    gameConn.Write(bytes)
}

// Distance calculates the distance between two points.
func distance(xa, ya, xb, yb int) float64 {
    xdist := float64(xb - xa)
    ydist := float64(yb - ya)
    return math.Sqrt(math.Pow(xdist, 2) + math.Pow(ydist, 2))
}

Chương trình này có đánh bại sự phục tùng cực đoan của tôi không?
pppery

Không có @ppperry, nó không. Đó là bia đỡ đạn, nhưng tôi đang làm việc trên một bot thứ hai.
Naribe

2

Kích hoạt hạnh phúc - Java 8

Trigger Happy là một sự phát triển đơn giản của bot Bombard ban đầu, nhưng không còn khả thi của tôi. Đây là một bot rất đơn giản sẽ bắn vào kẻ thù đang nhắm mục tiêu nếu có một phát bắn rõ ràng, nếu không thì thực hiện một bước đi ngẫu nhiên để cố gắng vào vị trí tốt hơn. Tất cả thời gian cố gắng để có một lá chắn.

Tuy nhiên, đối với tất cả sự đơn giản của nó, nó rất hiệu quả. Và sẽ dễ dàng phá hủy các bot mẫu.

Lưu ý, có nhiều lỗi với bot như đôi khi sẽ bắn ngay cả khi không bắn rõ ràng và có thể không duy trì lá chắn ... nhưng nó vẫn hiệu quả vì vậy sẽ chỉ gửi mục này như hiện trạng

món ăn chết chóc vs kích hoạt hạnh phúc

Death-Dish vs Trigger Happy

Mã như sau:

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

//import visual.Viewer;

public class TriggerHappy {

  static final int BOT_RADIUS = 30;
private static final double WALK_MAX_DIRECTION_CHANGE = Math.PI/3;
  static Bot targetedBot;

  enum BotState
  {
    INIT,
    RANDOM_WALK,
    FIRING,
    SHIELDING,
    DEAD,
    END
  }

  enum Power
  {
    MOVE,
    FIRE,
    SHIELD
  }


  private static PrintStream out;
private static List<Bot> enemyBots;
private static List<Bot> myBots;
//private static Viewer viewer;

  public static void main(String[] args) throws Exception
  {
    InetAddress localhost = Inet4Address.getLocalHost();
    Socket socket = new Socket(localhost, 50000);
    InputStream is = socket.getInputStream();
    out = new PrintStream(socket.getOutputStream());

    // read in the game configuration
    String line = readLine(is);
    Configuration config = new Configuration(line);
  //  viewer = new Viewer(line);

    myBots = config.bots.values().stream().filter(b->b.playerId==config.playerId).collect(Collectors.toList());
    enemyBots = config.bots.values().stream().filter(b->b.playerId!=config.playerId).collect(Collectors.toList());


    // set initial target
    targetedBot = enemyBots.get(enemyBots.size()/2);
    myBots.forEach(bot->target(bot,targetedBot));

    for (line = readLine(is);line!=null;line = readLine(is))
    {
//      viewer.update(line);
      // read in next bot update message from server
      Bot updatedBot = new Bot(line);
      Bot currentBot = config.bots.get(updatedBot.uniqueId);
      currentBot.update(updatedBot);

      // check for bot health
      if (currentBot.health<1)
      {
        // remove dead bots from lists
        currentBot.state=BotState.DEAD;
        if (currentBot.playerId == config.playerId)
        {
          myBots.remove(currentBot);
        }
        else
        {
          enemyBots.remove(currentBot);

          // change target if the targetted bot is dead
          if (currentBot == targetedBot)
          {
            if (enemyBots.size()>0)
            {
              targetedBot = enemyBots.get(enemyBots.size()/2);
              myBots.forEach(bot->target(bot,targetedBot));
            }
            // no more enemies... set bots to end state
            else
            {
              myBots.forEach(bot->bot.state = BotState.END);
            }
          }
        }
      }
      else
      {
          // ensure our first priority is shielding
          if (!currentBot.shield && currentBot.state!=BotState.SHIELDING)
          {
              currentBot.state=BotState.SHIELDING;
              shield(currentBot);
          }
          else
          {
              // not game end...
              if (currentBot.state != BotState.END)
              {
                // command to fire if we have a clear shot
                if (clearShot(currentBot))
                {
                    currentBot.state=BotState.FIRING;
                    fire(currentBot);
                }
                // randomly walk to try and get into a better position to fire
                else
                {
                    currentBot.state=BotState.RANDOM_WALK;
                    currentBot.dir+=Math.random()*WALK_MAX_DIRECTION_CHANGE - WALK_MAX_DIRECTION_CHANGE/2;
                    move(currentBot, (int)(currentBot.x+Math.cos(currentBot.dir)*100), (int) (currentBot.y+Math.sin(currentBot.dir)*100));
                }

              }
          }
      }
    }
    is.close();
    socket.close();
  }

// returns true if there are no friendly bots in firing line... mostly
private static boolean clearShot(Bot originBot)
{

    double originToTargetDistance = originBot.distanceFrom(targetedBot);
    for (Bot bot : myBots)
    {
        if (bot != originBot)
        {
            double x1 = originBot.x - bot.x;
            double x2 = targetedBot.x - bot.x;
            double y1 = originBot.y - bot.y;
            double y2 = targetedBot.y - bot.y;
            double dx = x2-x1;
            double dy = y2-y1;
            double dsquared = dx*dx + dy*dy;
            double D = x1*y2 - x2*y1;
            if (1.5*BOT_RADIUS * 1.5*BOT_RADIUS * dsquared > D * D && bot.distanceFrom(targetedBot) < originToTargetDistance)
            {
                return false;
            }
        }
    }

    return true;

}


  static class Bot
  {
    int playerId;
    int botId;
    int x;
    int y;
    int health;
    boolean fired;
    int hitX;
    int hitY;
    double dir = Math.PI*2*Math.random();
    boolean shield;
    int uniqueId;
    BotState state = BotState.INIT;
    Power power = Power.SHIELD;


    Bot(String line)
    {
      String[] tokens = line.split(",");
      playerId = extractInt(tokens[1]);
      botId = extractInt(tokens[2]);
      x = extractInt(tokens[3]);
      y = extractInt(tokens[4]);
      health = extractInt(tokens[5]);
      fired = extractBoolean(tokens[6]);
      hitX = extractInt(tokens[7]);
      hitY = extractInt(tokens[8]);
      shield = extractBoolean(tokens[10]);
      uniqueId = playerId*10000+botId;
    }

    Bot()
    {
    }

    double distanceFrom(Bot other)
    {
        return distanceFrom(new Point(other.x,other.y));
    }

    double distanceFrom(Point other)
    {
        double deltaX = x - other.x;
        double deltaY = y - other.y;
        return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    }

    void update(Bot other)
    {
      x = other.x;
      y = other.y;
      health = other.health;
      fired = other.fired;
      hitX = other.hitX;
      hitY = other.hitY;
      shield = other.shield;
    }
  }

  static class Configuration
  {
    BotState groupState = BotState.INIT;
    HashMap<Integer,Bot> bots = new HashMap<>();
    boolean isOpponentInitiated;
    int playerId;

    Configuration(String line) throws Exception
    {
      String[] tokens = line.split("\\[");
      playerId = extractInt(tokens[0].split(",")[1]);

      for (String token : tokens[1].split("\\|"))
      {
        Bot bot = new Bot(token);
        bots.put(bot.uniqueId,bot);
      }
    }
  }

  /**
   * Reads a line of text from the input stream. Blocks until a new line character is read.
   * NOTE: This method should be used in favor of BufferedReader.readLine(...) as BufferedReader buffers data before performing
   * text line tokenization. This means that BufferedReader.readLine() will block until many game frames have been received. 
   * @param in a InputStream, nominally System.in
   * @return a line of text or null if end of stream.
   * @throws IOException
   */
  static String readLine(InputStream in) throws IOException
  {
     StringBuilder sb = new StringBuilder();
     int readByte = in.read();
     while (readByte>-1 && readByte!= '\n')
     {
        sb.append((char) readByte);
        readByte = in.read();
     }
     return readByte==-1?null:sb.toString().replace(",{", "|").replaceAll("}", "");

  }

  final static class Point
  {
    public Point(int x2, int y2) {
        x=x2;
        y=y2;
    }
    int x;
    int y;
  }

  public static int extractInt(String token)
  {
    return Integer.parseInt(token.split(":")[1]);
  }

  public static boolean extractBoolean(String token)
  {
    return Boolean.parseBoolean(token.split(":")[1]);
  }

  static void distributePower(Bot bot, int fire, int move, int shield)
  {
    out.println("{\"Cmd\":\"POWER\",\"BID\":"+bot.botId+",\"FPow\":"+fire+",\"MPow\":"+move+",\"SPow\":"+shield+"}");
//  viewer.distributePower(bot.botId, fire, move, shield);
  }

  static void shield(Bot bot)
  {
    distributePower(bot,0,0,12);
    bot.power=Power.SHIELD;
  }

  static void move(Bot bot, int x, int y)
  {
    distributePower(bot,0,12,0);
    out.println("{\"Cmd\":\"MOVE\",\"BID\":"+bot.botId+",\"X\":"+x+",\"Y\":"+y+"}");
  }
  static void target(Bot bot, Bot target)
  {
    out.println("{\"Cmd\":\"TARGET\",\"BID\":"+bot.botId+",\"TPID\":"+target.playerId+",\"TBID\":"+target.botId+"}");
  }

  static void fire(Bot bot)
  {
    distributePower(bot,12,0,0);
    bot.power=Power.FIRE;
  }
}

Để biên dịch: javac TriggerHappy.java

Để chạy: java TriggerHappy

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.