Snowball Fight KoTH!


35

Hasil (22 Mei 2017 21:40:37 UTC)

Mastermemenangkan 18 putaran, kalah 2 putaran, dan diikat 0 putaran
Save Onememenangkan 15 putaran, kalah 3 putaran, dan diikat 2 putaran
Machine Gunmemenangkan 14 putaran, kalah 3 putaran, dan diikat 3 putaran
Monte Botmemenangkan 14 putaran, kalah 3 putaran, dan diikat 3 putaran
Amb Botmenang 12 putaran, kalah 8 putaran, dan diikat 0 putaran
Cowardmemenangkan 11 putaran, kalah 3 putaran, dan diikat 6 putaran
Pain in the Nashmemenangkan 11 putaran, kalah 9 putaran, dan diikat 0 putaran
Nece Botmemenangkan 10 putaran, kalah 7 putaran, dan diikat 3 putaran
Naming Things is Hardmemenangkan 10 putaran, kalah 7 putaran, dan diikat 3 putaran
The Procrastinatormemenangkan 10 putaran, kalah 8 putaran, dan diikat 2 putaran
Yggdrasilmemenangkan 10 putaran, kalah 10 putaran, dan diikat 0 putaran
Simple Botmemenangkan 9 putaran, kalah 4 putaran, dan diikat 7 putaran
Table Botmemenangkan 9 putaran, kalah 6 putaran, dan diikat 5 putaran
Prioritized Random Botmemenangkan 8 putaran, kalah 7 putaran, dan diikat 5 putaran
Upper Hand Botmemenangkan 7 putaran, kalah 13 putaran, dan diikat 0 putaran
Aggressormemenangkan 6 putaran, kalah 10 putaran, dan diikat 4 putaran
Insanememenangkan 5 putaran, kalah 15 putaran, dan diikat 0 putaran
The Ugly Ducklingmemenangkan 4 putaran, kalah 16 putaran, dan diikat 0 putaran
Know Botmenang 3 putaran, kalah 14 putaran, dan diikat 3 putaran
Paranoid Botmemenangkan 0 putaran, kalah 19 putaran, dan diikat 1 putaran
Panic Botmenang 0 putaran, kalah 19 putaran, dan diikat 1 putaran

Sayangnya saya tidak bisa menguji The Crazy X-Code Randomess karena saya tidak bisa menjalankannya dari bash di Linux. Saya akan memasukkannya jika saya bisa membuatnya bekerja.

Output Pengendali Penuh


Permainan

Ini adalah game KoTH yang sangat sederhana. Ini pertarungan bola salju satu lawan satu. Anda memiliki wadah yang awalnya kosong yang dapat menampung kbola salju. Anda dapat meringkuk hingga jwaktu. Setiap belokan, kedua pemain diminta untuk secara bersamaan memberikan pilihan untuk gerakan apa yang harus dilakukan. Ada tiga langkah:

  • ulang: memberi Anda bola salju lain (hingga k)
  • throw: melempar bola salju, yang akan membunuh pemain lain jika mereka memutuskan untuk memuat ulang. Jika kedua pemain melempar bola salju, tidak ada yang mati (mereka memiliki tujuan yang sangat baik sehingga mereka akan saling memukul bola salju satu sama lain)
  • bebek: tidak melakukan apa-apa, dan menghindari pukulan jika pemain lain melempar bola salju. Jika Anda tidak memiliki bebek yang tersisa, maka tidak ada yang terjadi dan jika pemain lain melempar bola salju, Anda akan mati.

Objektif

Jangan mati.

Spesifikasi Challlenge

Program Anda dapat ditulis dalam bahasa apa pun. Anda harus mengambil masing-masing variabel ini sebagai argumen pada setiap eksekusi:

[turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs]

turn- Berapa banyak putaran telah berlalu ( 0pada iterasi pertama)
snowballs- Berapa banyak bola salju yang Anda miliki
opponent_snowballs- Berapa banyak bola salju yang dimiliki lawan
ducks- Berapa kali Anda bisa bebek
opponent_ducks- Berapa kali lawan dapat merunduk
max_snowballs- Jumlah maksimum bola salju yang Anda bisa toko ( k)

Output fungsi kunci harus 0untuk memuat ulang, 1untuk melempar, dan 2untuk bebek. Anda harus menampilkan gerakan Anda, baris baru dihentikan. Tolong jangan membuat output gerakan yang tidak valid, tetapi pengontrolnya sangat ulet dan tidak akan pecah jika Anda menghasilkan gerakan yang tidak valid (bahkan jika gerakan Anda bahkan bukan bilangan bulat). Itu harus diakhiri baris baru. Jika pemindahan itu tidak ada [0, 1, 2], itu akan memindahkan bawaan Anda ke 0. Pemenang akan ditentukan sebagai pemain dengan kemenangan terbanyak dari turnamen round-robin penuh.

Aturan

Anda dapat membaca / menulis dari / ke satu file untuk penyimpanan memori antara iterasi. Bot Anda akan ditempatkan di direktori sendiri sehingga konflik nama file tidak akan terjadi. Anda tidak dapat mengubah fungsi bawaan (seperti generator acak). Itu cukup lucu saat pertama kali dilakukan , tetapi tidak akan lagi. Program Anda tidak diperbolehkan melakukan hal-hal yang hanya penghentian eksekusi yang mencolok. Celah Standar Berlaku .

Pengujian

Kode sumber untuk pengontrol dapat ditemukan di sini . Contoh menjalankannya: java Controller "python program1/test1.py" "python program2/test2.py" 10 5untuk 10 bola salju dan 5 bebek.

Menilai

Pemenang akan diputuskan dengan memilih orang dengan kemenangan terbanyak setelah round-robin penuh. Meskipun ini adalah seri, singkirkan semua orang yang tidak memiliki kemenangan terbanyak. Kemudian, ulangi sampai satu orang menang. Standar penjurian adalah 50 bola salju dan 25 bebek.

Selamat KoTHing!

EDIT : Permainan akan dinyatakan seri jika 1000 putaran berhasil. Bot Anda mungkin menganggap itu turn < 1000.


Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Dennis

@HyperNeutrino Pertanyaan lain: Saya pikir "standar penilaian" adalah 50 bola salju dan 25 bebek? Dan mengapa ada hasil imbang kadang-kadang setelah ~ 18 putaran?
CommonGuy

@Manu Ehh omong kosong Saya lupa mengubah pengaturan dalam argumen VM saya. Dan juga, itu karena jika mereka pergi ke loop tak berujung tabrakan bola salju, itu berakhir setelah 10 putaran mengulangi periode-1 atau periode-2 loop.
HyperNeutrino

1
Jadi, apakah akan ada putaran lain? Karena saya ingin mengunggah bot saya dan ingin tahu seberapa baik dia akan bekerja.
erbsenhirn

@erbsenhirn Jika Anda mengunggah bot dan ping saya di obrolan atau di The Nineteenth Byte , dan saya akan menjalankan proses lain.
HyperNeutrino

Jawaban:


13

Master, C #

Saya melatih jaringan saraf kecil (menggunakan Sharpneat ). Sepertinya suka mengambil bola salju dan menghindar ...

Dalam versi sebelumnya dari pengontrol, ia bahkan menemukan bug. Itu berubah dari 0% menang menjadi 100% ketika menemukan cara untuk menang dengan menipu.

Sunting: Saya lupa mengatur ulang status interal jaringan dan melatih jaringan yang salah. Jaringan yang baru dilatih jauh lebih kecil.

using System;
using System.Collections.Generic;

public class Master
{
    public CyclicNetwork _network;

    public static void Main(string[] args)
    {
        int s = int.Parse(args[1]);
        int os = int.Parse(args[2]);
        int d = int.Parse(args[3]);
        int od = int.Parse(args[4]);
        int ms = int.Parse(args[5]);

        var move = new Master().GetMove(s, os, d, od, ms);
        Console.WriteLine(move);
    }

    public Master()
    {
        var nodes = new List<Neuron>
        {
            new Neuron(0, NodeType.Bias),
            new Neuron(1, NodeType.Input),
            new Neuron(2, NodeType.Input),
            new Neuron(3, NodeType.Input),
            new Neuron(4, NodeType.Input),
            new Neuron(5, NodeType.Input),
            new Neuron(6, NodeType.Output),
            new Neuron(7, NodeType.Output),
            new Neuron(8, NodeType.Output),
            new Neuron(9, NodeType.Hidden)
        };
        var connections = new List<Connection>
        {
            new Connection(nodes[1], nodes[6], -1.3921811701131295),
            new Connection(nodes[6], nodes[6], 0.04683387519679514),
            new Connection(nodes[3], nodes[7], -4.746164930591382),
            new Connection(nodes[8], nodes[8], -0.025484025422054933),
            new Connection(nodes[4], nodes[9], -0.02084856381644095),
            new Connection(nodes[9], nodes[6], 4.9614062853759124),
            new Connection(nodes[9], nodes[9], -0.008672587457112968)
        };
        _network = new CyclicNetwork(nodes, connections, 5, 3, 2);
    }

    public int GetMove(int snowballs, int opponentBalls, int ducks, int opponentDucks, int maxSnowballs)
    {
        _network.InputSignalArray[0] = snowballs;
        _network.InputSignalArray[1] = opponentBalls;
        _network.InputSignalArray[2] = ducks;
        _network.InputSignalArray[3] = opponentDucks;
        _network.InputSignalArray[4] = maxSnowballs;

        _network.Activate();

        double max = double.MinValue;
        int best = 0;
        for (var i = 0; i < _network.OutputCount; i++)
        {
            var current = _network.OutputSignalArray[i];

            if (current > max)
            {
                max = current;
                best = i;
            }
        }

        _network.ResetState();

        return best;
    }
}

public class CyclicNetwork
{
    protected readonly List<Neuron> _neuronList;
    protected readonly List<Connection> _connectionList;
    protected readonly int _inputNeuronCount;
    protected readonly int _outputNeuronCount;
    protected readonly int _inputAndBiasNeuronCount;
    protected readonly int _timestepsPerActivation;
    protected readonly double[] _inputSignalArray;
    protected readonly double[] _outputSignalArray;
    readonly SignalArray _inputSignalArrayWrapper;
    readonly SignalArray _outputSignalArrayWrapper;

    public CyclicNetwork(List<Neuron> neuronList, List<Connection> connectionList, int inputNeuronCount, int outputNeuronCount, int timestepsPerActivation)
    {
        _neuronList = neuronList;
        _connectionList = connectionList;
        _inputNeuronCount = inputNeuronCount;
        _outputNeuronCount = outputNeuronCount;
        _inputAndBiasNeuronCount = inputNeuronCount + 1;
        _timestepsPerActivation = timestepsPerActivation;

        _inputSignalArray = new double[_inputNeuronCount];
        _outputSignalArray = new double[_outputNeuronCount];

        _inputSignalArrayWrapper = new SignalArray(_inputSignalArray, 0, _inputNeuronCount);
        _outputSignalArrayWrapper = new SignalArray(_outputSignalArray, 0, outputNeuronCount);
    }
    public int OutputCount
    {
        get { return _outputNeuronCount; }
    }
    public SignalArray InputSignalArray
    {
        get { return _inputSignalArrayWrapper; }
    }
    public SignalArray OutputSignalArray
    {
        get { return _outputSignalArrayWrapper; }
    }
    public virtual void Activate()
    {
        for (int i = 0; i < _inputNeuronCount; i++)
        {
            _neuronList[i + 1].OutputValue = _inputSignalArray[i];
        }

        int connectionCount = _connectionList.Count;
        int neuronCount = _neuronList.Count;
        for (int i = 0; i < _timestepsPerActivation; i++)
        {
            for (int j = 0; j < connectionCount; j++)
            {
                Connection connection = _connectionList[j];
                connection.OutputValue = connection.SourceNeuron.OutputValue * connection.Weight;
                connection.TargetNeuron.InputValue += connection.OutputValue;
            }
            for (int j = _inputAndBiasNeuronCount; j < neuronCount; j++)
            {
                Neuron neuron = _neuronList[j];
                neuron.OutputValue = neuron.Calculate(neuron.InputValue);
                neuron.InputValue = 0.0;
            }
        }
        for (int i = _inputAndBiasNeuronCount, outputIdx = 0; outputIdx < _outputNeuronCount; i++, outputIdx++)
        {
            _outputSignalArray[outputIdx] = _neuronList[i].OutputValue;
        }
    }
    public virtual void ResetState()
    {
        for (int i = 1; i < _inputAndBiasNeuronCount; i++)
        {
            _neuronList[i].OutputValue = 0.0;
        }
        int count = _neuronList.Count;
        for (int i = _inputAndBiasNeuronCount; i < count; i++)
        {
            _neuronList[i].InputValue = 0.0;
            _neuronList[i].OutputValue = 0.0;
        }
        count = _connectionList.Count;
        for (int i = 0; i < count; i++)
        {
            _connectionList[i].OutputValue = 0.0;
        }
    }
}
public class Connection
{
    readonly Neuron _srcNeuron;
    readonly Neuron _tgtNeuron;
    readonly double _weight;
    double _outputValue;

