Kartopu Savaşı KoTH!


35

Sonuçlar (May 22 2017 21:40:37 UTC)

Master18 tur kazandı, 2 tur attı, 0 tur attı
Save One15 tur kazandı, 3 tur attı, 2 tur attı
Machine Gun14 tur attı, 3 tur attı, 3 tur attı
Monte Bot14 tur attı, 3 tur attı ve 3 tur attı
Amb Bot12 mermiler, 8 mermi kaybetti ve 0 mermi
Coward11 mermi kazandı, 3 mermi kaybetti ve 6 mermi
Pain in the Nash11 mermi kazandı, 9 mermi, 0 mermi
Nece Bot10 mermi, 7 mermi ve 3 mermi
Naming Things is Hard10 mermi, 7 raund kaybetti ve 3 raund
The Procrastinatorkazandı, 10 raunt kazandı, 8 raund kaybetti ve 2 raund bağladı
Yggdrasil, 10 raund aldı, 10 raund kaybetti, 0 raunt bağladı
Simple Bot9 raund kazandı, 4 raund kaybetti ve 7 raund bağlı 9 raunt
Table Botkazandı, 6 mermi ve 5 mermi bağlandı
Prioritized Random Bot, 8 mermi kazandı, 7 mermi kaybetti ve 5 mermi bağladı
Upper Hand Bot7 raund kazandı, 13 raund kaybettim ve 0 raund
Aggressorkazandı 6 raund kazandı, 10 raund
Insanekaybettim ve
The Ugly Duckling4 raund bağladım 5 raund
Know Botkazandı mermi, 14 mermi kaybetti ve 3 mermi
Paranoid Botkazandı 0 mermi kazandı, 19 mermi kaybetti ve 1
Panic Botmermi 0 mermi kazandı, 19 mermi kaybetti ve 1 mermi bağladı

Ne yazık ki, Crazy X-Code Randomess'i test edemedim çünkü Linux'ta bash'tan çalıştırılamaz. Çalışmasını sağlayabilirsem dahil edeceğim.

Tam kontrol çıkışı


Oyun

Bu çok basit bir KoTH oyunudur. Bire bir kartopu savaşı. kKartopulara dayanabilen, başlangıçta boş bir kabınız var. Zamana kadar ördek olabilir j. Her turda, her iki oyuncudan aynı anda ne hamle yapılacağı için bir seçim yapmaları istenir. Üç hamle var:

  • yeniden yükle: size başka bir kartopu verir (en çok k)
  • fırlatma: yeniden yüklemeye karar verirse diğer oyuncuyu öldürecek bir kartopu fırlatır. Her iki oyuncu da bir kartopu atarsa, kimse ölmez (birbirlerinin kartoplarına vuracak kadar iyi bir amacı vardır)
  • ördek: hiçbir şey yapmaz ve diğer oyuncu bir kartopu atarsa, vurulmaktan kaçınır. Başka ördeğin kalmadıysa, o zaman hiçbir şey olmuyor ve diğer oyuncu bir kartopu atarsa ​​ölürsün.

Amaç

Ölme

Challlenge Özellikleri

Programınız herhangi bir dilde yazılabilir. Bu değişkenlerin her birini her uygulamada bir argüman olarak alacaksınız:

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

turn- kaç tur geçtiniz ( 0ilk tekrarlamada)
snowballs- kaç kartopunuz var
opponent_snowballs- rakip kaç kartopunuz var
ducks- kaç kez daha kaç kaybedersiniz
opponent_ducks- rakip kaç defa kaçabilir
max_snowballs- azami kartopu kaçabiliyorsunuz mağaza ( k)

Anahtar fonksiyonun çıktısı 0yeniden yükleme, 1atma ve 2ördek için olmalıdır. Hamlenin çıktısını almalısınız, yeni satır sonlandırıldı. Lütfen geçersiz hamle yapmayın, ancak denetleyici çok esnektir ve geçersiz hamle yaparsanız kırılmaz (hamle bir tamsayı olmasa bile). Bu gerekir gerçi yeni satır sonlandırılmış olması. Hareket içeride değilse, hareket [0, 1, 2]etmenizi varsayılan olarak ayarlar 0. Kazanan, tam bir turnuva turnuvasında en fazla galibiyet alan oyuncu olarak belirlenecektir.

