눈싸움 KoTH!


35

결과 (2017 년 5 월 22 일 21:40:37 UTC)

Master18 라운드, 2 라운드 잃고 0 라운드
Save One15 라운드, 3 라운드 잃고 2 라운드
Machine Gun14 라운드, 3 라운드, 3 라운드
Monte Bot14 라운드, 3 라운드, 3 라운드
Amb Bot12 라운드 라운드, 8 라운드, 0 라운드
Coward는 11 라운드, 3 라운드, 6 라운드
Pain in the Nash는 11 라운드, 9 라운드, 0 라운드
Nece Bot는 10 라운드, 7 라운드, 3 라운드
Naming Things is Hard는 10 라운드, 7 라운드, 3 라운드
The Procrastinator10 라운드, 8 라운드, 2 라운드
Yggdrasil10 라운드, 10 라운드, 0 라운드
Simple Bot9 라운드, 4 라운드, 4 라운드 7 라운드
Table Bot9 라운드, 6 라운드 5 라운드
Prioritized Random Bot, 8 라운드, 7 라운드, 5 라운드
Upper Hand Bot7 라운드, 13 라운드, 공동 0 라운드
Aggressor6 라운드, 10 라운드, 4 라운드
Insane5 라운드, 15 라운드, 0 라운드
The Ugly Duckling4 라운드, 16 라운드, 0 라운드
Know Bot3 라운드 라운드, 14 라운드, 3 라운드 묶음
Paranoid Bot은 0 라운드, 19 라운드, 1 라운드
Panic Bot0 라운드, 19 라운드, 1 라운드

불행히도 Linux의 bash에서 실행할 수 없기 때문에 Crazy X-Code Randomess를 테스트 할 수 없습니다. 작동시킬 수 있으면 포함시킬 것입니다.

전체 컨트롤러 출력


게임

이것은 매우 간단한 KoTH 게임입니다. 일대일 눈싸움입니다. k눈덩이를 담을 수있는 초기에 빈 용기가 있습니다 . 당신은 최대 j몇 번 오리 . 매 턴마다 두 선수 모두 움직일 대상을 동시에 선택해야합니다. 세 가지 동작이 있습니다.

  • 다시로드 : 당신에게 또 다른 눈덩이를 제공합니다 (최대 k)
  • 던지기 : 눈덩이를 던져 다른 플레이어가 다시 장전하기로 결정하면 죽습니다. 두 선수가 모두 눈덩이를 던지면 아무도 죽지 않습니다.
  • duck : 다른 플레이어가 눈덩이를 던지면 아무 일도 일어나지 않으며 맞지 않습니다. 더 이상 오리가 없으면 아무 일도 일어나지 않으며 다른 플레이어가 눈덩이를 던지면 죽습니다.

목표

죽지 마

Challlenge 사양

프로그램은 모든 언어로 작성 될 수 있습니다. 각 실행에 대한 인수로 이러한 각 변수를 사용해야합니다.

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

turn-몇 차례의 턴이 경과했는지 ( 0첫 번째 반복에서)
snowballs-당신이 가진 눈덩이의 수
opponent_snowballs-상대가 가진 눈덩이의
ducks
opponent_ducks- 오리를 몇 번 더
max_snowballs오리고 가게 ( k)

주요 기능의 출력은 0재 장전, 1던지기 및 2오리를 위한 것이어야합니다 . 줄 바꿈이 종료 된 이동을 출력해야합니다. 유효하지 않은 동작은 출력하지 않지만 컨트롤러는 복원력이 뛰어나며 유효하지 않은 동작을 출력하더라도 (이동이 정수가 아닌 경우에도) 중단되지 않습니다. 그것은 있어야 하지만 줄 바꿈 종료합니다. 이동이에없는 경우 [0, 1, 2]기본 이동은 0입니다. 우승자는 전체 라운드 로빈 토너먼트에서 가장 많은 승리를 한 선수로 결정됩니다.