    public Connection(Neuron srcNeuron, Neuron tgtNeuron, double weight)
    {
        _tgtNeuron = tgtNeuron;
        _srcNeuron = srcNeuron;
        _weight = weight;
    }
    public Neuron SourceNeuron
    {
        get { return _srcNeuron; }
    }
    public Neuron TargetNeuron
    {
        get { return _tgtNeuron; }
    }
    public double Weight
    {
        get { return _weight; }
    }
    public double OutputValue
    {
        get { return _outputValue; }
        set { _outputValue = value; }
    }
}

public class Neuron
{
    readonly uint _id;
    readonly NodeType _neuronType;
    double _inputValue;
    double _outputValue;

    public Neuron(uint id, NodeType neuronType)
    {
        _id = id;
        _neuronType = neuronType;

        // Bias neurons have a fixed output value of 1.0
        _outputValue = (NodeType.Bias == _neuronType) ? 1.0 : 0.0;
    }
    public double InputValue
    {
        get { return _inputValue; }
        set
        {
            if (NodeType.Bias == _neuronType || NodeType.Input == _neuronType)
            {
                throw new Exception("Attempt to set the InputValue of bias or input neuron. Bias neurons have no input, and Input neuron signals should be passed in via their OutputValue property setter.");
            }
            _inputValue = value;
        }
    }
    public double Calculate(double x)
    {
        return 1.0 / (1.0 + Math.Exp(-4.9 * x));
    }
    public double OutputValue
    {
        get { return _outputValue; }
        set
        {
            if (NodeType.Bias == _neuronType)
            {
                throw new Exception("Attempt to set the OutputValue of a bias neuron.");
            }
            _outputValue = value;
        }
    }
}

public class SignalArray
{
    readonly double[] _wrappedArray;
    readonly int _offset;
    readonly int _length;

    public SignalArray(double[] wrappedArray, int offset, int length)
    {
        if (offset + length > wrappedArray.Length)
        {
            throw new Exception("wrappedArray is not long enough to represent the requested SignalArray.");
        }

        _wrappedArray = wrappedArray;
        _offset = offset;
        _length = length;
    }

    public double this[int index]
    {
        get
        {
            return _wrappedArray[_offset + index];
        }
        set
        {
            _wrappedArray[_offset + index] = value;
        }
    }
}

public enum NodeType
{
    /// <summary>
    /// Bias node. Output is fixed to 1.0
    /// </summary>
    Bias,
    /// <summary>
    /// Input node.
    /// </summary>
    Input,
    /// <summary>
    /// Output node.
    /// </summary>
    Output,
    /// <summary>
    /// Hidden node.
    /// </summary>
    Hidden
}

Mengatur ulang keadaan jaringan membuat kinerja menjadi jauh lebih baik :)
HyperNeutrino

Terhadap apa yang Anda latih jaringan saraf? Terhadap bot lain yang diposting di sini?
JAD

@JarkoDubbeldam Ya, saya mengirim beberapa dari mereka ke C # dan melatih jaringan melawan mereka. Itu sebabnya mungkin akan longgar terhadap bot baru.
CommonGuy

Atau cukup latih jaringan lain melawan bot dan yang ini: p
JAD

Wat. 8 suara untuk jaringan saraf!
Christopher

6

Simpan Satu, Python

Melemparkan sebagian besar bola saljunya segera, tetapi selalu menyelamatkan satu jika lawan sedang mengawasi kekurangan amunisi. Kemudian, itik selama mungkin (sekali lagi, hemat 1) sebelum memuat ulang kecuali ada jaminan aman memuat kembali atau dijamin membunuh.

import sys
turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = map(int, sys.argv[1:])

reload_snowball=0
throw=1
duck=2

if snowballs<=1:
    if opponent_snowballs==0:
        if opponent_ducks==0:
            print throw
        else:
            print reload_snowball
    elif ducks > 1:
        print duck
    else:
        print reload_snowball
else:
    print throw

2
jika Anda memiliki 0 bola salju, ia akan mencoba melempar 1
Carl Bosch

@CarlBosch yang seharusnya tidak mungkin dijangkau (selain dimulai dengan 0), tetapi saya akan mengeditnya untuk membahas kasus itu
SnoringFrog

2
@SnoringFrog untuk mengklarifikasi aturan, Anda mulai dengan 0 bola salju
PhiNotPi

@PhiNotPi Saya pasti benar-benar mengabaikannya. Terima kasih atas klarifikasi
SnoringFrog

6

DiprioritaskanRandomBot, Jawa

import java.util.Random;

public class PrioritizedRandomBot implements SnowballFighter {
    static int RELOAD = 0;
    static int THROW = 1;
    static int DUCK = 2;
    static Random rand = new Random();

    public static void main(String[] args) {
        int t = Integer.parseInt(args[0]);
        int s = Integer.parseInt(args[1]);
        int os = Integer.parseInt(args[2]);
        int d = Integer.parseInt(args[3]);
        int od = Integer.parseInt(args[4]);
        int ms = Integer.parseInt(args[5]);
        if (s > os + od) {
            System.out.println(THROW);
            return;
        }
        if (os == 0) {
            if (s == ms || s > 0 && s == od && rand.nextInt(1001 - t) == 0) {
                System.out.println(THROW);
            } else {
                System.out.println(RELOAD);
            }
            return;
        }
        if (os == ms && d > 0) {
            System.out.println(DUCK);
            return;
        }
        int r = rand.nextInt(os + od);
        if (r < s) {
            System.out.println(THROW);
        } else if (r < s + d) {
            System.out.println(DUCK);
        } else {
            System.out.println(RELOAD);
        }
    }
}

Bot ini menyeleksi bilangan bulat acak dalam kisaran 0untuk os + od, dan kemudian memilih untuk baik lemparan, bebek, atau isi ulang, dengan batas yang ditentukan oleh jumlah saat ini bola salju dan bebek.

Satu hal yang penting untuk disadari, adalah bahwa sekali satu bot memiliki lebih banyak bola salju daripada bot lainnya memiliki bola salju + bebek, maka Anda dapat memaksa menang. Dari sini, kita dapat memunculkan konsep "poin":

my points = s - os - od
op points = os - s - d

 effects of moves on my points
        OPPONENT
       R    T    D
   R        L   ++
 M T   W          
 E D   -    +    +

Jika salah satu dari angka-angka ini menjadi positif, maka pemain itu dapat memaksa menang.

points dif = p - op = 2*(s - os) + d - od

 effects of moves on the difference in points (me - my opponent)
        OPPONENT
       R    T    D
   R        L   +++
 M T   W         -
 E D  ---   +   


points sum = p + op = - (d + od)

 effects of moves on the sum of points (me + my opponent)
        OPPONENT
       R    T    D
   R        L    +
 M T   W         +
 E D   +    +   ++

Tabel "perbedaan poin" membentuk dasar teori permainan untuk kompetisi ini. Itu tidak cukup menangkap semua informasi, tetapi itu menunjukkan bagaimana bola salju secara fundamental lebih berharga daripada bebek (karena bola salju adalah pelanggaran dan pertahanan). Jika lawan melempar bola salju dan Anda berhasil mengelak, maka Anda selangkah lebih dekat ke kemenangan paksa, karena lawan Anda menggunakan sumber daya yang lebih berharga. Tabel ini juga menjelaskan apa yang harus Anda lakukan dalam banyak kasus khusus, seperti ketika opsi pemindahan tertentu tidak tersedia.

Tabel "jumlah poin" menunjukkan bagaimana, seiring waktu, jumlah poin mendekati nol (karena kedua pemain kehabisan bebek), pada titik mana pemain pertama melakukan kesalahan (memuat kembali saat mereka tidak perlu) segera kalah.

Sekarang, mari kita coba memperluas strategi pemaksaan ini ke kasus-kasus di mana itu sebenarnya tidak bisa dipaksakan (seperti, kita menang dengan margin besar tetapi membaca pikiran pada bagian lawan akan mengalahkan kita). Pada dasarnya, kita memiliki sbola salju tetapi perlu bola salju lawan s+1(atau s+2, dll) waktu berturut-turut untuk menang Dalam hal ini, kami ingin melakukan beberapa itik atau beberapa isi ulang untuk membeli waktu.

Saat ini, bot ini selalu mencoba menyelinap di beberapa bebek, hanya karena itu tidak berisiko kehilangan segera: kami menganggap bahwa lawan mengikuti strategi yang sama seperti melemparkan bola salju sebanyak mungkin, sehingga upaya untuk memuat kembali benar-benar berbahaya. Juga, untuk mencegah prediktabilitas, kami ingin menyelinap dalam mengikuti distribusi acak-acak: probabilitas merunduk terkait dengan berapa banyak bebek yang perlu kita lakukan relatif terhadap jumlah bola salju yang perlu kita lempar.

Jika kita kalah parah, dalam hal ini s + d < os + odmaka kita perlu menyelinap masuk kembali selain menggunakan semua itik kita, dalam hal ini, kita ingin memuat ulang secara acak, tetapi hanya sebanyak yang kita butuhkan.

Inilah sebabnya mengapa bot kami memprioritaskan dalam urutan lemparan, bebek, dan muat ulang, dan gunakan os + oduntuk menghasilkan angka acak, karena itu adalah jumlah ambang batas langkah yang perlu kita buat.

Ada satu case edge, dan dua case khusus lainnya, yang saat ini ditangani oleh bot. Kasus tepi adalah ketika lawan tidak memiliki bola salju atau bebek, sehingga pengacakan tidak berfungsi, jadi kami melempar jika memungkinkan, jika tidak, kami memuat ulang. Satu kasus khusus lainnya adalah ketika lawan tidak dapat memuat ulang, sehingga tidak ada manfaat untuk melempar (karena lawan akan merunduk atau melempar), jadi kami selalu merunduk (karena menyelamatkan bola salju kami lebih berharga daripada menyelamatkan bebek kami). Kasing khusus terakhir adalah jika lawan tidak memiliki bola salju, dalam hal ini kami memainkannya dengan aman dan memuat ulang jika memungkinkan.


Ini mungkin berakhir dengan mencetak beberapa nomor yang mungkin tidak berfungsi dengan baik.
HyperNeutrino

@HyperNeutrino Saya lupa menambahkan blok "lain" ketika saya menulis ulang bot ini dari menggunakan pengembalian untuk mencetak pernyataan.
PhiNotPi

1
@HyperNeutrino Itu untuk saya, dan saya menganggapnya disadap ...
Erik the Outgolfer