kurallar

Yinelemeler arasında bellek depolaması için bir dosyadan / dosyasına bir dosya okuyabilir / yazabilirsiniz. Botunuz kendi dizinine yerleştirilir, böylece dosya adı çakışmaları ortaya çıkmaz. Yerleşik işlevleri değiştiremezsiniz (rastgele jeneratör gibi). Oldukça komikti o yapıldı İlk kez , ama artık olmayacak. Programınızın yalnızca bariz yürütme durması gibi şeyler yapmasına izin verilmez. Standart Loopholes Uygula .

Test yapmak

Denetleyicinin kaynak kodu burada bulunabilir . Koşu örneği: java Controller "python program1/test1.py" "python program2/test2.py" 10 510 kartopu ve 5 ördek için.

yargılamak

Kazanan, tam bir turdan sonra en çok kazanan kişiyi seçerek belirlenir. Bu bir kravat olsa da, en fazla kazananı olmayanları kaldırın. Ardından, bir kişi kazanana kadar tekrarlayın. Yargılama standardı 50 kartopu ve 25 ördek olacak.

Mutlu KoTHing!

EDIT : 1000 mermi geçerse oyun kravat olarak ilan edilir. Botunuz bunu varsayabilir turn < 1000.


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

@HyperNeutrino Daha fazla soru: "Yargılama standardı" nın 50 kartopu ve 25 ördek olacağını düşündüm? Ve neden bazen ~ 18 turdan sonra bir beraberlik var?
CommonGuy

@Manu Ehh crap VM değişkenlerimdeki ayarları değiştirmeyi unuttum. Ve ayrıca, eğer sonsuz bir kartopu çarpışma döngüsüne girerlerse, bir periyot-1 ya da periyot-2 döngü tekrar eden 10 tur sonra sona erer.
HyperNeutrino

1
Peki, başka bir tur daha olacak mı? Çünkü botumu yüklemek istiyorum ve ne kadar iyi çalışacağını merak ediyorum.
erbsenhirn

@ erbsenhirn Bir bot yükleyip sohbete veya Nineteenth Byte'a ping attıysam, başka bir koşuya koşardım .
HyperNeutrino

Yanıtlar:


13

Efendi, C #

Sharpneat kullanarak küçük bir sinir ağı eğittim . Kartopu toplamak ve ördek yavrusu almak gibi görünüyor ...

Denetleyicinin önceki bir sürümünde bir hata bile buldu. Hile yaparak nasıl kazanılacağını keşfettiğinde% 0 kazanma% 100'e geçti.

Düzenleme: Ağların interal durumunu sıfırlamayı unuttum ve ağı yanlış eğittim. Yeni eğitilmiş ağ çok daha küçük.

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
}

Görünüşe göre ağ durumunu sıfırlama performansı çok daha iyi yaptı :)
HyperNeutrino

Sinir ağını neye karşı eğittin? Burada yayınlanan diğer botlara karşı?
JAY

@JarkoDubbeldam Evet, birçoğunu C # 'ya taşıdım ve ağı onlara karşı eğittim. Bu yüzden muhtemelen yeni botlara karşı kaybolacak.
CommonGuy

Ya da sadece botlara ve buna karşı başka bir ağ
eğitin

Wat. Bir sinir ağı için 8 oy!
Christopher,

6

Birini Kaydet, Python

Kartoplarının çoğunu derhal atar, ancak rakibin cephane yetersizliği izlemesi durumunda daima bir tasarruf sağlar. Daha sonra, güvenli bir yeniden yükleme veya garantili bir öldürme olmadıkça, yeniden yüklemeden önce mümkün olduğu kadar uzun süre yeniden örter (1, tasarruf 1).

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
0 kartopunuz varsa, 1 atmaya çalışacak
Carl Bosch