규칙

반복 사이의 메모리 저장을 위해 하나의 파일에서 읽거나 쓸 수 있습니다. 봇은 자체 디렉토리에 위치하므로 파일 이름 충돌이 발생하지 않습니다. 내장 함수 (예 : 임의 생성기)는 변경할 수 없습니다. 처음했을 때는 꽤 재밌었 지만 더 이상은 아닙니다. 당신의 프로그램은 실행을 멈추게하는 일을 할 수 없습니다. 표준 허점이 적용 됩니다.

테스팅

컨트롤러의 소스 코드는 여기 에서 찾을 수 있습니다 . 실행 예 : java Controller "python program1/test1.py" "python program2/test2.py" 10 510 개의 눈덩이와 5 개의 오리.

심사

우승자는 전체 라운드 로빈 후 가장 많은 승리를 한 사람을 선택하여 결정됩니다. 이것은 동점이지만, 가장 많은 승리를 얻지 못한 사람들을 모두 제거하십시오. 그런 다음 한 사람이 이길 때까지 반복하십시오. 심사 기준은 50 개의 눈덩이와 25 마리의 오리입니다.

행복한 KoTHing!

편집 : 1000 라운드가 지나면 게임은 동점으로 선언됩니다. 당신의 봇은 그것을 가정 할 수 있습니다 turn < 1000.


의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
데니스

@HyperNeutrino 추가 질문 : "판정 기준"이 50 개의 눈덩이와 25 개의 오리라고 생각하십니까? 그리고 ~ 18 라운드 후에 왜 무승부가 있습니까?
CommonGuy

@Manu Ehh crap 내 VM 인수의 설정을 변경하는 것을 잊었습니다. 또한, 그것이 눈덩이 충돌의 끝없는 루프에 들어가면,주기 1 또는주기 2 루프를 10 라운드 반복 한 후에 그것을 끝내기 때문입니다.
HyperNeutrino

1
그럼 또 다른 라운드가 있을까요? 내가 내 봇을 업로드하고 싶어하고 그가 얼마나 잘 작동하는지 궁금 할 것입니다.
erbsenhirn

@erbsenhirn 봇을 업로드하고 채팅 또는 Nineteenth Byte 에 핑을하면 다른 실행을 실행합니다.
HyperNeutrino

답변:


13

마스터, C #

나는 작은 신경망을 훈련시켰다 ( Sharpneat 사용 ). 눈덩이를 집어 들고 더킹을 좋아하는 것 같습니다 ...

이전 버전의 컨트롤러에서는 버그를 발견했습니다. 부정 행위로이기는 방법을 발견했을 때 0 %에서 100 %로 상승했습니다.

편집 : 네트워크 상호 상태를 재설정하고 네트워크를 잘못 훈련하는 것을 잊었습니다. 새로 훈련 된 네트워크는 훨씬 작습니다.

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
}

분명히 네트워크 상태를 재설정하면 성능이 훨씬 향상되었습니다 :)
HyperNeutrino

신경망을 무엇에 대해 훈련 했습니까? 다른 봇에 대해 여기에 게시 하시겠습니까?
JAD

@JarkoDubbeldam 예, 몇 개를 C #으로 이식하고 네트워크를 교육했습니다. 그렇기 때문에 아마도 새로운 봇에 대해 느슨해 질 것입니다.
CommonGuy

또는 봇과 다른 네트워크에 대해 다른 네트워크를 훈련하십시오 : p
JAD

왓. 신경망에 8 표!
Christopher

6

파이썬 하나 저장