Ah. Ya, maaf karena mengacaukan kode Anda: P Tapi bagus, program pertama yang menggunakan keacakan!
HyperNeutrino

6

NeceBot - Python

Inilah tabel teori permainan untuk game:

        OPPONENT
       R    T     D
   R   ~    L   +D+S
 M T   W    ~   +D-S 
 E D -D-S  -D+S   ~

Di mana ~berarti tidak ada keuntungan, Wmenang, Lkalah, +-Sberarti bola salju diperoleh / kalah atas lawan, dan +-Dberarti bebek diperoleh / kalah atas lawan. Ini adalah game yang sepenuhnya simetris.

Perhatikan bahwa solusi saya tidak memperhitungkan tabel itu. Karena saya buruk dalam matematika.

import sys

RELOAD = 0
THROW = 1
DUCK = 2

def main(turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs):
    if 2 + ducks <3:
        if 2 + snowballs <3:
            return RELOAD
        if 2 + opponent_ducks <3 or 2 + opponent_snowballs <3:
            return THROW
        return RELOAD
    if 2 + snowballs <3:
        if -opponent_snowballs <3 - 5 or 2 + abs(opponent_snowballs - 1) <3:
            return DUCK
        return RELOAD
    if 2 + opponent_ducks <3 or 2 + abs(snowballs - max_snowballs) <3:
        return THROW
    if -snowballs <3 - 6 or turn % 5 <3:
        return THROW
    return DUCK

print(main(*map(int, sys.argv[1:])))

Ini disebut NeceBot karena mencoba mengurangi apa yang perlu terlebih dahulu. Ini memiliki beberapa strategi sewenang-wenang setelah itu, yang saya harap berhasil.


4
Whee begitu banyak <3s. +1 karena memiliki meja permainan dan kemudian tidak menggunakannya: P Tapi solusi yang bagus :)
HyperNeutrino

3 + opponent_snowballs <3ini mungkin kesalahan?
PhiNotPi

@PhiNotPi Yup. Dimaksudkan sebagai 2. Tetap sekarang, terima kasih!
Artyer

Sayangnya, sejumlah besar <3s membuat kode ini cukup sulit untuk dipahami :(
CalculatorFeline

5

Pengecut - Scala

Melempar, jika lawan tidak memiliki amunisi, jika tidak (sesuai urutan prioritas) bebek, lemparan atau reload.

object Test extends App {
  val s = args(1).toInt
  val os = args(2).toInt
  val d = args(3).toInt

  val move = 
    if(os == 0)
      if(s > 0)
        1
      else
        0
    else if(d > 0)
        2
    else if(s > 0)
      1
    else
      0

  println(move)
}

Sepertinya ini membuat bot saya macet ...
Erik the Outgolfer

5

TheUglyDuckling - Python

Akan selalu merunduk sampai tidak bisa kemudian mencoba untuk melempar jika lawan kosong atau memuat ulang jika keduanya kosong. Akan menggunakan memuat ulang sebagai pilihan terakhir.

import sys

arguments = sys.argv;

turn = int(arguments[1])
snowballs = int(arguments[2])
opponent_snowballs = int(arguments[3])
ducks = int(arguments[4])
opponent_ducks = int(arguments[5])
max_snowballs = int(arguments[6])

if ducks > 0:
    print 2
elif opponent_snowballs == 0 and snowballs > 0:
    print 1
elif opponent_snowballs == 0 and snowballs <= 0:
    print 0
elif snowballs > 0:
    print 1
elif snowballs <= 0:
    print 0

5

SimpleBot - Python 2

import sys
turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = map(int, sys.argv[1:])

if opponent_snowballs > 0 and ducks > 0: print 2
elif snowballs: print 1
else: print 0

Hal-hal sederhana.

  • Jika lawan memiliki bola salju dan Anda memiliki bebek, maka Anda bebek.
  • Jika lawan tidak memiliki bola salju dan Anda miliki, maka Anda melempar.
  • Pada kasus lain, Anda memuat ulang.

5

Bot Penamaan-hal-ini-sulit - VB.NET

Memberi penamaan adalah hal yang sulit, dan saya tidak yakin saya memiliki strategi yang kohesif.

Berusaha mempertaruhkan beberapa putaran pertama untuk mendapatkan kemenangan awal. Setelah itu, bermain lebih aman dari sisa waktu, mencoba menang dengan gesekan.

Module SnowballFight

    Private Enum Action
        Reload = 0
        ThrowSnowball = 1
        Duck = 2
    End Enum

    Sub Main(args As String())
        Dim turn As Integer = args(0)
        Dim mySnowballs As Integer = args(1)
        Dim opponentSnowballs As Integer = args(2)
        Dim myDucks As Integer = args(3)
        Dim opponentDucks As Integer = args(4)
        Dim maxSnowballs As Integer = args(5)

        If mySnowballs = 0 AndAlso opponentSnowballs = 0 Then
            ' can't throw, no need to duck
            Console.WriteLine(Action.Reload)
            Exit Sub
        End If

        If turn = 2 AndAlso opponentSnowballs > 0 Then
            ' everyone will probably reload and then throw, so try and duck, and throw turn 3
            Console.WriteLine(Action.Duck)
            Exit Sub
        End If

        If turn = 3 AndAlso opponentSnowballs = 0 Then
            ' they threw on turn 2, get them!
            Console.WriteLine(Action.ThrowSnowball)
            Exit Sub
        End If

        If mySnowballs > 0 AndAlso opponentSnowballs = 0 Then
            ' hope they don't duck
            Console.WriteLine(Action.ThrowSnowball)
            Exit Sub
        End If

        If mySnowballs = 0 AndAlso opponentSnowballs > 0 Then
            If myDucks > 0 Then
                ' watch out!
                Console.WriteLine(Action.Duck)
                Exit Sub
            Else
                ' well, maybe we'll get lucky
                Console.WriteLine(Action.Reload)
                Exit Sub
            End If
        End If

        If opponentSnowballs > 0 AndAlso myDucks > 5 Then
            ' play it safe
            Console.WriteLine(Action.Duck)
            Exit Sub
        End If

        If mySnowballs > 5 OrElse opponentDucks < 5 Then
            ' have a bunch saved up, start throwing them
            Console.WriteLine(Action.ThrowSnowball)
            Exit Sub
        End If

        ' start saving up
        Console.WriteLine(Action.Reload)
    End Sub

End Module

5

MachineGun, Python 3

Berusaha untuk menyimpan bola salju sampai dijamin membunuh lawan, atau sampai keluar dari bebek (Dalam hal ini, ia mulai menembakkan semua bola salju secara membabi buta, seperti senapan mesin)

Ia juga bebek setiap kali lawan memiliki bola salju, karena ia tidak ingin mati.

from os import sys
args = sys.argv[1:]
turn = int(args[0])
snowballs = int(args[1])
opponent_snowballs = int(args[2])
ducks = int(args[3])
opponent_ducks = int(args[4])
max_snowballs = int(args[5])
if ducks > 0 and opponent_snowballs > 0:
    print("2")
elif snowballs > 0 and opponent_snowballs == 0 and opponent_ducks == 0:
    print("1")
elif ducks == 0 and snowballs > 0:
    print("1")
elif snowballs < max_snowballs:
    print("0")
elif snowballs == max_snowballs:
    print("1")
else:
    print("0")

5

Knowbot, Python3

Melacak frekuensi gerakan sebelumnya, dengan asumsi lawan akan membuat yang paling sering lagi, bertahan melawan itu.

** Diperbarui untuk tidak mengharapkan gerakan yang tidak bisa dilakukan lawan **

import sys,pickle
TURN,BALLS,OTHROWS,DUCKS,ODUCKS,MAXB,OLOADS = [i for i in range(7)]

def save_state(data,prob):
    with open('snowball.pickle', 'wb') as f:
        pickle.dump((data,prob), f)

def load_state():
    with open('snowball.pickle', 'rb') as f:
        return pickle.load(f)

def reload(data = None):
    if not data or data[BALLS]<data[MAXB]:
        print(0)
        return True
    return False

def throw(data):
    if data[BALLS]>0:
        print(1)
        return True
    return False
def duck(data):
    if data[DUCKS]>0:
        print(2)
        return True
    return False


data = [int(v) for v in sys.argv[1:]]
data.append(0)

if data[TURN] > 0:
    last_data,prob = load_state()
    delta = [l-n for l,n in zip(last_data, data)]
    if delta[OTHROWS]<0:
        delta[OTHROWS]=0
        delta[OLOADS]=1
    prob = [p+d for p,d in zip(prob,delta)]
else:
    prob = [0]*7

expected = sorted(((prob[action],action) for action in [OTHROWS, ODUCKS, OLOADS]),
                      reverse=True)
expect = next( (a for p,a in expected if data[a]>0), OLOADS)

if expect == OTHROWS:
    duck(data) or throw(data) or reload()
elif expect == ODUCKS:
    reload(data) or duck(data) or throw(data) or reload()
else:
    throw(data) or reload(data) or duck(data) or reload()

save_state(data,prob);

Saya tidak yakin persis bagaimana ini bekerja, tetapi jika itu menyimpan data di antara putaran (sebagai lawan dari putaran), sayangnya semua data dihapus di antara putaran. Itu tidak membatalkan solusi Anda tetapi ingatlah itu :)
HyperNeutrino

Itu tidak mengharapkan untuk menyimpan data di antara putaran, hanya untuk mengharapkan lawan saat ini konsisten.
AShelly

Baik. Baik. Saya hanya ingin memastikan tidak ada kesalahpahaman. :)
HyperNeutrino

4

Braingolf , Sang Penyerang

<<?1:0

Penyerang itu bukan pengecut! Jika dia memiliki bola salju, dia akan melempar! Jika dia tidak memiliki bola salju, dia akan menghasilkan lebih banyak!

Braingolf , Gila

Ini sebenarnya bukan bot, itu hanya seorang programmer yang saya culik dan dipaksa untuk port setiap proyek yang pernah dibuatnya untuk braingolf. Dia tidak lagi memiliki sedikit pun kewarasan.

<3r!?:1+|%

Menghasilkan angka acak kurang dari 3, dan menghasilkan di t % rmana t adalah belokan saat ini dan r adalah angka acak

Untuk menjalankan ini, Anda harus mengunduh braingolf.pydari github, lalu simpan kode braingolf ke file dan jalankan

python3 braingolf.py -f filename <space separated inputs>

atau cukup masukkan kode langsung seperti ini

python3 braingolf.py -c '<<?1:0' <space separated inputs>

Inputnya cukup tidak relevan sepanjang argumen ke-2 setelah kode / nama file adalah jumlah bola salju yang dimiliki Aggressor.

Catatan: agresor sebenarnya berperilaku identik dengan TestBot, saya hanya ingin membuat entri di braingolf

Braingolf , The Brainy [Rusak sekarang]

VR<<<!?v1:v0|R>!?v1:v0|>R<<!?v1:v0|>R<!?v1:v0|<VR<<.m<.m~v<-?~v0:~v1|>vc
VRv.<.>+1-?2_;|>.M<v?:0_;|1

Tentu saja seseorang harus melakukan ini: D Bagus, dan bahkan bermain golf! : D
HyperNeutrino

Oh, tunggu, ini sama dengan milikku kecuali gofier. lol
HyperNeutrino

@HyperNeutrino ya, saya gunna bekerja pada yang asli dalam bahasa nyata sekarang. Saya akan menggunakan Braingolf untuk yang asli, tetapi itu tidak dapat melakukan conditional bersarang, sehingga membuat segalanya sulit
Skidsdev

2
Saya pikir Anda harus memposting "Otak" sebagai jawaban yang terpisah. Juga, saya pikir itu salah.
Erik the Outgolfer

"The Insane" bukan bot stabil, jadi saya tidak yakin bagaimana @HyperNeutrino akan memeriksanya.
Erik the Outgolfer

3

TestBot - Python