@CarlBosch'a ulaşılması imkansız olması gereken devlet (0 ile başlamak dışında), ama yine de bu vakayı kapsayacak bir düzenleme yapacağım
SnoringFrog

2
@SnoringFrog kuralları netleştirmek için, 0 kartopu ile başlarsınız
PhiNotPi

@PhiNotPi Bunu tamamen göz ardı etmiş olmalıyım. Açıklama için teşekkürler
SnoringFrog

6

ÖncelikliRandomBot, Java

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

Bu bot seçer rastgele arasında bir tam sayı 0için os + od, ve sonra kartoplarını ve ördekler mevcut sayısına göre belirlenen eşiklerle ya atış, ördek, ya da yeniden yükleme için seçer.

Gerçekleşmesi gereken önemli olan şey, bir botun diğer botun kartopu + ördekten daha fazla kartopu olduğu zaman kazanmaya zorlayabilmenizdir. Bundan "puan" kavramı ile gelebiliriz:

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   -    +    +

Bu sayılardan herhangi biri pozitif olursa, o oyuncu kazanmaya zorlayabilir.

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   +    +   ++

"Puan farkı" tablosu, oyun teorisinin bu yarışma için temelini oluşturur. Tüm bilgileri tam olarak ele almaz, ancak kartoplarının ördeklerden nasıl temelde daha değerli olduğunu gösterir (kartopu hem saldırı hem de savunmadır). Eğer rakip bir kartopu atarsa ​​ve başarılı bir şekilde vurursanız, o zaman zorla kazanılmış bir zafere bir adım daha yaklaşırsınız, çünkü rakibiniz daha değerli bir kaynak kullanmıştır. Bu tabloda ayrıca, belirli taşıma seçeneklerinin bulunmadığı durumlar gibi birçok özel durumda yapmanız gerekenler açıklanmaktadır.

"Puan toplamı" tablosu, zaman içinde puan toplamının sıfıra nasıl yaklaştığını (her iki oyuncunun da ördekleri tükettiği gibi), bu noktada ilk hata yapan (o zaman ihtiyaç duymadığında yeniden yükleme) hata yapan ilk oyuncuyu nasıl gösterir? kaybeder.

Şimdi, bu zorlama stratejisini gerçekte zorlanamayan vakalara genişletmeye çalışalım (olduğu gibi, büyük bir farkla kazanıyoruz ama rakibin tarafındaki zihin okuma bizi yenecek). Temel olarak, skartopumuz var ama kazanmak için arka arkaya rakibimizin s+1(ya da s+2vb.) Zamanını kartopu yapmamız gerekiyor . Bu durumda, kendimize zaman kazandırmak için birkaç ördek veya birkaç yeniden yükleme yapmak istiyoruz.

Şu anda, bu bot her zaman bazı ördekler arasına gizlice girmeye çalışır, çünkü o zaman ani bir kayıp riski yoktur: Rakibinin olabildiğince çok kartopu gibi bir lobbing stratejisini takip ettiğini varsayıyoruz, bu yüzden yeniden yüklemeye çalışmak gerçekten tehlikeli. Ayrıca, öngörülebilirliği önlemek için, bunları rastgele bir şekilde dağıtmak için bunları gizlice sokmak istiyoruz: ördekler olasılığı, atmamız gereken kartopu sayısına göre ne kadar ördek yapmamız gerektiği ile ilgilidir.

Kötü bir şekilde kaybediyorsak, bu durumda s + d < os + odtüm ördeklerimizi kullanmaya ek olarak bazı yüklere gizlice girmemiz gerekir, bu durumda, ihtiyacımız olduğu kadar rastgele yeniden yüklemek istiyoruz.

Bu yüzden botlarımız fırlatma, ördek ve yeniden yükleme sırasına göre öncelik os + odveriyor ve rastgele sayı üretmeyi kullanıyor , çünkü yapmamız gereken hamle eşik sayısı.