대부분의 눈덩이를 즉시 던지지 만 상대방이 탄약이 부족한 것을 지켜보고있을 경우에는 항상 눈덩이를 절약합니다. 그런 다음 보장 된 안전한 재 장전 또는 보장 된 킬이없는 한, 재 장전 전에 가능한 한 오랫동안 (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 개의 눈덩이가 있으면 1을 던지려고합니다.
Carl Bosch

@CarlBosch는 불가능한 상태에 도달해야합니다 (0부터 시작). 그러나 그 경우를 다루기 위해 편집 할 것입니다.
SnoringFrog

2
@SnoringFrog 규칙을 명확히하기 위해, 당신은 0 눈덩이로 시작합니다
PhiNotPi

@PhiNotPi 나는 그것을 완전히 간과해야합니다. 설명 주셔서 감사합니다
SnoringFrog

6

우선 순위 랜덤 랜트, 자바

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

이 봇 선택하는 임의의 범위의 정수 0os + od다음과 눈덩이 오리의 현재 번호에 의해 결정되는 임계 값과, 어느 던져 오리 또는 리로드를 선택한다.

한 가지 봇이 다른 봇보다 눈덩이 + 오리보다 많은 눈덩이가 있으면 승리를 강요 할 수 있다는 사실을 알아야합니다. 이것으로부터 "포인트"라는 개념을 생각 해낼 수 있습니다.

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

이 숫자 중 하나가 양수가되면, 그 플레이어는 승리를 강요 할 수 있습니다.

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

"포인트 차이"표는이 경쟁에 대한 게임 이론의 기초를 형성합니다. 모든 정보를 완전히 포착하지는 못하지만 눈덩이가 오리보다 기본적으로 얼마나 가치가 있는지를 보여줍니다 (눈덩이는 공격과 방어 모두). 상대방이 눈덩이를 던지고 성공적으로 오리를 던지면 상대가 더 귀중한 자원을 사용했기 때문에 강제 승리에 한 걸음 더 다가 서게됩니다. 이 표에는 특정 이동 옵션을 사용할 수없는 경우와 같은 많은 특수한 경우에 수행해야 할 작업도 설명되어 있습니다.

"포인트 합계"테이블은 시간이 지남에 따라 포인트 합계가 0에 도달하는 방법을 보여줍니다 (두 플레이어 모두 오리가 부족할 때).이 시점에서 첫 번째 플레이어가 실수 (필요하지 않은 경우 다시로드)하는 즉시 진다.

이제이 강제 전략을 실제로 강제 할 수없는 경우로 확장 해 봅시다 (우리는 큰 마진으로 이기고 있지만 상대방의 마음 읽기는 우리를 이길 것입니다). 기본적으로, 우리는 s눈덩이 를 가지고 있지만이기려면 상대 s+1(또는 s+2등) 시간을 연속적으로 눈덩이로 만들어야합니다 . 이 경우 시간을 벌기 위해 몇 마리의 오리 또는 몇 가지 재 장전을 수행하려고합니다.

바로 지금,이 봇은 항상 일부 오리에서 몰래 빠져 나가기 때문에 즉각적인 손실이 발생할 위험이 없습니다. 우리는 상대방이 가능한 많은 눈덩이를 찌르는 비슷한 전략을 따르고 있다고 생각합니다. 위험한. 또한 예측 가능성을 방지하기 위해 균일하게 분포 된 분포에 따라이를 몰래 숨기려고합니다. 더킹 확률은 던질 눈덩이의 수와 관련하여 수행해야하는 오리의 수와 관련이 있습니다.

심하게 잃어버린 경우, s + d < os + od모든 오리를 사용하는 것 외에도 일부 재 장전에서 몰래 들어가야합니다.이 경우 무작위로 재 장전하고 싶습니다.

이것이 우리의 봇이 던지기, 오리, 다시로드 순서대로 우선 순위를 매기고 os + od난수를 생성하는 데 사용 됩니다. 왜냐하면 우리가해야 할 임계 값 수이기 때문입니다.

봇이 현재 처리하는 하나의 엣지 케이스와 다른 두 가지 특수 케이스가 있습니다. 에지 케이스는 상대방이 눈덩이 나 오리가 없기 때문에 무작위 화가 작동하지 않으므로 가능한 경우 던지십시오. 그렇지 않으면 다시로드합니다. 또 하나의 특별한 경우는 상대방이 재 장전 할 수없고 던지기에 대한 이점이 없기 때문에 (상대자가 오리를 던지거나 던지기 때문에) 항상 오리입니다 (눈덩이를 절약하는 것이 오리를 저장하는 것보다 더 중요하기 때문에). 마지막 특수한 경우는 상대방에게 눈덩이가없는 경우입니다.이 경우 우리는 안전하게 플레이하고 가능한 경우 다시 장전합니다.


이로 인해 여러 숫자가 인쇄되어 제대로 작동하지 않을 수 있습니다.
HyperNeutrino

@HyperNeutrino이 봇을 return to print 문을 사용하여 다시 작성할 때 "else"블록을 추가하는 것을 잊었습니다.
PhiNotPi

1
@HyperNeutrino 그것은 나를 위해 일했고, 나는 그것을 버그로 생각했습니다 ...
Outgolfer Erik

아 예, 코드를 엉망으로 만들어서 죄송합니다. : P 그러나 임의성을 사용하는 멋진 첫 번째 프로그램!
HyperNeutrino

6

NeceBot-파이썬

게임에 대한 게임 이론 테이블은 다음과 같습니다.

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

여기서 ~이점 W이 없고, 이기고, L잃는다 +-S는 것은 눈싸움이 상대방보다지고 나가는 +-D것을 의미하며, 오리가 상대방보다지고 나가는 것을 의미합니다. 이것은 완전히 대칭적인 게임입니다.

내 솔루션은 해당 테이블을 고려하지 않습니다. 나는 수학에 나쁘기 때문에.

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

먼저 필요한 것을 줄이려고하기 때문에 NeceBot이라고합니다. 그 후에는 임의의 전략이 있습니다.


4
너무 많은 <3s lol. 게임 테이블을 가지고 그것을 사용하지 않은 +1 : P 그러나 멋진 해결책 :)
HyperNeutrino