Ini adalah kiriman tes untuk menunjukkan seperti apa kiriman itu. Strategi: Pengganti reload dan lempar secara bergantian. Strategi yang cukup buruk tetapi memberi Anda gambaran tentang bagaimana program Anda harus bekerja.

from os import sys
arguments = sys.argv;
turn = int(arguments[1])
print(turn % 2)

Apakah _, turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = sys.argvargumennya?
Artyer

@Artyer Ya. Ternyata argumen pertama memiliki nama file.
HyperNeutrino

Anda hanya dapat menggunakan sys.argv[1:]jika Anda tidak ingin mengacaukannya_
sagiksp

2

UpperHandBot, Python 3

import sys
turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = map(int, sys.argv[1:])

if snowballs <= opponent_snowballs:
  if opponent_snowballs > 0 and ducks > 0:
    print(2)
  else:
    if snowballs < max_snowballs:
      print(0)
    else:
      print(1)
else:
  print(1)

Bot ini mencoba mengumpulkan lebih banyak bola salju daripada lawannya, dan pada saat itu mulai melempar. Jika suatu saat UHB tidak memiliki lebih banyak bola salju daripada lawannya, itu akan:

  • Bebek, jika lawan memiliki bola salju dan itik yang tersisa
  • Kalau tidak, muat ulang (kecuali UHB maksimum, maka itu akan melempar, meskipun saya tidak berpikir situasi ini akan muncul)

2

Yggdrasli, Jawa

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class Yggdrasil implements SnowballFighter {
    public static boolean debug = false;
    static int RELOAD = 0;
    static int THROW = 1;
    static int DUCK = 2;
    static int INVALID = -3;
    static Random rand = new Random();

    public static void main(String[] args) {
        int t = Integer.parseInt(args[0]);
        int s = Integer.parseInt(args[1]);
        int os = Integer.parseInt(args[2]);
        int d = Integer.parseInt(args[3]);
        int od = Integer.parseInt(args[4]);
        int ms = Integer.parseInt(args[5]);
        System.out.println((new Yggdrasil()).move(t, s, os, d, od, ms));
    }

    public final int move(int t, int s, int os, int d, int od, int ms) {
        State state = State.get(s, os, d, od);
        double val = state.val(4);
        double[] strat = state.strat;
        int move = INVALID;
        if (debug) {
            System.out.println(val + " : " + strat[0] + " " + strat[1] + " " + strat[2]);
        }
        while (move == INVALID) {
            double r = rand.nextDouble();
            if (r < strat[RELOAD] && strat[RELOAD] > 0.0001) {
                move = RELOAD;
            } else if (r < strat[RELOAD] + strat[THROW] && strat[THROW] > 0.0001) {
                move = THROW;
            } else if (r < strat[RELOAD] + strat[THROW] + strat[DUCK] && strat[DUCK] > 0.0001) {
                move = DUCK;
            }
        }
        return move;
    }

    public static class State {

        public static boolean debug = false;
        public static int ms = 50;
        public int s;
        public int os;
        public static int md = 25;
        public int d;
        public int od;

        public State(int s, int os, int d, int od) {
            super();
            this.s = s;
            this.os = os;
            this.d = d;
            this.od = od;
        }

        Double val;
        int valdepth;
        double[] strat = new double[3];

        public Double val(int maxdepth) {
            if (s < 0 || s > ms || d < 0 || d > md || os < 0 || os > ms || od < 0 || od > md) {
                return null;
            } else if (val != null && valdepth >= maxdepth) {
                return val;
            }
            if (s > os + od) {
                val = 1.0; // force win
                strat = new double[] { 0, 1, 0 };
            } else if (os > s + d) {
                val = -1.0; // force loss
                strat = new double[] { 1.0 / (1.0 + s + d), s / (1.0 + s + d), d / (1.0 + s + d) };
            } else if (d == 0 && od == 0) {
                val = 0.0; // perfect tie
                if (s > 0) {
                    strat = new double[] { 0, 1, 0 };
                } else {
                    strat = new double[] { 1, 0, 0 };
                }
            } else if (maxdepth <= 0) {
                double togo = 1 - s + os + od;
                double otogo = 1 - os + s + d;
                double per = otogo * otogo / (togo * togo + otogo * otogo);
                double oper = togo * togo / (togo * togo + otogo * otogo);
                val = per - oper;
            } else {
                Double[][] fullmatrix = new Double[3][3];
                boolean[] vm = new boolean[3];
                boolean[] ovm = new boolean[3];
                for (int i = 0; i < 3; i++) {
                    int dest_s = s;
                    int dest_d = d;
                    if (i == 0) {
                        dest_s++;
                    } else if (i == 1) {
                        dest_s--;
                    } else {
                        dest_d--;
                    }
                    for (int j = 0; j < 3; j++) {
                        int dest_os = os;
                        int dest_od = od;
                        if (j == 0) {
                            dest_os++;
                        } else if (j == 1) {
                            dest_os--;
                        } else {
                            dest_od--;
                        }
                        if (i == 0 && j == 1 && dest_os >= 0 && dest_s <= ms) {
                            fullmatrix[i][j] = -1.0; // kill
                        } else if (i == 1 && j == 0 && dest_s >= 0 && dest_os <= ms) {
                            fullmatrix[i][j] = 1.0; // kill
                        } else {
                            fullmatrix[i][j] = get(dest_s, dest_os, dest_d, dest_od).val(maxdepth - 1);
                        }
                        if (fullmatrix[i][j] != null) {
                            vm[i] = true;
                            ovm[j] = true;
                        }
                    }
                }

                if (debug) {
                    System.out.println();
                    System.out.println(maxdepth);
                    System.out.println(s + " " + os + " " + d + " " + od);
                    for (int i = 0; i < 3; i++) {
                        System.out.print(vm[i]);
                    }
                    System.out.println();
                    for (int i = 0; i < 3; i++) {
                        System.out.print(ovm[i]);
                    }
                    System.out.println();
                    for (int i = 0; i < 3; i++) {
                        for (int j = 0; j < 3; j++) {
                            System.out.printf(" %7.4f", fullmatrix[i][j]);
                        }
                        System.out.println();
                    }
                }
                // really stupid way to find an approximate best strategy
                val = -1.0;
                double[] p = new double[3];
                for (p[0] = 0; p[0] < 0.0001 || vm[0] && p[0] <= 1.0001; p[0] += 0.01) {
                    for (p[1] = 0; p[1] < 0.0001 || vm[1] && p[1] <= 1.0001 - p[0]; p[1] += 0.01) {
                        p[2] = 1.0 - p[0] - p[1];
                        if (p[2] < 0.0001 || vm[2]) {
                            double min = 1;
                            for (int j = 0; j < 3; j++) {
                                if (ovm[j]) {
                                    double sum = 0;
                                    for (int i = 0; i < 3; i++) {
                                        if (vm[i]) {
                                            sum += fullmatrix[i][j] * p[i];
                                        }
                                    }
                                    min = Math.min(min, sum);
                                }
                            }
                            if (min > val) {
                                val = min;
                                strat = p.clone();
                            }
                        }
                    }
                }
                if (debug) {
                    System.out.println("v:" + val);
                    System.out.println("s:" + strat[0] + " " + strat[1] + " " + strat[2]);
                }
            }
            valdepth = maxdepth;
            return val;
        }

        static Map<Integer, State> cache = new HashMap<Integer, State>();

        static State get(int s, int os, int d, int od) {
            int key = (((s) * 100 + os) * 100 + d) * 100 + od;
            if (cache.containsKey(key)) {
                return cache.get(key);
            }
            State res = new State(s, os, d, od);
            cache.put(key, res);
            return res;
        }
    }
}

Saya menamakan bot ini "Yggdrasil" karena ia benar-benar melihat ke bawah pohon permainan dan melakukan penilaian negara, dari mana ia dapat menghitung strategi campuran yang kira-kira ideal. Karena bergantung pada strategi campuran, itu sangat non-deterministik. Saya tidak tahu seberapa baik hal ini akan dilakukan dalam persaingan nyata.

Beberapa hal tentang bot ini:

  • Inti adalah fungsi rekursif yang menghitung nilai dan strategi campuran hampir ideal untuk setiap kondisi permainan tertentu. Saat ini saya sudah mengatur untuk melihat 4 langkah ke depan.
  • Bermain sangat menarik, karena dalam banyak kasus bot ini setara dengan "memilih gerakan acak di batu-kertas-gunting". Ini memegang pijakan dan berharap bahwa lawannya memberikannya keuntungan statistik. Jika bot ini sempurna (yang bukan), yang terbaik yang bisa Anda lakukan untuk melawannya adalah 50% menang dan 50% kerugian. Akibatnya, tidak ada lawan yang secara konsisten mengalahkan, tetapi juga tidak ada yang kalah secara konsisten.

Saya masih tidak mengerti namanya ...: P
HyperNeutrino

@HyperNeutrino Yggdrasil adalah pohon mitologis, dan dalam hal ini saya mengacu pada pohon permainan.
PhiNotPi

Ohhhh benar aku merasa seharusnya aku ingat ini. : P Bagus!
HyperNeutrino

2

Nyeri di Nash (C ++)

Disebut demikian karena fakta bahwa saya harus menulis pemecah kesetimbangan Nash saya sendiri sangat menyakitkan. Saya kagum bahwa tidak ada perpustakaan penyelesaian Nash yang tersedia!

#include <fstream>
#include <iostream>
#include <vector>
#include <array>
#include <random>
#include <utility>

typedef double NumT;
static const NumT EPSILON = 1e-5;

struct Index {
    int me;
    int them;

    Index(int me, int them) : me(me), them(them) {}
};

struct Value {
    NumT me;
    NumT them;

    Value(void) : me(0), them(0) {}

    Value(NumT me, NumT them) : me(me), them(them) {}
};

template <int subDimMe, int subDimThem>
struct Game {
    const std::array<NumT, 9> *valuesMe;
    const std::array<NumT, 9> *valuesThemT;

    std::array<int, subDimMe> coordsMe;
    std::array<int, subDimThem> coordsThem;

    Game(
        const std::array<NumT, 9> *valuesMe,
        const std::array<NumT, 9> *valuesThemT
    )
        : valuesMe(valuesMe)
        , valuesThemT(valuesThemT)
        , coordsMe{}
        , coordsThem{}
    {}

    Index baseIndex(Index i) const {
        return Index(coordsMe[i.me], coordsThem[i.them]);
    }

    Value at(Index i) const {
        Index i2 = baseIndex(i);
        return Value(
            (*valuesMe)[i2.me * 3 + i2.them],
            (*valuesThemT)[i2.me + i2.them * 3]
        );
    }

    Game<2, 2> subgame22(int me0, int me1, int them0, int them1) const {
        Game<2, 2> b(valuesMe, valuesThemT);
        b.coordsMe[0] = coordsMe[me0];
        b.coordsMe[1] = coordsMe[me1];
        b.coordsThem[0] = coordsThem[them0];
        b.coordsThem[1] = coordsThem[them1];
        return b;
    }
};

struct Strategy {
    std::array<NumT, 3> probMe;
    std::array<NumT, 3> probThem;
    Value expectedValue;
    bool valid;

    Strategy(void)
        : probMe{}
        , probThem{}
        , expectedValue()
        , valid(false)
    {}

    void findBestMe(const Strategy &b) {
        if(b.valid && (!valid || b.expectedValue.me > expectedValue.me)) {
            *this = b;
        }
    }
};