Botun halihazırda ele aldığı bir kenar davası ve diğer iki özel durum var. Son durum, rakibin ne kartopu ne de ördeği olmadığı ve bu yüzden rastgele çalışmadığı zaman, mümkünse fırlatırız, yoksa yeniden yükleriz. Diğer bir özel durum, rakibin yeniden yüklenemediği zamandır ve bu nedenle fırlatmanın bir faydası yoktur (rakip ya attığından ya da fırlattığından), yani her zaman eğiliriz (kartopularımızı kurtarmak ördeklerimizi kurtarmaktan daha değerlidir). Son özel durum, rakibin kartopu olmaması durumunda, bu durumda güvenli oynar ve mümkünse yeniden yükleriz.


Bu, düzgün çalışmayabilecek birden fazla numara yazdırmanıza neden olabilir.
HyperNeutrino

@HyperNeutrino Bu botu yeniden yazarken ifadeleri yazdırmak için kullandığımda "else" bloğunu eklemeyi unuttum.
PhiNotPi

1
@HyperNeutrino Bu benim için yaptı ve ben bugged düşündü ...
Outgolfer Erik

Ah. Evet, kodunuzu bozduğum için üzgünüm: P Ama rastgele kullanan ilk program güzel!
HyperNeutrino

6

NeceBot - Python

İşte oyun için oyun teorisi tablosu:

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

Nerede ~hayır demektir avantajı Wkazanmak edilir L, kaybetmek ise +-Sbir kartopu rakibi karşısında kayıp / kazanılır anlamına gelir ve +-Drakibi karşısında kaybedilen kazanılan bir ördek / anlamına gelir. Bu tamamen simetrik bir oyundur.

Çözümümün bu tabloyu dikkate almadığını unutmayın. Çünkü matematikte kötüyüm.

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:])))

Buna NeceBot denir, çünkü önce gerekli olanı azaltmaya çalışır. Bundan sonra çalışmayı umduğum bazı keyfi stratejiler var.


4
Whee çok fazla <3lol. Bir oyun masasına sahip olan ve kullanmayan için +1: P Ama güzel bir çözüm :)
HyperNeutrino

3 + opponent_snowballs <3bu bir hata olabilir mi?
PhiNotPi

@PhiNotPi Yup. 2 olmak istedim. Şimdi düzeltildi, teşekkürler!
Artyer

Ne yazık ki, çok sayıda <3s kodu anlamak oldukça zor :(
CalculatorFeline

5

Korkak - Scala

Rakipte cephane yoksa fırlatır, aksi halde (öncelik sırasına göre) ördekler, fırlatır veya yeniden yükler.

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

Görünüşe göre bu
botumu sıkıştı

5

TheUglyDuckling - Python

Rakibinin boş olması durumunda atmaya çalışmadığı sürece her zaman ördek ya da her ikisi de boşsa yeniden yükleme yapacaktır. Son çare olarak yeniden yükleme kullanacaktır.

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

Basit şeyler.

  • Rakibin kartopu ve ördekleri varsa, o zaman ördek.
  • Rakibin kartopu yoksa ve sende varsa, o zaman fırlat.
  • Başka bir durumda, yeniden yüklersiniz.

5

Adlandırma-şeyler-zor Bot - VB.NET

Bazı şeyleri adlandırmak zordur ve bunu isimlendirmek için uyumlu bir stratejim olduğundan emin değilim.

Erken bir zafer kazanmak için ilk birkaç turda kumar oynamaya çalışır. Bundan sonra, yıpranma ile kazanmaya çalışırken, zamanın geri kalanında daha güvenli oynar.

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

Rakibi öldürmeyi garanti edinceye kadar ya da ördekleri tükenene kadar kartopu biriktirmeye çalışır (Bu durumda, makineli tüfek gibi tüm kartoplarını kör bir şekilde ateşlemeye başlar)

Aynı zamanda rakibin bir kartopu olduğu zaman da ördekleri çeker, çünkü ölmek istemez.

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

Önceki hamlelerin iz frekansını korur, rakibin tekrar en sık yaptıklarını varsayar, buna karşı savunur.

** Rakibinin hamle yapmamasını beklememek için güncellendi **

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

Bunun tam olarak nasıl çalıştığından emin değilim, ancak turlar arasında (sıralar yerine) veri saklıyorsa, ne yazık ki tüm veriler turlar arasında silinir. Çözümünüzü geçersiz kılmaz ama bunu aklınızda bulundurun :)
HyperNeutrino