3 + opponent_snowballs <3이것은 실수일까요?
PhiNotPi

@PhiNotPi 응. 2가 되었음
Artyer

불행히도 많은 수의 <3코드는 코드를 이해하기 어렵게 만듭니다 :(
CalculatorFeline

5

겁쟁이-스칼라

상대방에게 탄약이 없으면 던지기, 그렇지 않으면 (우선 순위에 따라) 오리, 던지거나 다시로드합니다.

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

이것이 내 봇을
막는

5

TheUglyDuckling-Python

상대가 비어 있으면 던질 수 없을 때까지 항상 오리를 던지거나 비어 있으면 다시로드합니다. 최후의 수단으로 다시로드를 사용합니다.

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-파이썬 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

간단한 것들.

  • 상대방에게 눈덩이가 있고 오리가 있다면 오리입니다.
  • 상대방에게 눈덩이가없고 눈덩이가 있으면 던집니다.
  • 다른 경우에는 다시로드하십시오.

5

이름 지정이 어려운 봇-VB.NET

이름을 짓는 것은 어렵고 이름을 붙일 응집력있는 전략이 있는지 확실하지 않습니다.

초기 승리를 위해 처음 몇 라운드를 도박하려고합니다. 그 후, 남은 시간 동안 더 안전하게 플레이하고, 이겨서 승리합니다.

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

적을 죽일 것이라고 보장 될 때까지 또는 오리에서 나올 때까지 눈덩이를 구하려고 시도합니다 (이 경우 기관총과 같은 모든 눈덩이를 맹목적으로 발사하기 시작합니다)

또한 상대방이 눈싸움을 할 때마다 오리를 피우기 위해 오리를 피 웁니다.

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

이전 움직임의 빈도를 추적하고 상대방이 가장 자주 행동한다고 ​​가정하고 그로부터 방어합니다.

** 상대가 할 수없는 움직임을 기대하지 않도록 업데이트 **

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

정확히 어떻게 작동하는지 잘 모르겠지만, 라운드 사이에 데이터를 저장하는 경우 (반복이 아닌) 불행히도 모든 데이터는 라운드 사이에서 삭제됩니다. 그것은 당신의 해결책을 무효화하지 않지만 단지 명심하십시오 :)
HyperNeutrino