template <int dimMe, int dimThem>
Strategy nash_pure(const Game<dimMe, dimThem> &g) {
    Strategy s;
    int choiceMe = -1;
    int choiceThem = 0;
    for(int me = 0; me < dimMe; ++ me) {
        for(int them = 0; them < dimThem; ++ them) {
            const Value &v = g.at(Index(me, them));
            bool valid = true;
            for(int me2 = 0; me2 < dimMe; ++ me2) {
                if(g.at(Index(me2, them)).me > v.me) {
                    valid = false;
                }
            }
            for(int them2 = 0; them2 < dimThem; ++ them2) {
                if(g.at(Index(me, them2)).them > v.them) {
                    valid = false;
                }
            }
            if(valid) {
                if(choiceMe == -1 || v.me > s.expectedValue.me) {
                    s.expectedValue = v;
                    choiceMe = me;
                    choiceThem = them;
                }
            }
        }
    }
    if(choiceMe != -1) {
        Index iBase = g.baseIndex(Index(choiceMe, choiceThem));
        s.probMe[iBase.me] = 1;
        s.probThem[iBase.them] = 1;
        s.valid = true;
    }
    return s;
}

Strategy nash_mixed(const Game<2, 2> &g) {
    //    P    Q
    // p a A  b B
    // q c C  d D

    Value A = g.at(Index(0, 0));
    Value B = g.at(Index(0, 1));
    Value C = g.at(Index(1, 0));
    Value D = g.at(Index(1, 1));

    // q = 1-p, Q = 1-P
    // Pick p such that choice of P,Q is arbitrary

    // p*A+(1-p)*C = p*B+(1-p)*D
    // p*A+C-p*C = p*B+D-p*D
    // p*(A+D-B-C) = D-C
    // p = (D-C) / (A+D-B-C)

    NumT p = (D.them - C.them) / (A.them + D.them - B.them - C.them);

    // P*a+(1-P)*b = P*c+(1-P)*d
    // P*a+b-P*b = P*c+d-P*d
    // P*(a+d-b-c) = d-b
    // P = (d-b) / (a+d-b-c)

    NumT P = (D.me - B.me) / (A.me + D.me - B.me - C.me);

    Strategy s;
    if(p >= -EPSILON && p <= 1 + EPSILON && P >= -EPSILON && P <= 1 + EPSILON) {
        if(p <= 0) {
            p = 0;
        } else if(p >= 1) {
            p = 1;
        }
        if(P <= 0) {
            P = 0;
        } else if(P >= 1) {
            P = 1;
        }
        Index iBase0 = g.baseIndex(Index(0, 0));
        Index iBase1 = g.baseIndex(Index(1, 1));
        s.probMe[iBase0.me] = p;
        s.probMe[iBase1.me] = 1 - p;
        s.probThem[iBase0.them] = P;
        s.probThem[iBase1.them] = 1 - P;
        s.expectedValue = Value(
            P * A.me + (1 - P) * B.me,
            p * A.them + (1 - p) * C.them
        );
        s.valid = true;
    }
    return s;
}

Strategy nash_mixed(const Game<3, 3> &g) {
    //    P    Q    R
    // p a A  b B  c C
    // q d D  e E  f F
    // r g G  h H  i I

    Value A = g.at(Index(0, 0));
    Value B = g.at(Index(0, 1));
    Value C = g.at(Index(0, 2));
    Value D = g.at(Index(1, 0));
    Value E = g.at(Index(1, 1));
    Value F = g.at(Index(1, 2));
    Value G = g.at(Index(2, 0));
    Value H = g.at(Index(2, 1));
    Value I = g.at(Index(2, 2));

    // r = 1-p-q, R = 1-P-Q
    // Pick p,q such that choice of P,Q,R is arbitrary

    NumT q = ((
        + A.them * (I.them-H.them)
        + G.them * (B.them-C.them)
        - B.them*I.them
        + H.them*C.them
    ) / (
        (G.them+E.them-D.them-H.them) * (B.them+I.them-H.them-C.them) -
        (H.them+F.them-E.them-I.them) * (A.them+H.them-G.them-B.them)
    ));

    NumT p = (
        ((G.them+E.them-D.them-H.them) * q + (H.them-G.them)) /
        (A.them+H.them-G.them-B.them)
    );

    NumT Q = ((
        + A.me * (I.me-F.me)
        + C.me * (D.me-G.me)
        - D.me*I.me
        + F.me*G.me
    ) / (
        (C.me+E.me-B.me-F.me) * (D.me+I.me-F.me-G.me) -
        (F.me+H.me-E.me-I.me) * (A.me+F.me-C.me-D.me)
    ));

    NumT P = (
        ((C.me+E.me-B.me-F.me) * Q + (F.me-C.me)) /
        (A.me+F.me-C.me-D.me)
    );

    Strategy s;
    if(
        p >= -EPSILON && q >= -EPSILON && p + q <= 1 + EPSILON &&
        P >= -EPSILON && Q >= -EPSILON && P + Q <= 1 + EPSILON
    ) {
        if(p <= 0) { p = 0; }
        if(q <= 0) { q = 0; }
        if(P <= 0) { P = 0; }
        if(Q <= 0) { Q = 0; }
        if(p + q >= 1) {
            if(p > q) {
                p = 1 - q;
            } else {
                q = 1 - p;
            }
        }
        if(P + Q >= 1) {
            if(P > Q) {
                P = 1 - Q;
            } else {
                Q = 1 - P;
            }
        }
        Index iBase0 = g.baseIndex(Index(0, 0));
        s.probMe[iBase0.me] = p;
        s.probThem[iBase0.them] = P;
        Index iBase1 = g.baseIndex(Index(1, 1));
        s.probMe[iBase1.me] = q;
        s.probThem[iBase1.them] = Q;
        Index iBase2 = g.baseIndex(Index(2, 2));
        s.probMe[iBase2.me] = 1 - p - q;
        s.probThem[iBase2.them] = 1 - P - Q;
        s.expectedValue = Value(
            A.me * P + B.me * Q + C.me * (1 - P - Q),
            A.them * p + D.them * q + G.them * (1 - p - q)
        );
        s.valid = true;
    }
    return s;
}

template <int dimMe, int dimThem>
Strategy nash_validate(Strategy &&s, const Game<dimMe, dimThem> &g, Index unused) {
    if(!s.valid) {
        return s;
    }

    NumT exp;

    exp = 0;
    for(int them = 0; them < dimThem; ++ them) {
        exp += s.probThem[them] * g.at(Index(unused.me, them)).me;
    }
    if(exp > s.expectedValue.me) {
        s.valid = false;
        return s;
    }

    exp = 0;
    for(int me = 0; me < dimMe; ++ me) {
        exp += s.probMe[me] * g.at(Index(me, unused.them)).them;
    }
    if(exp > s.expectedValue.them) {
        s.valid = false;
        return s;
    }

    return s;
}

Strategy nash(const Game<2, 2> &g, bool verbose) {
    Strategy s = nash_mixed(g);
    s.findBestMe(nash_pure(g));
    if(!s.valid && verbose) {
        std::cerr << "No nash equilibrium found!" << std::endl;
    }
    return s;
}

Strategy nash(const Game<3, 3> &g, bool verbose) {
    Strategy s = nash_mixed(g);
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(1, 2,  1, 2)), g, Index(0, 0)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(1, 2,  0, 2)), g, Index(0, 1)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(1, 2,  0, 1)), g, Index(0, 2)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 2,  1, 2)), g, Index(1, 0)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 2,  0, 2)), g, Index(1, 1)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 2,  0, 1)), g, Index(1, 2)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 1,  1, 2)), g, Index(2, 0)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 1,  0, 2)), g, Index(2, 1)));
    s.findBestMe(nash_validate(nash_mixed(g.subgame22(0, 1,  0, 1)), g, Index(2, 2)));
    s.findBestMe(nash_pure(g));
    if(!s.valid && verbose) {
        // theory says this should never happen, but fp precision makes it possible
        std::cerr << "No nash equilibrium found!" << std::endl;
    }
    return s;
}

struct PlayerState {
    int balls;
    int ducks;

    PlayerState(int balls, int ducks) : balls(balls), ducks(ducks) {}

    PlayerState doReload(int maxBalls) const {
        return PlayerState(std::min(balls + 1, maxBalls), ducks);
    }

    PlayerState doThrow(void) const {
        return PlayerState(std::max(balls - 1, 0), ducks);
    }

    PlayerState doDuck(void) const {
        return PlayerState(balls, std::max(ducks - 1, 0));
    }

    std::array<double,3> flail(int maxBalls) const {
        // opponent has obvious win;
        // try stuff at random and hope the opponent is bad

        (void) ducks;

        int options = 0;
        if(balls > 0) {
            ++ options;
        }
        if(balls < maxBalls) {
            ++ options;
        }
        if(ducks > 0) {
            ++ options;
        }

        std::array<double,3> p{};
        if(balls < balls) {
            p[0] = 1.0f / options;
        }
        if(balls > 0) {
            p[1] = 1.0f / options;
        }
        return p;
    }
};

class GameStore {
protected:
    const int balls;
    const int ducks;
    const std::size_t playerStates;
    const std::size_t gameStates;

public:
    static std::string filename(int turn) {
        return "nashdata_" + std::to_string(turn) + ".dat";
    }

    GameStore(int maxBalls, int maxDucks)
        : balls(maxBalls)
        , ducks(maxDucks)
        , playerStates((balls + 1) * (ducks + 1))
        , gameStates(playerStates * playerStates)
    {}

    std::size_t playerIndex(const PlayerState &p) const {
        return p.balls * (ducks + 1) + p.ducks;
    }

    std::size_t gameIndex(const PlayerState &me, const PlayerState &them) const {
        return playerIndex(me) * playerStates + playerIndex(them);
    }

    std::size_t fileIndex(const PlayerState &me, const PlayerState &them) const {
        return 2 + gameIndex(me, them) * 2;
    }

    PlayerState stateFromPlayerIndex(std::size_t i) const {
        return PlayerState(i / (ducks + 1), i % (ducks + 1));
    }

    std::pair<PlayerState, PlayerState> stateFromGameIndex(std::size_t i) const {
        return std::make_pair(
            stateFromPlayerIndex(i / playerStates),
            stateFromPlayerIndex(i % playerStates)
        );
    }

    std::pair<PlayerState, PlayerState> stateFromFileIndex(std::size_t i) const {
        return stateFromGameIndex((i - 2) / 2);
    }
};

class Generator : public GameStore {
    static char toDat(NumT v) {
        int iv = int(v * 256.0);
        return char(std::max(std::min(iv, 255), 0));
    }

    std::vector<Value> next;

public:
    Generator(int maxBalls, int maxDucks)
        : GameStore(maxBalls, maxDucks)
        , next()
    {}

    const Value &nextGame(const PlayerState &me, const PlayerState &them) const {
        return next[gameIndex(me, them)];
    }

    void make_probabilities(
        std::array<NumT, 9> &g,
        const PlayerState &me,
        const PlayerState &them
    ) const {
        const int RELOAD = 0;
        const int THROW = 1;
        const int DUCK = 2;

        g[RELOAD * 3 + RELOAD] =
            nextGame(me.doReload(balls), them.doReload(balls)).me;

        g[RELOAD * 3 + THROW] =
            (them.balls > 0) ? -1
            : nextGame(me.doReload(balls), them.doThrow()).me;

        g[RELOAD * 3 + DUCK] =
            nextGame(me.doReload(balls), them.doDuck()).me;

        g[THROW * 3 + RELOAD] =
            (me.balls > 0) ? 1
            : nextGame(me.doThrow(), them.doReload(balls)).me;

        g[THROW * 3 + THROW] =
            ((me.balls > 0) == (them.balls > 0))
            ? nextGame(me.doThrow(), them.doThrow()).me
            : (me.balls > 0) ? 1 : -1;

        g[THROW * 3 + DUCK] =
            (me.balls > 0 && them.ducks == 0) ? 1
            : nextGame(me.doThrow(), them.doDuck()).me;

        g[DUCK * 3 + RELOAD] =
            nextGame(me.doDuck(), them.doReload(balls)).me;

        g[DUCK * 3 + THROW] =
            (them.balls > 0 && me.ducks == 0) ? -1
            : nextGame(me.doDuck(), them.doThrow()).me;

        g[DUCK * 3 + DUCK] =
            nextGame(me.doDuck(), them.doDuck()).me;
    }

