Scrappers v0.1: Programmer Mercenary

22

Dalam dunia yang sunyi dan dilanda perang, di mana kota-kota dikuasai oleh penjahat dan pencuri, peradaban telah mengubah dirinya dalam bentuk koperasi industri kecil, terisolasi, yang tersebar di seluruh lanskap yang sebelumnya tidak berpenghuni. Keberadaan komunitas-komunitas ini bergantung pada tim pekerja bayaran yang disebut "scrappers", yang mencari wilayah yang tak ternoda untuk mencari bahan-bahan berharga untuk dijual ke kandang. Karena bahan-bahan ini telah tumbuh semakin langka, membuang menjadi profesi yang semakin sulit dan berbahaya. Pekerja manusia yang rapuh sebagian besar telah diganti dengan stand-in robot jarak jauh, yang disebut "bot", dan tentara bayaran yang khas lebih cenderung menjadi programmer yang terampil daripada tukang las bersenjata. Karena kehadiran manusia dalam scrapping telah menurun, demikian juga rasa saling menghormati antara kelompok tentara bayaran satu sama lain. Bot dilengkapi tidak hanya untuk mengumpulkan memo, tetapi untuk mempertahankannya, dan dalam beberapa kasus mengambilnya dengan paksa. Programmer bot bekerja tanpa lelah merancang strategi baru untuk mengakali scrappers saingan, menghasilkan bot yang semakin agresif, dan bahaya lain bagi manusia yang menjelajah di luar tembok komunitas mereka.

Sampel Game Scrappers

(Ya, logo dipangkas dengan lucu)

Selamat datang di Scrappers!

Ini adalah versi awal Scrappers di mana pengumpulan memo dan pabrik belum diimplementasikan. Ini pada dasarnya adalah "tembak mereka".

Anda adalah seorang programmer tentara bayaran yang bertugas membuat program untuk melakukan bot Anda dari jarak jauh untuk kemenangan atas kelompok-kelompok penembak saingan. Bot Anda adalah mesin mirip laba-laba yang terdiri dari generator listrik dan pelindung di intinya, dikelilingi oleh banyak pelengkap yang dilengkapi dengan alat mencengkeram, memotong, dan menyerang. Generator listrik mampu menghasilkan 12 unit daya (pu) per tick (unit waktu pencakar). Anda mengendalikan bagaimana kekuatan ini didistribusikan di antara tiga kebutuhan utama bot: gerakan, perisai, dan daya tembak.

Bot Scrapper adalah mesin yang sangat gesit, dan dapat dengan mudah bergerak, di bawah, dan di sekitar rintangan yang mereka temui. Jadi, tabrakan bukanlah sesuatu yang perlu dipertimbangkan oleh program Anda. Anda bebas mengalokasikan semua, sebagian, atau tidak ada 12pu yang tersedia untuk bot Anda untuk dipindahkan, selama Anda berurusan dengan angka keseluruhan. Mengalokasikan 0pu ke fungsi pergerakan bot akan membuatnya tidak bergerak. Mengalokasikan 2pu akan memungkinkan bot untuk memindahkan 2 unit jarak (du) per tick, 5pu akan menghasilkan 5du / tick, 11pu akan menghasilkan 11du / tick, dan seterusnya.

Generator pelindung bot Anda memproyeksikan gelembung energi deflektif di sekitar tubuh mereka. Sebuah perisai dapat membelokkan hingga 1 kerusakan sebelum muncul, sehingga meninggalkan bot Anda terbuka sampai generator perisai itu membangun kekuatan yang cukup untuk memasang kembali perisai ke tempatnya. Anda bebas mengalokasikan semua, sebagian, atau tidak ada 12pu yang tersedia untuk bot Anda ke arah perisai itu. Mengalokasikan 0pu ke tameng bot berarti ia tidak akan pernah menghasilkan tameng. Mengalokasikan 2pu akan memungkinkan bot untuk menghasilkan perisai baru 2 dari 12 kutu, atau sekali setiap 6 kutu. 5pu akan menghasilkan regenerasi perisai 5 dari setiap 12 kutu, dan seterusnya.

Dengan mengisi muatan pada laser pengelasan mereka, bot Anda dapat menembakkan sinar yang merusak jarak pendek dengan akurasi yang wajar. Seperti pembuatan perisai, kecepatan tembakan bot Anda tergantung pada daya yang dialokasikan untuk laser mereka. Mengalokasikan 0pu ke laser bot berarti ia tidak akan pernah menyala. Mengalokasikan 2pu akan memungkinkan bot untuk menembakkan 2 dari setiap 12 ticks, dan seterusnya. Laser bot akan melakukan perjalanan sampai bertemu dengan suatu benda atau menyebar menjadi tidak berguna, jadi berhati-hatilah dengan api yang bersahabat. Meskipun bot Anda cukup akurat, mereka tidak sempurna. Anda harus mengharapkan +/- 2.5 derajat variasi dalam akurasi. Saat sinar laser bergerak, partikel-partikelnya secara bertahap dibelokkan oleh atmosfer sampai sinar itu menjadi tidak berbahaya secara efektif dengan jarak yang cukup. Laser tidak merusak 1 pada jarak titik kosong, dan 2,5% lebih sedikit merusak setiap bot panjang yang dilalui.