현재 상대가 일관성을 유지하기 위해 라운드 사이에 데이터를 유지할 것으로 기대하지 않습니다.
AShelly

좋구나. 괜찮아. 나는 단지 오해가 없는지 확인하고 싶었습니다. :)
HyperNeutrino

4

Braingolf , 침략자

<<?1:0

침략자는 겁쟁이가 아닙니다! 눈덩이가 있으면 던질 것입니다! 눈덩이가 없다면 더 많이 만들 것입니다!

브레인 골프 , 제정신

이것은 실제로 봇이 아니며, 내가 만든 모든 프로젝트를 납치하여 브레인 골프에 강제로 이식 한 프로그래머 일뿐입니다. 그는 더 이상 정신 이상을 잃지 않았다.

<3r!?:1+|%

3보다 작은 난수를 생성하고 t % r, t는 현재 회전이고 r은 난수입니다.

이것을 실행하려면 braingolf.pygithub에서 다운로드 한 다음 braingolf 코드를 파일에 저장하고 실행해야합니다

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

또는 다음과 같이 코드를 직접 삽입하십시오.

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

코드 / 파일 이름 다음의 두 번째 인수가 침략자가 가지고있는 눈덩이의 양이면 입력은 관련이 없습니다.

참고 : 침략자는 실제로 TestBot과 동일하게 작동합니다. 방금 braingolf에 입장하고 싶었습니다.

Braingolf , The Brainy [지금 고장난]

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

물론 누군가는 이것을해야했습니다 : D 니스, 심지어 골프! : D
HyperNeutrino

아 잠깐만, 고 피어를 제외하고는 내 것과 같아 lol
HyperNeutrino

@HyperNeutrino yup, 저는 실제 언어로 실제 작업을하고 있습니다. 나는 Braingolf를 실용적으로 사용하지만, 중첩 된 조건을 수행 할 수 없으므로 상황이 어려워진다
Skidsdev

2
"The Brainy"를 별도의 답변으로 게시해야한다고 생각합니다. 또한 오류가 있다고 생각합니다.
아웃 골퍼 Erik 14

"The Insane"은 안정적인 봇이 아니므로 @HyperNeutrino가 어떻게 그것을 확인할지 잘 모르겠습니다.
아웃 골퍼 Erik 14

3

TestBot-파이썬

유효한 제출 방법을 보여주는 테스트 제출입니다. 전략 : 대체 재 장전 및 투척. 상당히 나쁜 전략이지만 프로그램 작동 방식에 대한 아이디어를 제공합니다.

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

시겠습니까 _, turn, snowballs, opponent_snowballs, ducks, opponent_ducks, max_snowballs = sys.argv인수 할?
Artyer

@Artyer 예. 첫 번째 인수는 파일 이름을 가지고 있습니다.
HyperNeutrino

당신이 그걸 sys.argv[1:]_
망치고

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)

이 봇은 상대보다 더 많은 눈덩이를 수집하려고 시도하고 그 시점에서 던지기 시작합니다. 어떤 시점에서 UHB에 상대보다 더 많은 눈덩이가 없으면 다음과 같습니다.

  • 오리, 상대방에게 눈덩이가 있고 오리가 남은 경우
  • 그렇지 않으면 다시로드하십시오 (UHB가 최대가 아닌 한이 상황이 발생하지 않을 것이라고 생각하지만 대신 던집니다)

2

이그 드라 슬라, 자바

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

나는이 봇을 "Yggdrasil"이라고 불렀습니다. 실제로 게임 트리를 내려다보고 상태 평가를 수행하기 때문에 대략 이상적인 혼합 전략을 계산할 수 있습니다. 이 혼합 전략에 의존하기 때문에, 그것은이다 매우 비 결정적. 나는이 일이 실제 경쟁에서 얼마나 잘 될지 모른다.