    Game<3, 3> make_game(const PlayerState &me, const PlayerState &them) const {
        static std::array<NumT, 9> globalValuesMe;
        static std::array<NumT, 9> globalValuesThemT;
        #pragma omp threadprivate(globalValuesMe)
        #pragma omp threadprivate(globalValuesThemT)

        make_probabilities(globalValuesMe, me, them);
        make_probabilities(globalValuesThemT, them, me);
        Game<3, 3> g(&globalValuesMe, &globalValuesThemT);
        for(int i = 0; i < 3; ++ i) {
            g.coordsMe[i] = i;
            g.coordsThem[i] = i;
        }
        return g;
    }

    Strategy solve(const PlayerState &me, const PlayerState &them, bool verbose) const {
        if(me.balls > them.balls + them.ducks) { // obvious answer
            Strategy s;
            s.probMe[1] = 1;
            s.probThem = them.flail(balls);
            s.expectedValue = Value(1, -1);
            return s;
        } else if(them.balls > me.balls + me.ducks) { // uh-oh
            Strategy s;
            s.probThem[1] = 1;
            s.probMe = me.flail(balls);
            s.expectedValue = Value(-1, 1);
            return s;
        } else if(me.balls == 0 && them.balls == 0) { // obvious answer
            Strategy s;
            s.probMe[0] = 1;
            s.probThem[0] = 1;
            s.expectedValue = nextGame(me.doReload(balls), them.doReload(balls));
            return s;
        } else {
            return nash(make_game(me, them), verbose);
        }
    }

    void generate(int turns, bool saveAll, bool verbose) {
        next.clear();
        next.resize(gameStates);
        std::vector<Value> current(gameStates);
        std::vector<char> data(2 + gameStates * 2);

        for(std::size_t turn = turns; (turn --) > 0;) {
            if(verbose) {
                std::cerr << "Generating for turn " << turn << "..." << std::endl;
            }
            NumT maxDiff = 0;
            NumT msd = 0;
            data[0] = balls;
            data[1] = ducks;
            #pragma omp parallel for reduction(+:msd), reduction(max:maxDiff)
            for(std::size_t meBalls = 0; meBalls < balls + 1; ++ meBalls) {
                for(std::size_t meDucks = 0; meDucks < ducks + 1; ++ meDucks) {
                    const PlayerState me(meBalls, meDucks);
                    for(std::size_t themBalls = 0; themBalls < balls + 1; ++ themBalls) {
                        for(std::size_t themDucks = 0; themDucks < ducks + 1; ++ themDucks) {
                            const PlayerState them(themBalls, themDucks);
                            const std::size_t p1 = gameIndex(me, them);

                            Strategy s = solve(me, them, verbose);

                            NumT diff;

                            data[2+p1*2  ] = toDat(s.probMe[0]);
                            data[2+p1*2+1] = toDat(s.probMe[0] + s.probMe[1]);
                            current[p1] = s.expectedValue;
                            diff = current[p1].me - next[p1].me;
                            msd += diff * diff;
                            maxDiff = std::max(maxDiff, std::abs(diff));
                        }
                    }
                }
            }

            if(saveAll) {
                std::ofstream fs(filename(turn).c_str(), std::ios_base::binary);
                fs.write(&data[0], data.size());
                fs.close();
            }

            if(verbose) {
                std::cerr
                    << "Expectations changed by at most " << maxDiff
                    << " (RMSD: " << std::sqrt(msd / gameStates) << ")" << std::endl;
            }
            if(maxDiff < 0.0001f) {
                if(verbose) {
                    std::cerr << "Expectations have converged. Stopping." << std::endl;
                }
                break;
            }
            std::swap(next, current);
        }

        // Always save turn 0 with the final converged expectations
        std::ofstream fs(filename(0).c_str(), std::ios_base::binary);
        fs.write(&data[0], data.size());
        fs.close();
    }
};

void open_file(std::ifstream &target, int turn, int maxDucks, int maxBalls) {
    target.open(GameStore::filename(turn).c_str(), std::ios::binary);
    if(target.is_open()) {
        return;
    }

    target.open(GameStore::filename(0).c_str(), std::ios::binary);
    if(target.is_open()) {
        return;
    }

    Generator(maxBalls, maxDucks).generate(200, false, false);
    target.open(GameStore::filename(0).c_str(), std::ios::binary);
}

int choose(int turn, const PlayerState &me, const PlayerState &them, int maxBalls) {
    std::ifstream fs;
    open_file(fs, turn, std::max(me.ducks, them.ducks), maxBalls);

    unsigned char balls = fs.get();
    unsigned char ducks = fs.get();
    fs.seekg(GameStore(balls, ducks).fileIndex(me, them));
    unsigned char p0 = fs.get();
    unsigned char p1 = fs.get();
    fs.close();

    // only 1 random number per execution; no need to seed a PRNG
    std::random_device rand;
    int v = std::uniform_int_distribution<int>(0, 254)(rand);
    if(v < p0) {
        return 0;
    } else if(v < p1) {
        return 1;
    } else {
        return 2;
    }
}

int main(int argc, const char *const *argv) {
    if(argc == 4) { // maxTurns, maxBalls, maxDucks
        Generator(atoi(argv[2]), atoi(argv[3])).generate(atoi(argv[1]), true, true);
        return 0;
    }

    if(argc == 7) { // turn, meBalls, themBalls, meDucks, themDucks, maxBalls
        std::cout << choose(
            atoi(argv[1]),
            PlayerState(atoi(argv[2]), atoi(argv[4])),
            PlayerState(atoi(argv[3]), atoi(argv[5])),
            atoi(argv[6])
        ) << std::endl;
        return 0;
    }

    return 1;
}

Kompilasi sebagai C ++ 11 atau lebih baik. Untuk kinerja, sebaiknya dikompilasi dengan dukungan OpenMP (tapi ini hanya untuk kecepatan; itu tidak diperlukan)

g++ -std=c++11 -fopenmp pain_in_the_nash.cpp -o pain_in_the_nash

Ini menggunakan kesetimbangan Nash untuk memutuskan apa yang harus dilakukan pada setiap belokan, yang berarti bahwa secara teori itu akan selalu menang atau seri dalam jangka panjang (dalam banyak pertandingan), apa pun strategi yang digunakan lawan. Apakah itu yang terjadi dalam praktik tergantung pada apakah saya membuat kesalahan dalam implementasi. Namun, karena kompetisi KoTH ini hanya memiliki satu putaran melawan masing-masing lawan, itu mungkin tidak akan berhasil dengan baik di papan peringkat.

Ide awal saya adalah memiliki fungsi penilaian sederhana untuk setiap kondisi permainan (mis. Setiap bola bernilai + b, setiap bebek adalah + d), tetapi ini mengarah pada masalah yang jelas mencari tahu seperti apa penilaian itu seharusnya, dan berarti itu tidak bisa bertindak atas berkurangnya pengembalian semakin banyak bola, dll. Jadi, ini akan menganalisis seluruh pohon permainan , bekerja mundur dari belokan 1000, dan mengisi penilaian aktual berdasarkan pada bagaimana setiap permainan bisa berjalan dengan baik.

Hasilnya adalah bahwa saya sama sekali tidak tahu strategi apa yang digunakan ini, kecuali untuk beberapa perilaku "jelas" keras-kode (melempar bola salju jika Anda memiliki lebih banyak bola daripada lawan Anda memiliki bola + bebek, dan memuat kembali jika Anda berdua keluar bola salju). Jika ada yang ingin menganalisis dataset yang dihasilkannya, saya bayangkan ada beberapa perilaku menarik untuk ditemukan!

Menguji ini terhadap "Simpan Satu" menunjukkan bahwa ia memang menang dalam jangka panjang, tetapi hanya dengan selisih kecil (514 menang, 486 kerugian, 0 imbang dalam batch pertama dari 1000 pertandingan, dan 509 menang, 491 kerugian, 0 menarik yang kedua).


Penting!

Ini akan berhasil, tetapi itu bukan ide yang bagus. Dibutuhkan sekitar 9 menit pada laptop saya yang cukup-pengembang-spesifik untuk menghasilkan pohon permainan penuh. Tapi itu akan menyimpan probabilitas akhir menjadi file setelah mereka dibuat, dan setelah itu setiap belokan hanya menghasilkan angka acak dan membandingkannya dengan 2 byte, jadi ini sangat cepat.

Untuk pintas semua itu, cukup unduh file ini (3.5MB) dan letakkan di direktori dengan executable.

Atau Anda dapat membuatnya sendiri dengan menjalankan:

./pain_in_the_nash 1000 50 25

Yang akan menyimpan satu file per giliran, hingga konvergensi. Perhatikan bahwa setiap file berukuran 3,5 MB dan konvergen pada gilirannya 720 (yaitu 280 file, ~ 1GB), dan karena sebagian besar game tidak mendekati 720, file pra-konvergensi memiliki kepentingan yang sangat rendah.


Apakah mungkin membuat program hanya menampilkan hasil akhir? Terima kasih!
HyperNeutrino

@HyperNeutrino semua output lainnya harus ke stderr, jadi seharusnya tidak berdampak apa pun, tapi saya telah memperbaruinya untuk hanya menunjukkan kemajuan ketika menjalankan dalam mode preprocessing. Sekarang hanya akan menulis ke stdout ketika berjalan secara normal. Saya sarankan mengikuti saran "penting", karena jika tidak, itu hanya akan berkeliaran di belokan pertama selama beberapa menit (setidaknya dengan preprocessing Anda dapat melihat kemajuannya).
Dave

Oh baiklah. Saya akan mengikuti saran itu, terima kasih!
HyperNeutrino

Saya akan sangat menghargai jika Anda dapat mengunggah file data karena butuh selamanya untuk menghasilkan semuanya. Jika Anda bisa melakukan itu, itu akan menjadi luar biasa :)
HyperNeutrino

@HyperNeutrino OK, perlu waktu selamanya untuk mengunggah di internet saya yang mengerikan, tetapi file konvergensi 3.5MB tersedia di sini: github.com/davidje13/snowball_koth_pitn/blob/master/… (cukup taruh di direktori yang sama).
Dave

1

Swift - TheCrazy_XcodeRandomness

Sayangnya, ini hanya dapat dijalankan secara lokal, dalam Xcode, karena mengandung Foundationmodul dan fungsinya arc4random_uniform(),. Namun, Anda bisa mengetahui apa itu algoritma:

import Foundation

func game(turn: Int, snowballs: Int, opponent_snowballs: Int, ducks: Int, opponent_ducks: Int, max_snowballs: Int) -> Int{
    let RELOAD = 0
    let THROW = 1
    let DUCK = 2
    if turn == 0{
        return arc4random_uniform(2)==0 ? THROW : DUCK
    }
    else if ducks == 0{
        if snowballs != 0{return THROW}
        else {return RELOAD}
    }
    else if snowballs < max_snowballs && snowballs != 0{
        if opponent_ducks == 0 && opponent_snowballs == 0{return THROW}
        else if opponent_snowballs == 0{
            return arc4random_uniform(2)==0 ? THROW : RELOAD
        }
        else if opponent_ducks == 0{return THROW}
        else { return arc4random_uniform(2)==0 ? THROW : RELOAD }
    }
    else if opponent_snowballs == max_snowballs{
        return DUCK
    }
    else if snowballs == max_snowballs || opponent_ducks < 1 || turn < max_snowballs{return THROW}
    return arc4random_uniform(2)==0 ? THROW : RELOAD
}

Apakah ini dapat dijalankan dari bash di Linux?
HyperNeutrino

