Sıyırıcılar v0.1: Paralı Programcılar


22

Şehirlerin haydutlar ve hırsızlar tarafından istila edildiği ıssız, savaşın yıkıldığı bir dünyada, medeniyet, daha önce yaşanmamış manzaraya dağılmış, küçük, izole edilmiş, endüstriyel kooperatifler biçiminde kendini yeniden keşfetti. Bu toplulukların varlığı, toplanmamış toprakları kümeslere satmak için değerli materyaller arayan “sıyırıcılar” olarak adlandırılan paralı işçi ekiplerine dayanıyor. Bu malzemeler daha az büyüdükçe, hurdaya çıkarmak çok daha zor ve tehlikeli bir mesleğe dönüştü. Kırılgan insan işçilerin çoğu “bot” olarak adlandırılan uzak robot standları ile değiştirildi ve tipik bir paralı askerin silahlı bir kaynakçıdan daha yetenekli bir programcı olması daha muhtemel. Hurdalıktaki insan varlığı azaldıkça paralı gruplar arasında birbirlerine saygı duyuluyor. Robotlar sadece hurda toplamak için değil aynı zamanda onu savunmak ve bazı durumlarda zorla almak için donatılıyorlar. Bot programcıları yorucu bir şekilde rakip sıyırıcıları yenmek için yeni stratejiler geliştirmeye çalışmakta, bu da giderek daha agresif botlar oluşmasına neden olmakta ve ayrıca topluluklarının duvarlarının dışına çıkan insanlar için başka bir tehlike oluşturmaktadır.

Sıyırıcılar oyunu örnek

(evet, logo çok komik bir şekilde kırpılıyor)

Sıyırıcılara Hoşgeldiniz!

Bu, hurda toplama ve fabrikaların uygulanmadığı bir sıyırıcıdır. Temelde bir “onları vur”.

Botlarınızı rakip sıyırıcı grupları karşısında zafere taşıyacak bir program oluşturmakla görevli bir paralı asker programcısınız. Botlarınız, çekirdeğinde güç ve kalkan jeneratörlerinden oluşan, kavrama, kesme ve saldıran aletler ile donatılmış pek çok ek parça ile çevrili, örümcek benzeri makinelerdir. Jeneratör, kene başına 12 güç ünitesi (pu) üretebilir (bir hurdacının zaman birimi). Bu gücün bir botun üç temel ihtiyacına nasıl dağıldığını kontrol ediyorsun: Hareket, kalkanlar ve ateş gücü.

Sıyırıcı botları son derece çevik makinelerdir ve karşılaştıkları engellerin üzerinden, altından ve çevresinden kolayca geçebilirler. Dolayısıyla çarpışma, programınızın dikkate alması gereken bir şey değildir. Tam sayılarda işlem yaptığınız sürece, botunuza kullanabileceğiniz 12pu'nun tümünü veya bir kısmını veya hiçbirini tahsis etmekte özgürsünüz. 0pu bir botun hareket fonksiyonlarına atamak onu hareketsiz kılar. 2pu tahsis edilmesi, bir botun kene başına 2 mesafe birimini (du) hareket ettirmesine, 5pu 5du / tick ile sonuçlanmasına, 11pu, 11du / tick ile sonuçlanmasına neden olacaktı.

Botlarınızın kalkan jeneratörleri, vücutlarının etrafında bir deflektif enerji balonu yansıtıyor. Bir kalkan, fırlamadan önce 1 hasara kadar sapma gösterebilir, bu nedenle botu kalkan jeneratörü tekrar yerine oturtmaya yetecek güç gelene kadar açığa çıkarır. Botunuz için mevcut olan 12pu’nın tümünü, bir kısmını veya hiçbirini kalkanına ayırmakta özgürsünüz. 0pu'nun bot kalkanına tahsis edilmesi, asla kalkan oluşturmayacağı anlamına gelir. 2pu tahsis edilmesi, bir botun 12 tikten 2 veya her 6 tikten sonra yeni bir kalkan oluşturmasını sağlayacaktır. 5pu, her 12 kenenin 5'inde kalkan rejenerasyonu ile sonuçlanacaktır.

Kaynak lazerlerinde bir şarj oluşturarak, botlarınız kısa mesafelerdeki hasarlı kirişleri adil bir hassasiyetle ateşleyebilir. Kalkan üretimi gibi, botlarınızın atış hızı da, lazerlerine tahsis edilen güce bağlıdır. 0pu bir botun lazerlerine tahsis etmek asla ateş etmeyeceği anlamına gelir. 2pu tahsis etmek, bir botun her 12 tikten 2'sini atmasına vb. Neden olur. Bir botun lazeri, bir nesneyle karşılaşıncaya ya da işe yaramazlığa dağılıncaya kadar seyahat eder, bu yüzden dost ateşi konusunda dikkatli olun. Botlarınız oldukça doğru olmasına rağmen mükemmel değiller. Doğrulukta +/- 2,5 derece değişkenlik beklemelisiniz. Lazer ışını hareket ettikçe partikülleri, ışın yeterli mesafeyle etkili bir şekilde zararsız hale gelinceye kadar atmosfer tarafından kademeli olarak saptırılır. Bir lazer, nokta boşluğunda 1 hasar verir ve seyahat ettiği her bot uzunluğunda% 2,5 daha az hasar verir.