이 봇에 대한 몇 가지 사항 :

  • 핵심은 특정 게임 상태에 대한 가치와 이상적인 혼합 전략을 계산하는 재귀 함수입니다. 지금은 4 단계 앞당겨 보이도록 설정했습니다.
  • 많은 경우에이 봇은 "가위 바위 보에서 임의의 움직임을 고르는"것과 동일하기 때문에 극도로 약합니다. 그것은 자신의 입장을 고수하고 상대방이 통계적 이점을 제공하기를 희망합니다. 만약이 봇이 완벽하지 않다면, 당신이 할 수있는 최선의 방법은 50 %의 승리와 50 %의 손실입니다. 결과적으로, 지속적으로 이길 상대는 없으며, 또한 지속적으로지는 것도 없습니다.

나는 아직도 그 이름을 이해하지 못한다 ... : P
HyperNeutrino

@HyperNeutrino Yggdrasil 은 신화적인 나무이며,이 경우 게임 트리를 참조합니다.
PhiNotPi

아 맞다. 나는 이것을 기억해야한다고 느낀다. : P 멋지다!
HyperNeutrino

2

내쉬의 고통 (C ++)

내 자신의 내쉬 평형 솔버를 작성해야한다는 사실은 정말 고통 스러웠습니다. 쉽게 이용할 수있는 내쉬 해결 라이브러리가 없다는 것이 놀랍습니다!

#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 이상으로 컴파일하십시오. 성능을 위해 OpenMP 지원으로 컴파일하는 것이 좋습니다 (그러나 이것은 단지 속도를위한 것이므로 필수는 아닙니다)

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

이것은 매 차례마다해야 할 일을 결정하기 위해 내쉬 평형을 사용합니다. 이는 이론적으로 상대방이 어떤 전략을 사용하든 장기적으로 (많은 게임에서) 승리하거나 추첨 한다는 것을 의미 합니다. 실제로 그러한 경우는 구현에서 실수를했는지 여부에 달려 있습니다. 그러나이 KoTH 경쟁은 각 상대에 대해 단일 라운드 만 있기 때문에 리더 보드에서는 그다지 좋지 않을 것입니다.

내 원래 아이디어는 각 게임 상태 (예 : 각 공의 가치 + b, 각 오리의 가치 + d)에 대해 간단한 평가 기능을 사용하는 것이었지만, 이러한 평가가 무엇인지 알아내는 데 명백한 문제가 생겨 불가능합니다. 점점 더 많은 공을 모으는 것의 수익 감소에 대해 행동하십시오. 대신, 이것은 전체 게임 트리를 분석하고 1000 턴에서 거꾸로 작동하며 각 게임이 어떻게 펼쳐질 수 있는지에 따라 실제 평가를 채 웁니다.

결과적으로, 몇 가지 하드 코딩 된 "명백한"행동을 제외하고는이 전략이 어떤 전략을 사용하는지 전혀 알지 못합니다. 눈덩이). 누구나 데이터 세트를 분석하고 싶다면 생성해야 할 흥미로운 행동이 있다고 생각합니다!

"Save One"에 대해 테스트하면 장기적으로 실제로 승리하지만 작은 마진 (514 승, 486 패, 1000 게임의 첫 번째 배치에서 0 무승부, 509 승, 491 패, 0 두 번째에 그립니다).


중대한!

이것은 기본적으로 작동하지만 좋은 생각은 아닙니다. 전체 게임 트리를 생성하려면 적당히 개발자 사양의 노트북에서 약 9 분이 걸립니다. 그러나 최종 확률은 일단 생성되면 파일에 저장되며 그 후에는 각 숫자가 임의의 숫자를 생성하고 2 바이트와 비교하기 때문에 매우 빠릅니다.