Verileri turlar arasında tutmak beklemiyor, sadece mevcut rakibin tutarlı olmasını beklemek.
AShelly

Peki. Tamam. Yanlış anlamaların olmadığından emin olmak istedim. :)
HyperNeutrino

4

Saldırgan Braingolf

<<?1:0

Saldırgan korkak değil! Bir kartopu varsa, atacak! Kartopu yoksa, daha fazlasını yapar!

Braingolf , Insane

Bu aslında bir bot değil, sadece kaçırdığım ve şimdiye kadar yaptığı her projeyi Braingolf'a teslim etmek zorunda bıraktığım bir programcı. Artık aklı başında bir akıl sağlığı yok.

<3r!?:1+|%

3'ten küçük bir rasgele sayı üretir ve t % rt'nin geçerli dönüş ve r'nin rasgele sayı olduğu çıktılar

Bunları çalıştırmak braingolf.pyiçin, github'dan indirmeniz ve ardından braingolf kodunu bir dosyaya kaydetmeniz ve çalıştırmanız gerekir.

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

veya sadece kodu doğrudan bunun gibi girin

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

Girdiler, kod / dosya adından sonraki 2. argüman Aggressor'un sahip olduğu kartopu tutarı olduğu sürece oldukça önemsizdir.

Not: Saldırgan aslında TestBot ile aynı şekilde davranıyor, sadece braingolf'a giriş yapmak istedim

Braingolf , Brainy [Şimdi kırıldı ]

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

Elbette biri bunu yapmak zorundaydı: D Güzel ve hatta golf! : D
HyperNeutrino

Bekle, bu gofier hariç benimkiyle aynı. lol
HyperNeutrino 15:17

@HyperNeutrino yup, şimdi gerçek bir dil üzerinde gerçek bir dil üzerinde çalışacağım. Braingolf'u gerçek bir tane için kullanırdım, ancak iç içe koşullandırma yapamıyor, bu da işleri zorlaştırıyor
Skidsdev

2
Bence "Brainy" i ayrı bir cevap olarak göndermelisin. Ayrıca, sanırım errs.
Outgolfer Erik,

"Çılgın" istikrarlı bir bot değil, bu yüzden @HyperNeutrino bunu nasıl kontrol edeceğinden emin değilim.
Outgolfer Erik,

3

TestBot - Python

Bu, geçerli bir başvurunun neye benzeyebileceğini gösteren bir deneme yazısıdır. Strateji: Alternatif yeniden yükleme ve atma. Oldukça kötü bir strateji ancak programınızın nasıl çalışması gerektiği konusunda size bir fikir veriyor.

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

Misiniz _, turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = sys.argvargümanlar olacak?
Artyer

@ Artyer Evet. İlk argümanın dosya adı olduğu ortaya çıktı.
HyperNeutrino

Sadece sys.argv[1:]bununla _
uğraşmak

2

YukarıBasın, 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)

Bu bot rakibinden daha fazla kartopu toplamaya çalışır ve bu noktada atmaya başlar. Eğer herhangi bir noktada UHB, rakibinden daha fazla kartopuna sahip değilse, o:

  • Ördek, eğer rakibin kartopu varsa ve kalan ördeği varsa
  • Aksi takdirde, yeniden yükleme (UHB maksimum olmadıkça, bunun yerine atılır, ancak bu durumun ortaya çıkacağını sanmıyorum)

2

Yggdrasli, Java

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

Bu botu "Yggdrasil" olarak adlandırdım çünkü oyun ağacının gerisinde gözüküyor ve yaklaşık olarak ideal bir karma stratejiyi hesaplayabildiği durum değerlemesi yapıyor. Karışık stratejilere dayandığından, çok determinist değildir. Bu şeyin gerçek rekabette ne kadar iyi olacağını bilmiyorum.