@HyperNeutrino Saya tahu itu bisa di macOS, tapi saya tidak tahu apakah itu di Linux. Jika Anda bisa memeriksanya, itu akan bagus. Coba swiftperintah dan kemudian periksa apakah itu berfungsi
Tn. Xcoder

Tampaknya tidak ada; ada paket dengan itu tetapi tidak Swift bahasa. Jadi saya mungkin tidak menguji ini sampai saya bisa mendapatkan sesuatu yang berfungsi, maaf.
HyperNeutrino

satu-satunya yang memungkinkan adalah Xcode dan IntelliJ, tetapi tidak dapat dijalankan secara online karena Foundation, maaf: /
Mr. Xcoder

meninggal dunia. Saya harus dapat menjalankannya dari baris perintah untuk menjalankan pengontrol dengan itu, tetapi jika saya punya waktu, saya mungkin secara manual menjalankan ini lagi semua bot lainnya.
HyperNeutrino

1

TableBot, Python 2

Disebut TableBot karena dibuat dengan mengimplementasikan tabel ini:

snow   duck   osnow   oduck   move
0      0      0       0       0
0      0      0       1       0
0      0      1       0       0
0      0      1       1       0
0      1      0       0       0
0      1      0       1       0
0      1      1       0       2
0      1      1       1       2
1      0      0       0       1
1      0      0       1       1
1      0      1       0       1
1      0      1       1       1
1      1      0       0       1
1      1      0       1       1
1      1      1       0       1
1      1      1       1       1

A 1 menunjukkan memiliki 1 atau lebih, 0 menunjukkan tidak memiliki.

Bot:

import sys

reload=0
throw=1
duck=2

t,snowballs,o_snowballs,ducks,o_ducks,m=map(int,sys.argv[1:])

if snowballs > 0:
	print throw
elif ducks==0:
	print reload
elif o_snowballs==0:
	print reload
else:
	print duck

Cobalah online!


1

AmbBot - Skema Racket

Saya kebanyakan ingin mencoba menggunakan amb, karena itu keren. Bot ini secara acak memesan opsi (muat ulang, melempar, dan menghindar), memfilter opsi yang tidak masuk akal, dan memilih opsi pertama. Tetapi dengan amb, kita bisa menggunakan kelanjutan dan mundur!

#lang racket
(require racket/cmdline)

; Defining amb.
(define failures null)

(define (fail)
  (if (pair? failures) ((first failures)) (error "no more choices!")))

(define (amb/thunks choices)
  (let/cc k (set! failures (cons k failures)))
  (if (pair? choices)
    (let ([choice (first choices)]) (set! choices (rest choices)) (choice))
    (begin (set! failures (rest failures)) (fail))))

(define-syntax-rule (amb E ...) (amb/thunks (list (lambda () E) ...)))

(define (assert condition) (unless condition (fail)))

(define (!= a b)
  (not (= a b)))

(define (amb-list list)
  (if (null? list)
      (amb)
      (amb (car list)
           (amb-list (cdr list)))))

; The meaningful code!
; Start by defining our options.
(define reload 0)
(define throw 1)
(define duck 2)

; The heart of the program.
(define (make-choice turn snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (let ((can-reload? (reload-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs))
        (can-throw? (throw-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs))
        (can-duck? (duck-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs)))
    (if (not (or can-reload? can-throw? can-duck?))
        (random 3) ; something went wrong, panic
        (let* ((ls (shuffle (list reload throw duck)))
               (action (amb-list ls)))
          (assert (or (!= action reload) can-reload?))
          (assert (or (!= action throw) can-throw?))
          (assert (or (!= action duck) can-duck?))
          action))))

; Define what makes a move possible.
(define (reload-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (not (or
        (= snowballs max_snowballs) ; Don't reload if we're full.
        (and (= opponent_ducks 0) (= opponent_snowballs max_snowballs)) ; Don't reload if opponent will throw.
        )))

(define (throw-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (not (or
        (= snowballs 0) ; Don't throw if we don't have any snowballs.
        (= opponent_snowballs max_snowballs) ; Don't throw if our opponent won't be reloading.
        )))

(define (duck-valid? snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (not (or
        (= ducks 0) ; Don't duck if we can't.
        (= opponent_snowballs 0) ; Don't duck if our opponent can't throw.
        )))

; Parse the command line, make a choice, print it out.
(command-line
 #:args (turn snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
 (writeln (make-choice
           (string->number turn)
           (string->number snowballs)
           (string->number opponent_snowballs)
           (string->number ducks)
           (string->number opponent_ducks)
           (string->number max_snowballs))))

Saya juga membuat program uji kecil untuk menjalankan dua bot ini satu sama lain. Rasanya seperti bot kedua menang lebih sering, jadi saya mungkin telah membuat kesalahan di suatu tempat.

(define (run)
  (run-helper 0 0 0 5 5 5))                         

(define (run-helper turn snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (printf "~a ~a ~a ~a ~a ~a ~n" turn snowballs opponent_snowballs ducks opponent_ducks max_snowballs)
  (let ((my-action (make-choice turn snowballs opponent_snowballs ducks opponent_ducks max_snowballs))
        (opponent-action (make-choice turn opponent_snowballs snowballs opponent_ducks ducks max_snowballs)))
    (cond ((= my-action reload)
           (cond ((= opponent-action reload)
                  (run-helper (+ turn 1) (+ snowballs 1) (+ opponent_snowballs 1) ducks opponent_ducks max_snowballs))
                 ((= opponent-action throw)
                  (writeln "Opponent wins!"))
                 ((= opponent-action duck)
                  (run-helper (+ turn 1) (+ snowballs 1) opponent_snowballs ducks (- opponent_ducks 1) max_snowballs))))
          ((= my-action throw)
           (cond ((= opponent-action reload)
                  (writeln "I win!"))
                 ((= opponent-action throw)
                  (run-helper (+ turn 1) (- snowballs 1) (- opponent_snowballs 1) ducks opponent_ducks max_snowballs))
                 ((= opponent-action duck)
                  (run-helper (+ turn 1) (- snowballs 1) opponent_snowballs ducks (- opponent_ducks 1) max_snowballs))))
          ((= my-action duck)
           (cond ((= opponent-action reload)
                  (run-helper (+ turn 1) snowballs (+ opponent_snowballs 1) (- ducks 1) opponent_ducks max_snowballs))
                 ((= opponent-action throw)
                  (run-helper (+ turn 1) snowballs (- opponent_snowballs 1) (- ducks 1) opponent_ducks max_snowballs))
                 ((= opponent-action duck)
                  (run-helper (+ turn 1) snowballs opponent_snowballs (- ducks 1) (- opponent_ducks 1) max_snowballs)))))))

1

MonteBot, C ++

Saya pada dasarnya mengambil kode dari koth ini dan memodifikasinya untuk tantangan ini. Ini menggunakan algoritma UCT Monte Carlo Tree Search Decoupled. Itu harus cukup dekat dengan keseimbangan nash.

#include <cstdlib>
#include <cmath>
#include <random>
#include <cassert>
#include <iostream>


static const int TOTAL_ACTIONS = 3;
static const int RELOAD = 0;
static const int THROW = 1;
static const int DUCK = 2;

//The number of simulated games we run every time our program is called.
static const int MONTE_ROUNDS = 10000;

struct Game
{
    int turn;
    int snowballs;
    int opponentSnowballs;
    int ducks;
    int opponentDucks;
    int maxSnowballs;
    bool alive;
    bool opponentAlive;

    Game(int turn, int snowballs, int opponentSnowballs, int ducks, int opponentDucks, int maxSnowballs)
        : turn(turn),
          snowballs(snowballs),
          opponentSnowballs(opponentSnowballs),
          ducks(ducks),
          opponentDucks(opponentDucks),
          maxSnowballs(maxSnowballs),
          alive(true),
          opponentAlive(true)
    {
    }

    Game(int turn, int snowballs, int opponentSnowballs, int ducks, int opponentDucks, int maxSnowballs, bool alive, bool opponentAlive)
        : turn(turn),
        snowballs(snowballs),
        opponentSnowballs(opponentSnowballs),
        ducks(ducks),
        opponentDucks(opponentDucks),
        maxSnowballs(maxSnowballs),
        alive(alive),
        opponentAlive(opponentAlive)
    {
    }

    bool atEnd() const
    {
        return !(alive && opponentAlive) || turn >= 1000;
    }

    bool isValidMove(int i, bool me)
    {
        if (atEnd())
        {
            return false;
        }

        switch (i)
        {
        case RELOAD:
            return (me ? snowballs : opponentSnowballs) < maxSnowballs;
        case THROW:
            return (me ? snowballs : opponentSnowballs) > 0;
        case DUCK:
            return (me ? ducks : opponentDucks) > 0 && (me ? opponentSnowballs : snowballs) > 0;
        default:
            throw "This should never be executed.";
        }

    }

    Game doTurn(int my_action, int enemy_action)
    {
        assert(isValidMove(my_action, true));
        assert(isValidMove(enemy_action, false));

        Game result(*this);

        result.turn++;

        switch (my_action)
        {
        case RELOAD:
            result.snowballs++;
            break;
        case THROW:
            result.snowballs--;
            if (enemy_action == RELOAD)
            {
                result.opponentAlive = false;
            }
            break;
        case DUCK:
            result.ducks--;
            break;
        default:
            throw "This should never be executed.";
        }

        switch (enemy_action)
        {
        case RELOAD:
            result.opponentSnowballs++;
            break;
        case THROW:
            result.opponentSnowballs--;
            if (my_action == RELOAD)
            {
                result.alive = false;
            }
            break;
        case DUCK:
            result.opponentDucks--;
            break;
        default:
            throw "This should never be executed.";
        }

        return result;
    }
};

struct Stat
{
    int wins;
    int attempts;

    Stat() : wins(0), attempts(0) {}
};

/**
* A Monte tree data structure.
*/
struct MonteTree
{
    //The state of the game.
    Game game;

    //myStats[i] returns the statistic for doing the i action in this state.
    Stat myStats[TOTAL_ACTIONS];
    //opponentStats[i] returns the statistic for the opponent doing the i action in this state.
    Stat opponentStats[TOTAL_ACTIONS];
    //Total number of times we've created statistics from this tree.
    int totalPlays = 0;

    //The action that led to this tree.
    int myAction;
    //The opponent action that led to this tree.
    int opponentAction;

    //The tree preceding this one.
    MonteTree *parent = nullptr;

    //subtrees[i][j] is the tree that would follow if I did action i and the
    //opponent did action j.
    MonteTree *subtrees[TOTAL_ACTIONS][TOTAL_ACTIONS] = { { nullptr } };

    MonteTree(const Game &game) :
        game(game), myAction(-1), opponentAction(-1) {}


    MonteTree(Game game, MonteTree *parent, int myAction, int opponentAction) :
        game(game), myAction(myAction), opponentAction(opponentAction), parent(parent)
    {
        //Make sure the parent tree keeps track of this tree.
        parent->subtrees[myAction][opponentAction] = this;
    }

    //The destructor so we can avoid slow ptr types and memory leaks.
    ~MonteTree()
    {
        //Delete all subtrees.
        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            for (int j = 0; j < TOTAL_ACTIONS; j++)
            {
                auto branch = subtrees[i][j];

                if (branch)
                {
                    branch->parent = nullptr;
                    delete branch;
                }
            }
        }
    }

    double scoreMove(int move, bool me)
    {

        const Stat &stat = me ? myStats[move] : opponentStats[move];
        return stat.attempts == 0 ?
            HUGE_VAL :
            double(stat.wins) / stat.attempts + sqrt(2 * log(totalPlays) / stat.attempts);
    }


    MonteTree * expand(int myAction, int enemyAction)
    {
        return new MonteTree(
            game.doTurn(myAction, enemyAction),
            this,
            myAction,
            enemyAction);
    }

    int bestMove() const
    {
        //Select the move with the highest win rate.
        int best;
        double bestScore = -1;
        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            if (myStats[i].attempts == 0)
            {
                continue;
            }

            double score = double(myStats[i].wins) / myStats[i].attempts;
            if (score > bestScore)
            {
                bestScore = score;
                best = i;
            }
        }

        return best;
    }
};

int random(int min, int max)
{
    static std::random_device rd;
    static std::mt19937 rng(rd());

    std::uniform_int_distribution<int> uni(min, max - 1);

    return uni(rng);
}

/**
* Trickle down root until we have to create a new leaf MonteTree or we hit the end of a game.
*/
MonteTree * selection(MonteTree *root)
{
    while (!root->game.atEnd())
    {
        //First pick the move that my bot will do.

        //The action my bot will do.
        int myAction;
        //The number of actions with the same bestScore.
        int same = 0;
        //The bestScore
        double bestScore = -1;

        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            //Ignore invalid or idiot moves.
            if (!root->game.isValidMove(i, true))
            {
                continue;
            }

            //Get the score for doing move i. Uses
            double score = root->scoreMove(i, true);

            //Randomly select one score if multiple actions have the same score.
            //Why this works is boring to explain.
            if (score == bestScore)
            {
                same++;
                if (random(0, same) == 0)
                {
                    myAction = i;
                }
            }
            //Yay! We found a better action.
            else if (score > bestScore)
            {
                same = 1;
                myAction = i;
                bestScore = score;
            }
        }

        //The action the enemy will do.
        int enemyAction;

        //Use the same algorithm to pick the enemies move we use for ourselves.
        same = 0;
        bestScore = -1;
        for (int i = 0; i < TOTAL_ACTIONS; i++)
        {
            if (!root->game.isValidMove(i, false))
            {
                continue;
            }

            double score = root->scoreMove(i, false);
            if (score == bestScore)
            {
                same++;
                if (random(0, same) == 0)
                {
                    enemyAction = i;
                }
            }
            else if (score > bestScore)
            {
                same = 1;
                enemyAction = i;
                bestScore = score;
            }
        }

        //If this combination of actions hasn't been explored yet, create a new subtree to explore.
        if (!(*root).subtrees[myAction][enemyAction])
        {
            return root->expand(myAction, enemyAction);
        }

        //Do these actions and explore the next subtree.
        root = (*root).subtrees[myAction][enemyAction];
    }
    return root;
}

/**
* Chooses a random move for me and my opponent and does it.
*/
Game doRandomTurn(Game &game)
{
    //Select my random move.
    int myAction;
    int validMoves = 0;

    for (int i = 0; i < TOTAL_ACTIONS; i++)
    {
        //Don't do idiotic moves.
        //Select one at random.
        if (game.isValidMove(i, true))
        {
            validMoves++;
            if (random(0, validMoves) == 0)
            {
                myAction = i;
            }
        }
    }

    //Choose random opponent action.
    int opponentAction;

    //Whether the enemy has encountered this situation before
    bool enemyEncountered = false;

    validMoves = 0;

    //Weird algorithm that works and I don't want to explain.
    //What it does:
    //If the enemy has encountered this position before,
    //then it chooses a random action weighted by how often it did that action.
    //If they haven't, makes the enemy choose a random not idiot move.
    for (int i = 0; i < TOTAL_ACTIONS; i++)
    {
        if (game.isValidMove(i, false))
        {
            validMoves++;
            if (random(0, validMoves) == 0)
            {
                opponentAction = i;
            }
        }
    }

    return game.doTurn(myAction, opponentAction);
}


/**
* Randomly simulates the given game.
* Has me do random moves that are not stupid.
* Has opponent do random moves.
*
* Returns 1 for win. 0 for loss. -1 for draw.
*/
int simulate(Game game)
{
    while (!game.atEnd())
    {
        game = doRandomTurn(game);
    }

    if (game.alive > game.opponentAlive)
    {
        return 1;
    }
    else if (game.opponentAlive > game.alive)
    {
        return 0;
    }
    else //Draw
    {
        return -1;
    }
}


/**
* Propagates the score up the MonteTree from the leaf.
*/
void update(MonteTree *leaf, int score)
{
    while (true)
    {
        MonteTree *parent = leaf->parent;
        if (parent)
        {
            //-1 = draw, 1 = win for me, 0 = win for opponent
            if (score != -1)
            {
                parent->myStats[leaf->myAction].wins += score;
                parent->opponentStats[leaf->opponentAction].wins += 1 - score;
            }
            parent->myStats[leaf->myAction].attempts++;
            parent->opponentStats[leaf->opponentAction].attempts++;
            parent->totalPlays++;
            leaf = parent;
        }
        else
        {
            break;
        }
    }
}

int main(int argc, char* argv[])
{
    Game game(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), atoi(argv[5]), atoi(argv[6]));

    MonteTree current(game);

    for (int i = 0; i < MONTE_ROUNDS; i++)
    {
        //Go down the tree until we find a leaf we haven't visites yet.
        MonteTree *leaf = selection(&current);

        //Randomly simulate the game at the leaf and get the result.
        int score = simulate(leaf->game);

        //Propagate the scores back up the root.
        update(leaf, score);
    }

    int move = current.bestMove();

    std::cout << move << std::endl;

    return 0;
}

Kompilasi Instruksi untuk linux:

Simpan ke MonteBot.cpp.
Lari g++ -o -std=c++11 MonteBot MonteBot.cpp.

Perintah untuk dijalankan: ./MonteBot <args>


1

The suka menunda - Python 3

Prokrastinator akan menunda-nunda dengan memainkan save beberapa belokan pertama. Tiba-tiba monster panik itu ingin menghindari kehilangan perang sumber daya dengan melawan gerakan lawan yang paling sering digunakan.

import sys

turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = map(int, sys.argv[1:])

max_ducks = 25
times_opponent_ducked = max_ducks - ducks 
times_opponent_thrown = (turn - times_opponent_ducked - opponent_snowballs) / 2
times_opponent_reloaded = times_opponent_thrown + opponent_snowballs


## return a different action, if the disiered one is not possible
def throw():
    if snowballs:
        return 1
    else:
        return duck()

def duck():
    if ducks:
        return 2
    else:
        return reload()

def reload():
    return 0





def instant_gratification_monkey():
    ## throw, if you still have a ball left afterwards
    if snowballs >= 2 or opponent_ducks == 0:
        return throw()
    ## duck, if opponent can throw
    elif opponent_snowballs > 0:
        return duck()
    ## reload, if opponent has no balls and you have only one
    else:
        return reload()

def panic_monster():
    ## throw while possible, else reload
    if times_opponent_reloaded > times_opponent_ducked: 
        if snowballs > 0:
            return throw() 
        else:
            return reload()
    ## alternating reload and duck
    else: 
        if turn % 2 == 1:
            return reload() 
        else:
            return duck()

def procrastinator():     
    if turn < 13 or (snowballs + ducks > opponent_snowballs + opponent_ducks):
        return instant_gratification_monkey()
    else:
        return panic_monster()


print(procrastinator())

"Penunda". Jadi, semua orang di PPCG yang sebenarnya ingin mengerjakan pekerjaan rumah? (Jangan menyangkalnya, orang yang membaca ini. Dan saya)
HyperNeutrino

1
"Monyet Gratifikasi Instan" Anda pernah melihat TEDTalk itu juga? :)
HyperNeutrino