바로 가기 위해서는 이 파일 (3.5MB)을 다운로드 하여 실행 파일이있는 디렉토리에 넣으십시오.

또는 다음을 실행하여 직접 생성 할 수 있습니다.

./pain_in_the_nash 1000 50 25

수렴 할 때까지 한 턴에 하나의 파일을 저장합니다. 각 파일은 3.5MB이며 720 번에 수렴 될 것입니다 (예 : 280 개 파일, ~ 1GB). 대부분의 게임은 720 번 근처에 오지 않으므로 사전 수렴 파일의 중요성은 매우 낮습니다.


프로그램이 최종 결과 만 출력하도록 할 수 있습니까? 감사!
HyperNeutrino

@HyperNeutrino 다른 모든 출력은 stderr에 있어야하므로 영향을 미치지 않아야하지만 전처리 모드에서 실행할 때 진행률 만 표시하도록 업데이트했습니다. 이제 정상적으로 실행될 때만 stdout에 씁니다. 그래도 "중요한"제안을 따르는 것이 좋습니다. 그렇지 않으면 첫 번째 턴에서 몇 분 동안 중단됩니다 (적어도 전처리를 통해 진행 상황을 볼 수 있음).
Dave

오 알았어 나는 그 제안을 따를 것입니다, 감사합니다!
HyperNeutrino

데이터 파일을 모두 생성하는 데 시간이 오래 걸리기 때문에 데이터 파일을 업로드 할 수 있다면 고맙겠습니다. 그렇게 할 수 있다면 그것은 좋을 것입니다 :)
HyperNeutrino

@HyperNeutrino OK, 그것은 또한 끔찍한 인터넷에 업로드하는 데 영원히 걸렸지 만 3.5MB 수렴 파일은 여기에서 사용할 수 있습니다 : github.com/davidje13/snowball_koth_pitn/blob/master/… (단지 동일한 디렉토리에 두십시오).
Dave

1

스위프트-TheCrazy_XcodeRandomness

슬프게도, 이것은 Xcode에서 로컬로만 실행할 수 있습니다. Foundation모듈과 기능이 포함되어 있기 때문 arc4random_uniform()입니다. 그러나 알고리즘이 무엇인지 거의 알 수 있습니다.

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
}

Linux의 bash에서 실행할 수 있습니까?
HyperNeutrino

@HyperNeutrino macOS에서는 가능하지만 Linux에서는 가능하다는 것을 모르겠습니다. 그것을 확인할 수 있다면 좋을 것입니다. 시도 swift명령을하고 작동하는 경우 다음 검사
씨 Xcoder

존재하지 않는 것 같습니다. 패키지가 있지만 언어는 스위프트가 아닙니다. 그래서 나는 무언가를 얻을 수있을 때까지 이것을 테스트하지 않을 수 있습니다.
HyperNeutrino

유일하게 가능한 complier는 Xcode와 IntelliJ이지만 Foundation, 죄송합니다 : /
Mr. Xcoder

삼가 고인의 명복을 빕니다. 명령 줄에서 컨트롤러를 실행하여 컨트롤러를 실행할 수 있어야하지만 시간이 있으면 다른 모든 봇을 수동으로 다시 실행할 수 있습니다.
HyperNeutrino

1

TableBot, Python 2

이 테이블을 구현하여 작성되었으므로 TableBot을 호출했습니다.

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

1은 1 이상을 나타내고, 0은 없음을 나타낸다.

봇 :

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

온라인으로 사용해보십시오!


1

AmbBot-라켓 구성표

나는 amb시원하기 때문에 주로 사용하려고했습니다 . 이 봇은 무작위로 옵션 (재 장전, 던지기 및 덕)을 주문하고, 이해가되지 않는 옵션을 걸러 내고 첫 번째 옵션을 선택합니다. 그러나에 amb, 우리는 연속성을하고 되돌아을 사용하여 얻을!

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