Bu bot hakkında bir kaç şey:

  • Çekirdek, herhangi bir oyun durumu için değeri ve ideale yakın karışık stratejiyi hesaplayan özyinelemeli bir işlevdir. Şu anda 4 adım ileriye bakmaya hazırım.
  • Pek çok durumda olduğu gibi, bu bot "taş-kağıt-makasta rastgele bir hareket seçmek" ile eşdeğer olduğu için son derece kabadayı oynuyor. Zeminini tutar ve rakibinin ona istatistiksel bir avantaj sağlamasını umar. Eğer bu bot mükemmel olsaydı (ki değil), buna karşı yapabileceğin en iyi şey% 50 kazanç ve% 50 zarar olur. Sonuç olarak, sürekli olarak dövdüğü bir rakip yok, aynı zamanda sürekli olarak kaybettiği hiçbir rakip yok.

İsmini hala anlamıyorum ...: P
HyperNeutrino

@HyperNeutrino Yggdrasil mitolojik bir ağaçtır ve bu durumda oyun ağacına atıfta bulunuyorum.
PhiNotPi

Ohhhh doğru Bunu hatırlamış olmalıyım gibi hissediyorum. : P Nice!
HyperNeutrino

2

Nash'de Ağrı (C ++)

Sözde çünkü kendi Nash denge çözücümü yazmak zorunda kalmam gerçek bir acıydı. Herhangi bir hazır Nash-çözme kütüphanesi olmadığına şaşırdım!

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

C ++ 11 veya daha iyisi olarak derleyin. Performans için OpenMP desteğiyle derlemek iyidir (ancak bu sadece hız içindir; gerekli değildir).

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

Bu, her turda ne yapılması gerektiğine karar vermek için Nash dengesini kullanır, bu da teoride , rakibin hangi stratejiyi kullandığı fark etmeksizin uzun vadede (birçok oyunda) her zaman kazanacağı veya çekileceği anlamına gelir . Uygulamada durumun bu olup olmadığı, uygulamada herhangi bir hata yapıp yapmamama bağlıdır. Ancak, bu KoTH yarışmasının sadece her rakibe karşı tek bir tur atması nedeniyle, muhtemelen büyük olasılıkla çok iyi sonuç vermeyecektir.

Asıl fikrim, her oyun durumu için basit bir değerleme işlevine sahip olmaktı (örneğin, her top + b, her ördek + d'dir), ancak bu, bu değerlerin ne olması gerektiğini belirleyen bariz sorunlara yol açmaktadır ve bu, ne yapabileceği anlamına gelir. Giderek daha fazla topun toplanmasının azalan getirileri üzerine hareket ederler. Bunun yerine bu, oyun ağacının tamamını analiz eder , 1000'den geriye doğru çalışır ve her oyunun nasıl sonuçlanacağına bağlı olarak gerçek değerleri doldurur.

Sonuç olarak, birkaç kodlanmış "açık" davranış dışında, hangi stratejinin kullanıldığı hakkında hiçbir fikrim yok (rakibinizin topları ve ördeklerinden daha fazla topunuz varsa kartopu atın ve ikiniz de bittiğinde yeniden yükleyin) kartopu). Birisi ürettiği veri setini analiz etmek isterse keşfedilecek ilginç davranışlar olduğunu hayal ediyorum!

Bunu "Save One" a karşı test etmek, uzun vadede gerçekten kazandığını, ancak sadece küçük bir farkla (514 galibiyet, 486 mağlubiyet, 0 1000 maçın ilk kümesinde 0 beraberlik ve 509 galibiyet, 491 mağlubiyet, 0 ikinci çizer).


Önemli!

Bu kullanıma hazır olacak, ancak bu iyi bir fikir değil. Oyun ağacının tamamını oluşturmak için orta derecede geliştirici özellikli dizüstü bilgisayarımda yaklaşık 9 dakika sürüyor. Ancak, son olasılıkları oluşturulduktan sonra bir dosyaya kaydeder ve ondan sonra her bir sıra rastgele bir sayı oluşturur ve 2 bayt ile karşılaştırır, bu yüzden süper hızlıdır.