Scrapper bot cukup otonom untuk menangani fungsi-fungsi dasar, tetapi bergantung pada Anda, programmer mereka, untuk menjadikannya berguna sebagai grup. Sebagai seorang programmer, Anda dapat mengeluarkan perintah berikut untuk setiap bot individu:

  • MOVE: Tentukan koordinat ke arah mana bot akan bergerak.
  • TARGET: Identifikasi bot untuk diarahkan dan menembak ketika alokasi daya memungkinkan.
  • KEKUATAN: Mendistribusikan kembali kekuatan antara gerakan, perisai, dan daya tembak.

Detail Game Teknis

Ada tiga program yang perlu Anda ketahui. The Game Engine adalah pengangkat berat dan menyediakan TCP API bahwa program pemutar terhubung ke. Program pemain adalah apa yang akan Anda tulis, dan saya telah memberikan beberapa contoh dengan binari di sini . Akhirnya, Renderer memproses output dari Game Engine untuk menghasilkan GIF pertempuran.

Mesin Game

Anda dapat mengunduh mesin gim dari sini . Ketika game diluncurkan, ia akan mulai mendengarkan pada port 50000 (saat ini tidak dapat dikonfigurasi) untuk koneksi pemain. Setelah menerima dua koneksi pemain, ia mengirimkan pesan SIAP ke para pemain dan memulai permainan. Program pemain mengirim perintah ke permainan melalui TCP API. Ketika game selesai, file JSON bernama scrappers.json (juga, saat ini tidak dapat dikonfigurasi) dibuat. Inilah yang digunakan renderer untuk membuat GIF game.

API TCP

Program pemain dan mesin gim berkomunikasi dengan mengirimkan string JSON yang diakhiri oleh baris baru kembali dan keempat melalui koneksi TCP. Hanya ada lima pesan JSON berbeda yang dapat dikirim atau diterima.

Pesan Siap

Pesan READY dikirim dari game ke program pemain dan hanya dikirim sekali. Pesan ini memberitahu program pemain apa ID pemainnya (PID) dan menyediakan daftar semua bot dalam permainan. PID adalah satu-satunya cara untuk menentukan Bot mana yang ramah lawan musuh. Info lebih lanjut tentang bidang bot di bawah ini.

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

Pesan Bot

Pesan BOT dikirim dari game ke program pemain dan dikirim ketika atribut bot berubah. Misalnya, ketika perisai diproyeksikan, atau perubahan kesehatan, pesan BOT dikirim. ID Bot (BID) hanya unik di dalam pemain tertentu.

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

Pindahkan Pesan

Pesan MOVE adalah perintah dari program pemain ke permainan (tetapi anggap itu sebagai perintah untuk bot). Cukup mengidentifikasi bot yang ingin Anda pindahkan, dan koordinatnya. Diasumsikan bahwa Anda memerintahkan bot Anda sendiri, jadi tidak perlu PID.

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

Pesan Target

Pesan TARGET memberitahu salah satu bot Anda untuk menargetkan beberapa bot lainnya.

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

Pesan Kekuatan

Pesan POWER mengalokasikan kembali 12pu yang tersedia untuk bot Anda di antara gerakan, daya tembak, dan perisai.

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

Sebuah kompetisi

Jika Anda cukup berani untuk menjelajahi tanah yang tidak dijinakkan, Anda akan dimasukkan ke dalam turnamen eliminasi ganda melawan rekan-rekan tentara bayaran Anda. Harap buat jawaban untuk kiriman Anda dan rekatkan kode Anda atau berikan tautan ke git repo, intisari, dll. Bahasa apa pun dapat diterima, tetapi Anda harus menganggap saya tidak tahu apa-apa tentang bahasa tersebut dan memasukkan instruksi untuk menjalankan program Anda. Buat kiriman sebanyak yang Anda suka, dan pastikan untuk memberi mereka nama!

Program sampel pemain akan dimasukkan dalam turnamen, jadi saya sangat merekomendasikan menguji bot Anda terhadap mereka. Turnamen akan dimulai kira-kira dua minggu setelah kami mendapatkan empat pengajuan program yang unik. Semoga berhasil!

--- Winner's Bracket ---

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

Info Penting Lainnya

  • Gim ini berjalan pada 12 ticks / detik, sehingga Anda tidak akan menerima pesan lebih sering daripada setiap 83 milidetik atau lebih.
  • Setiap bot berdiameter 60du. Perisai tidak membutuhkan ruang tambahan. Dengan akurasi +/- 2.5%, peluang untuk memukul bot pada jarak tertentu diwakili oleh grafik ini:

grafik akurasi

  • Kerusakan kerusakan laser pada jarak diwakili oleh grafik ini:

grafik kerusakan kerusakan

  • Akurasi bot dan peluruhan laser bergabung untuk menghitung rata-rata kerusakan per tembakan. Yaitu, kerusakan rata-rata yang akan ditimbulkan bot saat ditembak dari jarak tertentu. Kerusakan per tembakan diwakili oleh grafik ini:

kerusakan per grafik tembakan

  • Laser bot berasal setengah jalan antara pusat bot dan ujungnya. Dengan demikian, menumpuk bot Anda akan menghasilkan api ramah.
  • Bot musuh muncul sekitar 1440du terpisah.
  • Permainan berakhir jika 120 kutu (10 detik) berlalu tanpa ada kerusakan yang diberikan.
  • Pemenangnya adalah pemain dengan bot terbanyak, kemudian paling sehat saat pertandingan berakhir.

Memahami Gambar yang Diubah

  • Player 1 diwakili oleh lingkaran dan pemain 2 oleh segi enam.
  • Warna bot mewakili alokasi daya. Lebih merah berarti lebih banyak daya telah dialokasikan untuk menembak. Lebih banyak biru berarti lebih banyak perisai. Lebih hijau berarti lebih banyak gerakan.
  • "Lubang" di tubuh bot menunjukkan kerusakan. Semakin besar lubangnya, semakin banyak kerusakan yang terjadi.
  • Lingkaran putih yang mengelilingi bot adalah tamengnya. Jika sebuah bot memiliki pelindung di ujung belokan, itu ditunjukkan. Jika perisai muncul dengan mengambil kerusakan, itu tidak ditampilkan.
  • Garis merah antara bot mewakili tembakan yang diambil.
  • Ketika bot terbunuh, "ledakan" merah besar akan ditampilkan.
Rip Leeb
sumber
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Dennis

Jawaban:

4

Ekstremis (Python 3)

Bot ini akan selalu mencurahkan seluruh kekuatannya untuk satu hal: melindungi jika tidak dilindungi, bergerak jika berada di luar posisi, dan menembak sebaliknya. Mengalahkan semua bot sampel kecuali piring kematian.

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
pppery
sumber
Saya tidak terbiasa dengan python, tetapi tampaknya ada beberapa masalah dengan kiriman Anda: 1) baris 212 120 tidak indentasi dengan benar dan 2) target_hp tidak didefinisikan. Saya dapat memperbaiki (1) tetapi (2) menghentikan saya menjalankan kiriman Anda. Tapi bisa jadi saya kurang berpengalaman dengan python.
Moogie
Seluruh pernyataan jika itu adalah sisa dari beberapa debugging dan tidak perlu sama sekali
pppery
2

Kurang sembarangan ( Go )

go run main.go

Awalnya, saya berencana untuk sedikit memodifikasi program sampel Reckless Abaikan sehingga bot tidak akan menyala jika bot ramah menghalangi. Saya berakhir dengan bot yang memilih target baru ketika seorang teman berada di jalan, yang saya kira lebih baik. Itu akan mengalahkan dua program pertama.

Kode tidak sempurna. Logika untuk menentukan apakah bidikan jelas menggunakan beberapa tebakan acak yang cukup.

Sepertinya tidak ada mekanisme untuk menargetkan "tidak ada". Itu mungkin fitur yang bagus untuk ditambahkan.

TCP API bagus karena bahasa apa pun bisa dimainkan, tetapi itu juga berarti banyak kode boilerplate. Jika saya tidak terbiasa dengan bahasa sampel bot ditulis, saya mungkin tidak akan termotivasi untuk bermain-main dengan ini. Kumpulan sampel boilerplate dalam berbagai bahasa akan menjadi tambahan yang bagus untuk repositori git Anda yang lain.

(sebagian besar kode berikut adalah salin / tempel dari salah satu bot sampel)

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))
}
Naribe
sumber
Apakah program ini mengalahkan pengajuan ekstrimis saya?
pppery
Tidak, @perperry, tidak. Ini umpan meriam, tapi aku sedang mengerjakan bot kedua.
Naribe
2

Trigger Happy - Java 8

Trigger Happy adalah evolusi sederhana dari botard asli saya, tetapi tidak lagi layak. Ini adalah bot yang sangat sederhana yang hanya akan menembak musuh yang saat ini ditargetkan jika ada tembakan yang jelas, jika tidak melakukan perjalanan acak untuk mencoba masuk ke posisi yang lebih baik. Setiap saat berusaha memiliki perisai.

Namun, untuk semua kesederhanaannya sangat efektif. Dan akan dengan mudah menghancurkan bot sampel.

Catatan, ada beberapa bug dengan bot seperti kadang-kadang akan menyala meskipun bukan tembakan yang jelas dan mungkin tidak mempertahankan perisai ... tetapi masih efektif sehingga hanya akan mengirimkan entri ini apa adanya

piring kematian vs pemicu bahagia

Death-dish vs Trigger Happy

Kode sebagai berikut:

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

Untuk mengkompilasi: javac TriggerHappy.java

Untuk menjalankan: java TriggerHappy

Moogie
sumber