또한이 두 개의 봇을 서로 실행하기위한 작은 테스트 프로그램을 만들었습니다. 두 번째 봇이 더 자주 승리하는 것처럼 느껴지므로 어딘가에 실수를했을 수 있습니다.

(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

몬테 봇, C ++

나는 기본적 으로이 koth 에서 코드를 가져 와서이 도전을 위해 수정했습니다. Decoupled UCT Monte Carlo Tree Search 알고리즘을 사용합니다. 그것은 내쉬 평형에 꽤 가까워 야합니다.

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

리눅스 컴파일 지침 :

에 저장하십시오 MonteBot.cpp.
를 실행하십시오 g++ -o -std=c++11 MonteBot MonteBot.cpp.

실행할 명령 : ./MonteBot <args>


1

미루는 - 파이썬 3

미루는 사람은 처음 몇 턴을 저장하여 미루게됩니다. 갑자기 공황 몬스터는 가장 많이 사용되는 적을 상대로 자원 전쟁을 잃지 않기를 원합니다.

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

"중재자". 실제로 PPCG의 모든 사람들이 실제로 숙제를하려고합니까? (이것을 읽는 사람들과 저를 부인하지 마십시오.)
HyperNeutrino

1
"Inttant Gratification Monkey"TEDTalk도 보셨나요? :)
HyperNeutrino


0

ParanoidBot 및 PanicBot- ActionScript3 ( RedTamarin )

부적합한 틈새 언어 (명령 줄 인수를 제공하는 확장 기능 포함)는 교묘 한 ParanoidBot와 그의 맹렬한 동맹국 PanicBot을 환영합니다.

편집증 봇

ParanoidBot은 마음을 잃고 있으며 불필요하게 특정 전략에 의존하고 있습니다. 먼저 임계 값에 도달 할 때까지 눈덩이를 발사하여 일부를 준비합니다. 그런 다음, 3 마리의주의 오리 후 편집증이 시작되고 봇은 무작위 오리 사이에 더 많은 눈덩이를 비축하려고 시도합니다. 공급을 보충 한 후, ParanoidBot는 맹목적으로 던졌습니다. 머리 속의 목소리로 인해 ParanoidBot은 승패가 보장되는지 알 수 있으며 그에 따라 "전략"을 세울 수 있습니다.

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

중괄호는 크기를 응축하는 데 도움이됩니다.

패닉 봇

이미 미쳐 버린 PanicBot은 본능적 인 두려움에서 반응합니다. 겁에 질려 오리가 부족한 후, PanicBot은 모든 눈덩이를 맹목적으로 던지고 (아마도) 패배 할 때까지 필사적으로 더 많은 눈덩이를 만들어 던졌습니다.

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



이것은 PPCG에서 AS3을 사용하는 15 개 미만의 다른 항목 중 하나입니다. 언젠가이 이국적인 언어가 지배적 인 퍼즐을 발견 할 것입니다.


Linux의 bash에서 실행할 수 있습니까?
HyperNeutrino

나는 그것을 테스트하지 않았지만, 그렇습니다. RedTamarin 실행 파일 (redshell) 윈도우, 맥, 리눅스 용 내장되어 있습니다 : http://redtamarin.com/tools/redshell . 위의 봇 중 하나 snow.as$ ./redshell snow.as -- 0 50 50 25 25

이것을 실행하려고 할 때 권한 거부 오류가 발생합니다.
HyperNeutrino

@HyperNeutrino chmod +x redshell는 당신의 친구입니다 ...
Outgolfer Erik

아마 chmod 777 전부? RedTamarin 웹 사이트에도 문제가있을 수 있습니다.

0

수비수, 파이썬

플레이어가 눈덩이를 가지고 있지 않으면 다시로드합니다. 눈덩이가 있으면 던집니다. 눈덩이는 없지만 상대방은 눈덩이를 가지고 있고, 가능하다면 다시 오리로 돌아갑니다.

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

참고 : 아직 테스트되지 않았습니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.