Tüm bunları kısaltmak için, sadece bu dosyayı (3.5MB) indirin ve çalıştırılabilir klasöre yerleştirin.

Veya çalıştırarak kendiniz de oluşturabilirsiniz:

./pain_in_the_nash 1000 50 25

Yakınsama kadar, tur başına bir dosya kaydeder. Her dosyanın 3.5 MB olduğunu ve 720'de dönüştüğünü (yani 280 dosya, ~ 1 GB) ve çoğu oyunun 720 dönüşte yakın bir yerde bulunmadığından, yakınsama dosyalarının çok düşük bir öneme sahip olduğunu unutmayın.


Programın yalnızca nihai sonuç çıktısını alması mümkün mü? Teşekkürler!
HyperNeutrino

@HyperNeutrino diğer tüm çıktılar stderr olmalıdır, bu nedenle herhangi bir etkisi olmamalıdır, ancak yalnızca ön işleme modunda çalışırken ilerleme göstermek için güncellendi. Şimdi sadece normal çalışırken stdout'a yazacak. "Önemli" öneriyi takip etmeyi öneriyorum, çünkü aksi halde ilk turda birkaç dakika bekleyecek (en azından ön işleme ile ilerlemeyi görebilirsiniz).
Dave,

Tamam. Bu öneriyi takip edeceğim, teşekkürler!
HyperNeutrino

Veri dosyalarını yükleyebiliyorsanız sevinirim çünkü hepsini oluşturmak sonsuza kadar sürüyor. Bunu yapabilirseniz çok iyi olur :)
HyperNeutrino

@HyperNeutrino Tamam, korkunç internetime yüklemek de sonsuza dek sürdü, ancak 3.5MB birleştirilmiş dosya burada mevcut: github.com/davidje13/snowball_koth_pitn/blob/master/… (sadece aynı dizine koyun).
Dave,

1

Swift - TheCrazy_XcodeRandomness

Ne yazık ki, bu yalnızca yerel olarak, Xcode'da çalıştırılabilir, çünkü Foundationmodülü ve işlevini içerir arc4random_uniform(). Ancak, algoritmanın ne olduğunu hemen hemen söyleyebilirsiniz:

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
}

Bu Linux'ta bashtan çalıştırılabilir mi?
HyperNeutrino

@HyperNeutrino MacOS'ta yapabileceğini biliyorum, ama Linux'ta yapıp yapmadığını bilmiyorum. Bunu kontrol edebilirsen çok iyi olur. swiftKomutu deneyin ve çalışıp çalışmadığını kontrol edin
Bay Xcoder

Var görünmüyor; Yanında bir paket var ama bu Swift dili değil. Bu yüzden, çalışacak bir şey bulana kadar bunu test edemem, üzgünüm.
HyperNeutrino

mümkün olan tek derleyiciler Xcode ve IntelliJ'dir, ancak çevrimiçi olmaları nedeniyle Foundationözür dileriz: /
Bay Xcoder

Huzur içinde yatsın. Kumandayı çalıştırmak için komut satırından çalıştırabilmem gerekirdi, ancak zamanım varsa, diğer botların hepsinde manuel olarak tekrar çalıştırabilirim.
HyperNeutrino

1

TableBot, Python 2

Bu tabloyu uygulayarak oluşturulduğu için TableBot adı verildi:

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

Bir 1, 1 veya daha fazlasına sahip olmayı, 0 ise hiçbirine sahip olmadığını gösterir.

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

Çevrimiçi deneyin!


1

AmbBot - Raket Planı

Çoğunlukla kullanmayı denemek istedim amb, çünkü harika. Bu bot rastgele seçenekleri sıralar (yeniden yükleme, fırlatma ve ördek), mantıklı olmayanları filtreler ve ilk seçeneği seçer. Ancak ambsürekliliği ve geri izlemeyi kullanıyoruz!

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