Kazıyıcı botları, temel işlevleri yerine getirecek kadar özerktir, ancak bunları bir grup olarak kullanabilmeniz için size, programcılarına güvenir. Bir programcı olarak, her bir bot için aşağıdaki komutları verebilirsiniz:

  • MOVE: Bir botun hareket edeceği koordinatları belirleyin.
  • HEDEF: Amaçlanan ve bir güç tahsis edildiğinde ateş etmek için bir bot tanımlayın.
  • POWER: Hareket, kalkanlar ve ateş gücü arasındaki gücü yeniden dağıtın.

Teknik Oyun Detayları

Aşina olmanız gereken üç program var. Oyun Motoru ağır kaldırıcı ve oyuncu programları bağlanmak olduğunu TCP API sağlar. Oynatıcı program yazacağınız şey ve burada ikililere bazı örnekler verdim . Son olarak, Renderer , Game Engine'deki çıktısını, bir GIF savaşı üretmek üzere işler.

Oyun Motoru

Oyun motorunu buradan indirebilirsiniz . Oyun başlatıldığında, oyuncu bağlantıları için 50000 numaralı bağlantı noktasını (şu anda yapılandırılamaz) dinlemeye başlayacak. İki oyuncu bağlantısı aldığında, HAZIR mesajını oyunculara gönderir ve oyuna başlar. Oyuncu programları oyuna komutları TCP API üzerinden gönderir. Oyun bittiğinde, scrappers.json (ayrıca şu anda yapılandırılamaz) adlı bir JSON dosyası oluşturulur. Bu, işleyicinin oyunun bir GIF'sini oluşturmak için kullandığı şeydir.

TCP API

Oyuncu programları ve oyun motoru, yeni bir sonlandırılmış JSON dizgisini bir TCP bağlantısı üzerinden dördüncü ve sonrasına ileterek iletişim kurar. Gönderilebilen veya alınabilen yalnızca beş farklı JSON mesajı vardır.

Hazır mesaj

HAZIR mesajı oyundan oyuncu programlarına gönderilir ve yalnızca bir kez gönderilir. Bu mesaj, oyuncu programına, oyuncu kimliğinin (PID) ne olduğunu söyler ve oyundaki tüm botların bir listesini sunar. PID, hangi Botların düşmana karşı dost olduğunu belirlemenin tek yoludur. Aşağıdaki bot alanları hakkında daha fazla bilgi.

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

Bot Mesajı

YİD mesajı oyundan oyuncu programlarına gönderilir ve bir botun özellikleri değiştiğinde gönderilir. Örneğin, kalkanlar yansıtıldığında veya sağlık değiştiğinde, bir YİD mesajı gönderilir. Bot Kimliği (BID) yalnızca belirli bir oynatıcıda benzersizdir.

{
    "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.
}

İletiyi Taşı

MOVE mesajı, oynatıcı programından oyuna bir komuttur (ancak bir botun komutu olarak düşünün). Basitçe taşımak istediğiniz botu ve koordinatları tanımlayın. Kendi botunuzu kontrol ettiğiniz varsayılmaktadır, dolayısıyla PID gerekli değildir.

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

Hedef Mesaj

HEDEF mesajı botlarınızdan birine diğer botları hedeflemesini söyler.

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

Güç mesajı

POWER mesajı botunuz için mevcut olan 12pu'yu hareket, ateş gücü ve kalkanlar arasında yeniden tahsis eder.

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

Rekabet

İşlenmemiş toprakları keşfetmek için yeterince cesursanız, paralı akranlarınıza karşı iki eleme turnuvasına girersiniz. Lütfen gönderiminiz için bir cevap oluşturun ve kodunuzu yapıştırın veya git repo, öz vb. İçin bir bağlantı verin. Herhangi bir dil kabul edilebilir, ancak dil hakkında hiçbir şey bilmediğimi ve programınızı çalıştırma talimatlarını içerdiğimi varsaymalısınız. İstediğiniz kadar gönderi oluşturun ve onlara ad verdiğinizden emin olun!

Numune oynatıcı programlar ederim onlara karşı bot test tavsiye böylece, turnuvaya dahil edilecektir. Dört benzersiz program başvurusu aldıktan sonra turnuva yaklaşık iki hafta başlayacak. İyi şanslar!

--- Winner's Bracket ---

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

Diğer Önemli Bilgiler

  • Oyun 12 tik / saniyede oynanır, bu yüzden her 83 milisaniyeden daha fazla mesaj almayacaksınız.
  • Her botun çapı 60du'dur. Kalkan fazladan yer kaplamaz. +/-% 2.5 doğrulukla, bir botu belli bir mesafeye çarpma ihtimali bu grafikte gösterilmiştir:

doğruluk grafiği

  • Lazer hasarının mesafe üzerinden düşmesi bu grafikte gösterilir:

hasar bozunum grafiği

  • Bir botun doğruluğu ve lazer çürümesi, atış başına ortalama hasarını hesaplamak için birleşir. Yani, bir botun belli bir mesafeden ateşlendiğinde neden olacağı ortalama hasar. Atış başına hasar bu grafikte gösterilir:

atış grafiği başına hasar

  • Bir botun lazeri, botun merkezi ile kenarı arasında yarı yolda. Böylece botlarınızı istiflemek dost ateşi ile sonuçlanacaktır.
  • Düşman botları kabaca 1440du aralıklarla ortaya çıkar.
  • 120 tik (10 saniye) herhangi bir hasar vermeden geçerse oyun sona erer.
  • Kazanan en fazla botu olan oyuncu, daha sonra oyun bittiğinde en sağlık olan oyuncu.

Render İmajını Anlamak

  • Oyuncu 1 daireler tarafından temsil edilir ve oyuncu 2 altıgenlerle gösterilir.
  • Bir botun rengi güç dağılımını temsil eder. Daha fazla kırmızı, ateş etmeye daha fazla güç tahsis edildiği anlamına gelir. Daha fazla mavi daha fazla kalkan anlamına gelir. Daha fazla yeşil, daha fazla hareket demektir.
  • Bir botun gövdesindeki "delik" hasarı temsil eder. Delik ne kadar büyük olursa, o kadar fazla hasar alınmıştır.
  • Bir botu çevreleyen beyaz daireler kalkandır. Bir botun dönüşün sonunda bir kalkanı varsa, gösterilir. Kalkan hasar alarak atıldıysa, gösterilmez.
  • Botlar arasındaki kırmızı çizgiler, çekilen çekimleri temsil eder.
  • Bir bot öldürüldüğünde, büyük bir kırmızı "patlama" gösterilir.

Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
Dennis,

Yanıtlar:


4

Aşırılıkçı (Python 3)

Bu bot her zaman tüm gücünü bir şeye adayacaktır: korumalı değilse koruma, pozisyon dışıysa hareket etme ve başka şekilde ateş etme. Ölüm yemeği hariç tüm örnek botları yener.

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

Python'u tanımıyorum, ancak gönderiminizde birden fazla sorun var gibi görünüyor: 1) satırlar 212 120 doğru girintili değil ve 2) target_hp tanımlanmamış. (1) 'i düzeltebilirim ama (2) başvurunuzu yapmamı engelliyor Ama bu benim python ile olan deneyim eksikliğim olabilir.
Moogie

If if ifadesinin tümü bir hata ayıklama
işleminden kalmıştı

2

Daha az dikkatsiz ( git )

go run main.go

Başlangıçta, Reckless Abandon örnek programını biraz değiştirmeyi planlamıştım, böylece dostça bir bot yolda olsaydı botlar patlamazdı. Bir arkadaş yoldayken yeni hedefler seçen botlar ile bitirdim, sanırım daha iyi. İlk iki programı yenecek.

Kod mükemmel değil. Bir atış net olup olmadığını belirlemek için mantık bazı oldukça rastgele tahmin kullanır.

"Hiç kimseyi" hedef alan bir mekanizma yok gibi görünüyor. Eklemek için iyi bir özellik olabilir.

TCP API, herhangi bir dilin oynayabileceği hoş bir programdır ancak aynı zamanda birçok kod kodu anlamına da gelir. Örnek botların yazıldığı dile aşina olmasaydım, muhtemelen bununla oynamak için motive olmazdım. Çeşitli dillerdeki kazan plakası örneklerinin toplanması, diğer git repolarınıza harika bir katkı olacaktır.

(aşağıdaki kodun çoğu örnek botlardan birinden kopyala / yapıştır)

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))
}

Bu program benim ekstremist sunumumu yeniyor mu?
pppery

Hayır @ pperry, öyle değil. Bu top yem, ama ben ikinci bir bot üzerinde çalışıyorum.
Naribe

2

Mutlu Tetikleyici - Java 8

Trigger Happy, orjinalimin basit bir evrimi, ancak artık geçerli değil Bombard bot. Net bir atış olursa, şu anda hedeflenen düşmana ateş edecek basit bir bot, aksi halde daha iyi bir pozisyona geçmek için rastgele bir yürüyüş yapar. Her zaman bir kalkan olmaya çalışıyor.

Ancak, tüm sadeliği için çok etkilidir. Ve kolayca örnek botları yok edecek.

Dikkat edin, bot ile net bir çekim olmasa bile bazen ateş açacak ve kalkan kalmayacak gibi birden fazla hata var ... ama yine de etkilidir, bu yüzden bu girişi olduğu gibi gönderir.

ölüm yemeği vs mutlu tetik

Ölüm yemeği vs Trigger Happy

Aşağıdaki gibi kodlayın:

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;
  }
}

Derlemek için: javac TriggerHappy.java

Çalıştırmak için: Java TriggerHappy

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.