0

ParanoidBot dan PanicBot - ActionScript3 ( RedTamarin )

Dari bahasa niche yang tidak sesuai (dengan ekstensi untuk memberikan argumen baris perintah) memuji Skittish ParanoidBot dan sekutunya yang membosankan, PanicBot.

ParanoidBot

ParanoidBot kehilangan akal, dan memiliki strategi khusus yang tidak perlu untuk diandalkan. Pertama, meluncurkan bola salju sampai ambang tercapai, menjaga beberapa cadangan. Kemudian, setelah tiga itik peringatan, paranoia masuk, dan bot mencoba menimbun lebih banyak bola salju di antara itik acak. Setelah mengisi kembali persediaannya, ParanoidBot kembali melempar dengan membabi buta. Karena suara-suara di kepalanya, ParanoidBot dapat mengetahui apakah dijamin akan menang atau kalah, dan akan "menyusun strategi" sesuai dengan itu.

import shell.Program;
import shell;

var TURN:int = Program.argv[0];
var SB:int = Program.argv[1];
var OPSB:int = Program.argv[2];
var DC:int = Program.argv[3];
var OPDC:int = Program.argv[4];
var MAXSB:int = Program.argv[5];
var usedDucks:int = 0;

if (!FileSystem.exists("data"))
    FileSystem.write("data", 0);
else
    usedDucks = FileSystem.read("data");

if (SB > OPSB + OPDC)
{ trace(1); Program.abort(); }
if (SB + DC < OPSB) {
if (DC > 0)
    trace(2);
else if (SB > 0)
    trace(1);
else
    trace(0);
Program.abort(); }

if (usedDucks >= 3) {
    if (SB > MAXSB / 3) {
        usedDucks = 0;
        FileSystem.write("data", usedDucks);
        trace(1);
        Program.abort();
    }
    else {
        if (Number.random() > 0.5 && DC > 0)
            trace(2);
        else
            trace(0);
    }
}
else {
    if (SB > (MAXSB / 6) && SB >= 3)
    { trace(1); Program.abort(); }
    else {
        usedDucks++;
        FileSystem.write("data", usedDucks);
        if (DC > 0)
            trace(2);
        else if (SB > 0)
            trace(1);
        else
            trace(0);
        Program.abort();
    }
}

Kawat gigi sedikit miring untuk membantu mengembun ukuran

PanicBot

Karena sudah gila, PanicBot bereaksi karena rasa takut naluriah. Setelah kehabisan bebek karena meringkuk ketakutan, PanicBot membabi buta melempar semua bola salju, lalu mati-matian membuat dan melempar lebih banyak bola salju sampai (mungkin) dikalahkan.

import shell.Program;

var SB:int = Program.argv[1];
var DC:int = Program.argv[3];

if (DC > 0)
{ trace(2); Program.abort(); }
if (SB > 0)
{ trace(1); Program.abort(); }
else
{ trace(0); Program.abort(); }



Ini adalah salah satu dari kurang dari 15 entri lain yang menggunakan AS3 di PPCG. Suatu hari, mungkin bahasa yang eksotis ini bisa dibilang akan menemukan teka-teki untuk mendominasi.


Bisakah ini dijalankan dari bash di Linux?
HyperNeutrino

Saya belum mengujinya, tapi ya, seharusnya. Eksekusi RedTamarin (redshell) dibuat untuk Windows, Mac, dan Linux: http://redtamarin.com/tools/redshell . Jika salah satu bot di atas disimpan ke file bernama snow.as, berikut ini harus bekerja di bash:$ ./redshell snow.as -- 0 50 50 25 25

Ini memberi saya izin ditolak kesalahan ketika saya mencoba menjalankan ini.
HyperNeutrino

@HyperNeutrino chmod +x redshelladalah teman Anda di sini ...
Erik the Outgolfer

Mungkin chmod 777 segalanya? Mungkin ada beberapa pemecahan masalah di situs web RedTamarin juga

0

Pembela, Python

Muat ulang saat kedua pemain tidak memiliki bola salju. Jika memiliki bola salju, ia melempar. Jika tidak memiliki bola salju, tetapi lawan memilikinya, itik jika bisa, jika tidak, reload.

def get_move(turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs):
    if snowballs == opponent_snowballs == 0:
        return 0 #Reload
    elif snowballs > 0:
        return 1 # Throw
    elif ducks > 0:
        return 2 # Duck
    else:
        return 0 # Reload

if __name__ == "__main__": # if this is the main program
    import sys
    print(main(*[int(arg) for arg in sys.argv[1:]]))

Catatan: belum diuji

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.