Ayrıca bu botlardan ikisini birbirine karşı çalıştırmak için küçük bir test programı yaptım. İkinci bot daha sık kazanıyor gibi geliyor, bu yüzden bir yerde bir hata yapmış olabilirim.

(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 ++

Temel olarak kodu bu kothtan aldım ve bu zorluk için değiştirdim. Decoupled UCT Monte Carlo Ağacı Arama algoritmasını kullanır. Nash dengesine oldukça yakın olmalıdır.

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

Linux için Derleme Talimatları:

Kaydet MonteBot.cpp.
Run g++ -o -std=c++11 MonteBot MonteBot.cpp.

Çalıştırılacak komut: ./MonteBot <args>


1

Procrastinator - Python 3

Erteleme işlemi, ilk birkaç turu kurtarıp oynayarak erteleyecektir. Aniden panik canavarı, en çok kullanılan hamleyi sayarak kaynak savaşını kaybetmekten kaçınmak istiyor.

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

"Erteleme". Peki, aslında PPCG'de ev ödevi yapmak isteyen herkes? (İnkar etme, bunu okuyan insanlar ve ben.)
HyperNeutrino

1
"Anında Tatmin Maymun" Sen de TEDTalk’u gördün mü? :)
HyperNeutrino


0

ParanoidBot ve PanicBot - ActionScript3 ( RedTamarin )

Uygun olmayan bir şekilde, niş dilin (komut satırı argümanlarını sağlayacak uzantılara sahip), skittish ParanoidBot ve donuk müttefiki PanicBot'u selamlar.

ParanoidBot

ParanoidBot fikrini kaybediyor ve buna bağlı olarak ihtiyaç duyulması gereken spesifik bir stratejiye sahip. Birincisi, rezervini koruyarak eşiğe ulaşılana kadar kartopu fırlatır. Ardından, üç ihtiyatlı ördekten sonra paranoya devreye girer ve bot rastgele ördekler arasında daha fazla kartopu biriktirmeye çalışır. Arzunu doldurduktan sonra ParanoidBot kör atmaya geri döndü. Kafasındaki sesler yüzünden ParanoidBot kazanma ya da kaybetme garantisinin olup olmadığını söyleyebilir ve buna göre “strateji” yapar.

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

Parantezlerin boyutlarının yoğunlaşmasına yardımcı olmak için biraz garip

PanicBot

Zaten delirmiş olan PanicBot, içgüdüsel korkudan tepki veriyor. Ördeklerin korkudan korkmasıyla tükendikten sonra PanicBot, bütün kartoplarını kör bir şekilde atıyor, sonra (muhtemelen) mağlup olana kadar umutsuzca daha fazla kartopu yapıyor ve fırlatıyor.

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



Bu, burada PPCG'de AS3 kullanan 15'ten az giriş. Bir gün, belki de bu tartışmalı egzotik dil, egemen olacak bir bilmece bulacaktır.


Bu Linux'ta bashtan çalıştırılabilir mi?
HyperNeutrino

Bunu test etmedim, ama evet, öyle olmalı. RedTamarin çalıştırılabilir (redshell), Windows, Mac ve Linux için inşa edilmiştir: http://redtamarin.com/tools/redshell . Yukarıdaki botlardan biri adında bir dosyaya kaydedilmişse snow.as, aşağıdaki bash içerisinde çalışması gerekir:$ ./redshell snow.as -- 0 50 50 25 25

Bunu çalıştırmayı denediğimde hata bana izin vermedi.
HyperNeutrino

@HyperNeutrino chmod +x redshellburada senin arkadaşın ...
Outgolfer Erik

Belki 777 chmod her şey? RedTamarin web sitesinde de bazı sorun giderme işlemleri olabilir

0

Defans, Python

Hiçbir oyuncunun kartopu olmadığı zaman yeniden yükler. Kartopu varsa atar. Eğer kartopu yoksa, ancak rakip varsa, eğer yapabilirse ördektir, aksi halde yeniden yükler.

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:]]))

Not: henüz test edilmedi

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