สโนว์บอลต่อสู้ KoTH!


35

ผลลัพธ์ (22 พฤษภาคม 2560 21:40:37 UTC)

Masterได้รับรางวัล 18 รอบแพ้ 2 รอบและผูก 0 รอบ
Save Oneชนะ 15 รอบแพ้ 3 รอบและผูก 2 รอบ
Machine Gunชนะ 14 รอบแพ้ 3 รอบแพ้ 3 รอบและผูก 3 รอบ
Monte Botชนะ 14 รอบแพ้ 3 รอบและผูก 3 รอบ
Amb Botชนะ 12 รอบแพ้ 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 Procrastinatorชนะ 10 รอบแพ้ 8 รอบและผูก 2 รอบ
Yggdrasilชนะ 10 รอบแพ้ 10 รอบแพ้ 10 รอบและผูก 0 รอบ
Simple Botชนะ 9 รอบแพ้ 4 รอบและผูก 7 รอบ
Table Botชนะ 9 รอบแพ้ 6 รอบ รอบและผูก 5 รอบ
Prioritized Random Botชนะ 8 รอบแพ้ 7 รอบและผูก 5 รอบ
Upper Hand Botชนะ 7 รอบ, แพ้ 13 รอบ, และผูก 0 รอบ
Aggressorชนะ 6 รอบ, แพ้ 10 รอบ, และผูก 4 รอบ
Insaneชนะ 5 รอบ, แพ้ 15 รอบ, และผูก 0 รอบ
The Ugly Ducklingชนะ 4 รอบ, แพ้ 16 รอบ, และผูก 0 รอบ
Know Botชนะ 3 รอบ, แพ้ 14 รอบ, และผูก 3 รอบ
Paranoid Botชนะ 0 รอบ, แพ้ 19 รอบ, และผูก 1 รอบ
Panic Botชนะ 0 รอบ, แพ้ 19 รอบ, และผูก 1 รอบ

น่าเสียดายที่ฉันไม่สามารถทดสอบการสุ่มรหัส The Crazy X-Code เพราะฉันไม่สามารถเรียกใช้จากการทุบตีบน Linux ฉันจะรวมมันหากฉันสามารถทำให้มันทำงานได้

เอาต์พุตคอนโทรลเลอร์แบบเต็ม


เกม

นี่เป็นเกม KoTH ที่ง่ายมาก มันคือการต่อสู้ก้อนหิมะแบบหนึ่งต่อหนึ่ง คุณมีภาชนะที่ว่างในตอนแรกที่สามารถเก็บkก้อนหิมะได้มาก คุณสามารถมุดjครั้ง ทุกเทิร์นผู้เล่นทั้งสองจะถูกขอให้เลือกพร้อมกันสำหรับสิ่งที่เคลื่อนไหว มีสามการเคลื่อนไหว:

  • โหลด: ช่วยให้คุณก้อนหิมะอื่น (ขึ้นไปk)
  • การโยน: โยนก้อนหิมะซึ่งจะฆ่าผู้เล่นคนอื่นหากพวกเขาตัดสินใจที่จะบรรจุซ้ำ หากผู้เล่นทั้งสองโยนลูกบอลหิมะไม่มีใครตาย (พวกเขามีจุดมุ่งหมายที่ดีเช่นนั้นเพื่อชนลูกบอลหิมะของกันและกัน)
  • เป็ด: ไม่ทำอะไรเลยและหลีกเลี่ยงการโดนถ้าผู้เล่นคนอื่นขว้างก้อนหิมะ หากคุณไม่มีเป็ดเหลืออยู่เลยไม่มีอะไรเกิดขึ้นและถ้าผู้เล่นคนอื่นขว้างก้อนหิมะคุณก็ตาย

วัตถุประสงค์

อย่าตาย

ข้อมูลจำเพาะของ 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 5สำหรับลูกบอลหิมะ 10 ลูกและเป็ด 5 ลูก

การตัดสิน

ผู้ชนะจะได้รับการตัดสินโดยการเลือกบุคคลที่มีผู้ชนะมากที่สุดหลังจากรอบเต็มรูปแบบ ในขณะนี้คือเสมอกันเอาคนทั้งหมดที่ไม่มีชนะมากที่สุด จากนั้นทำซ้ำจนกว่าจะมีคนชนะ มาตรฐานการตัดสินจะเป็นลูกบอลหิมะ 50 ลูกและเป็ด 25 ตัว

ขอให้มีความสุข!

แก้ไข : เกมจะได้รับการประกาศเสมอถ้า 1,000 รอบผ่าน turn < 1000บอทของคุณอาจคิดว่า


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
เดนนิส

@HyperNeutrino คำถามเพิ่มเติม: ฉันคิดว่า "มาตรฐานการตัดสิน" จะเป็นลูกบอลหิมะ 50 ลูกและเป็ดอีก 25 ตัว? และทำไมบางครั้งถึงมีการออกรอบ ~ 18 รอบ?
CommonGuy

@Manu Ehh อึฉันลืมเปลี่ยนการตั้งค่าในอาร์กิวเมนต์ VM ของฉัน และนั่นก็เป็นเพราะถ้าพวกเขาเข้าสู่การวนรอบของการชนกันของก้อนหิมะมันจะจบลงหลังจากผ่าน 10 รอบของการวนซ้ำช่วงเวลา 1 หรือจุด -2
HyperNeutrino

1
ดังนั้นจะมีรอบอื่นหรือไม่ เพราะฉันต้องการอัพโหลดบอทของฉันและอยากรู้ว่าเขาจะทำงานได้ดีแค่ไหน
erbsenhirn

@erbsenhirn หากคุณอัปโหลดบอทและ ping ฉันในการแชทหรือบน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

ประหยัดหนึ่ง Python

ขว้างก้อนหิมะส่วนใหญ่ทันที แต่จะเก็บไว้ในกรณีที่ฝ่ายตรงข้ามมองหากระสุนไม่เพียงพอ จากนั้นเป็ดจะยาวที่สุด (อีกครั้งประหยัด 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

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

บอตนี้เลือกจำนวนเต็มแบบสุ่มในช่วง0ไปos + 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   +    +   ++

ตาราง "ความแตกต่างของคะแนน" เป็นรากฐานของทฤษฎีเกมสำหรับการแข่งขันครั้งนี้ มันไม่ได้ค่อนข้างเก็บข้อมูลทั้งหมด แต่มันแสดงให้เห็นว่าก้อนหิมะมีคุณค่ามากกว่าเป็ดอย่างมาก (เนื่องจากก้อนหิมะมีทั้งความผิดและการป้องกัน) หากฝ่ายตรงข้ามขว้างก้อนหิมะและคุณประสบความสำเร็จในการหลบหนีคุณก็จะเข้าใกล้ชัยชนะหนึ่งครั้งเนื่องจากฝ่ายตรงข้ามของคุณใช้ทรัพยากรที่มีค่ามากขึ้น ตารางนี้ยังอธิบายสิ่งที่คุณควรทำในกรณีพิเศษมากมายเช่นเมื่อตัวเลือกการย้ายบางอย่างไม่พร้อมใช้งาน

ตาราง "ผลรวมของคะแนน" แสดงให้เห็นว่าเมื่อเวลาผ่านไปผลรวมของคะแนนเข้าใกล้ศูนย์ (ในขณะที่ผู้เล่นทั้งสองหมดเป็ด) ซึ่งเป็นจุดที่ผู้เล่นคนแรกทำผิด (โหลดซ้ำเมื่อไม่จำเป็น) สูญเสีย

ตอนนี้เราลองขยายกลยุทธ์บังคับนี้ไปยังกรณีที่มันไม่ได้บังคับจริง (อย่างเช่นเรากำลังชนะด้วยกำไรที่มาก แต่การอ่านใจในส่วนของฝ่ายตรงข้ามจะเอาชนะเรา) โดยทั่วไปเรามีสsโนว์บอล แต่จำเป็นต้องสโนว์บอลคู่ต่อสู้ของเราs+1(หรือs+2อื่น ๆ ) เวลาต่อเนื่องที่จะชนะ ในกรณีนี้เราต้องการแสดงเป็ดสองสามตัวหรือสองสามโหลดเพื่อซื้อเวลา

ตอนนี้บอทนี้มักจะแอบเข้าไปในเป็ดบางตัวเพราะมันไม่เสี่ยงต่อการสูญเสียทันที: เราคิดว่าฝ่ายตรงข้ามกำลังทำตามกลยุทธ์ที่คล้ายกันกับการสโนว์บอลก้อนหิมะให้มากที่สุดเท่าที่จะทำได้ เป็นอันตราย นอกจากนี้เพื่อป้องกันการคาดการณ์เราต้องการแอบตามการกระจายแบบสุ่ม: ความน่าจะเป็นของการเป็ดเกี่ยวข้องกับจำนวนเป็ดที่เราต้องแสดงเมื่อเทียบกับจำนวนลูกหิมะที่เราต้องโยน

หากเราแพ้อย่างหนักในกรณีนี้s + d < os + odเราจำเป็นต้องแอบดูใน reloads บางส่วนนอกเหนือจากการใช้เป็ดของเราทั้งหมดในกรณีนี้เราต้องการโหลดแบบสุ่ม แต่มีหลายครั้งเท่าที่เราต้องการ

นี่คือเหตุผลที่บอตของเราจัดลำดับความสำคัญตามลำดับการโยนเป็ดและโหลดและใช้os + odเพื่อสร้างหมายเลขสุ่มเนื่องจากเป็นจำนวนเกณฑ์การย้ายที่เราต้องทำ

มีกรณีขอบหนึ่งกรณีและอีกสองกรณีพิเศษที่บอทจัดการอยู่ในปัจจุบัน กรณีขอบคือเมื่อฝ่ายตรงข้ามไม่มีลูกบอลหิมะหรือเป็ดและการสุ่มจึงไม่ทำงานดังนั้นเราจึงโยนถ้าเป็นไปได้มิฉะนั้นเราจะโหลดซ้ำ อีกกรณีพิเศษคือเมื่อฝ่ายตรงข้ามไม่สามารถรีโหลดและดังนั้นจึงไม่มีประโยชน์ในการขว้าง (เนื่องจากฝ่ายตรงข้ามจะโยนหรือโยน) ดังนั้นเราจึงมักจะเป็ด (เนื่องจากการบันทึกก้อนหิมะของเรามีค่ามากกว่าการรักษาเป็ดของเรา) กรณีพิเศษขั้นสุดท้ายคือถ้าฝ่ายตรงข้ามไม่มีก้อนหิมะซึ่งในกรณีนี้เราเล่นอย่างปลอดภัยและโหลดซ้ำถ้าเป็นไปได้


นี่อาจเป็นการพิมพ์หลายหมายเลขซึ่งอาจทำงานไม่ถูกต้อง
HyperNeutrino

@HyperNeutrino ฉันลืมเพิ่มบล็อก "else" เมื่อฉันเขียน bot นี้อีกครั้งจากการใช้ return to print statement
PhiNotPi

1
@HyperNeutrino มันทำเพื่อฉันและฉันคิดว่ามัน
บั๊ก

อา ใช่ขอโทษที่ทำให้รหัสของคุณยุ่งเหยิง: P แต่ดีโปรแกรมแรกที่ใช้การสุ่ม!
HyperNeutrino

6

NeceBot - Python

นี่คือตารางทฤษฎีเกมสำหรับเกม:

        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
Whee จำนวนมากดังนั้น<3s ฮ่า ๆ +1 สำหรับการมีตารางเกมและไม่ใช้: P แต่วิธีแก้ปัญหาที่ดี :)
HyperNeutrino

3 + opponent_snowballs <3นี่อาจเป็นข้อผิดพลาดหรือไม่?
PhiNotPi

@PhiNotPi Yup หมายถึงเป็น 2 แก้ไขแล้วขอบคุณ!
2560

แต่น่าเสียดายที่จำนวนมาก<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)
}

ดูเหมือนว่าที่นี้ติดขัดบอทของฉัน ...
เอริก Outgolfer

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

เรื่องง่าย ๆ

  • หากฝ่ายตรงข้ามมีลูกบอลหิมะและคุณมีเป็ดแล้วคุณจะเป็ด
  • หากฝ่ายตรงข้ามไม่ได้มีก้อนหิมะและคุณมีแล้วคุณจะโยน
  • ในกรณีอื่นคุณโหลดซ้ำ

5

Bot ตั้งชื่อสิ่งที่เป็นเรื่องยาก - 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

Alright ถูก ฉันแค่ต้องการทำให้แน่ใจว่าไม่มีการเข้าใจผิด :)
HyperNeutrino

4

Braingolfผู้รุกราน

<<?1:0

ผู้รุกรานไม่ใช่คนขี้ขลาด! หากเขามีก้อนหิมะเขาจะขว้าง! ถ้าเขาไม่มีก้อนหิมะเขาก็จะทำมากขึ้น!

Braingolfบ้า

นี่ไม่ใช่บอทจริงๆมันเป็นเพียงโปรแกรมเมอร์ที่ฉันถูกลักพาตัวและถูกบังคับให้ย้ายทุกโครงการที่เขาเคยทำเพื่อ braingolf เขาไม่ได้เป็นคนขาดสติอีกต่อไป

<3r!?:1+|%

สร้างตัวเลขสุ่มน้อยกว่า 3 และเอาท์พุทt % rโดยที่ t คือเทิร์นปัจจุบันและ r คือตัวเลขสุ่ม

ในการเรียกใช้สิ่งเหล่านี้คุณจะต้องดาวน์โหลดbraingolf.pyจาก github จากนั้นบันทึกรหัส braingolf ไปยังไฟล์และเรียกใช้

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

หรือใส่รหัสโดยตรงเช่นนี้

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

อินพุตไม่เกี่ยวข้องอย่างเป็นธรรมตราบใดที่อาร์กิวเมนต์ที่ 2 หลังจากรหัส / ชื่อไฟล์คือจำนวนของก้อนหิมะที่ผู้รุกรานมี

หมายเหตุ: ผู้รุกรานทำตัวเหมือน TestBot จริง ๆ แล้วฉันแค่อยากจะทำรายการกับ braingolf

Braingolf , The Brainy [Broken ตอนนี้]

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

โอ้เดี๋ยวก่อนนี่ก็เหมือนกับของฉันยกเว้น gofier lol
HyperNeutrino

@HyperNeutrino yup, ฉัน gunna ทำงานจริงในภาษาจริงตอนนี้ ฉันจะใช้ Braingolf สำหรับของจริง แต่มันไม่สามารถทำเงื่อนไขที่ซ้อนกันดังนั้นสิ่งที่ทำให้ยาก
Skidsdev

2
ฉันคิดว่าคุณควรโพสต์ "The Brainy" เป็นคำตอบแยกต่างหาก นอกจากนี้ฉันคิดว่ามันผิดพลาด
Erik the Outgolfer

"The Insane" ไม่ใช่บอทที่มีความเสถียรดังนั้นฉันไม่แน่ใจว่า @HyperNeutrino จะตรวจสอบได้อย่างไร
Erik the Outgolfer

3

TestBot - Python

นี่คือการส่งการทดสอบเพื่อแสดงให้คุณเห็นว่าการส่งที่ถูกต้องอาจมีลักษณะเป็นอย่างไร กลยุทธ์: การโหลดและการโยนสำรองแบบอื่น ค่อนข้างเป็นกลยุทธ์ที่ไม่ดี แต่มันทำให้คุณเข้าใจว่าโปรแกรมของคุณควรทำงานอย่างไร

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เป็นข้อโต้แย้ง?
2560

@Artyer ใช่ ปรากฎว่าอาร์กิวเมนต์แรกมีชื่อไฟล์
HyperNeutrino

คุณสามารถใช้งานได้sys.argv[1:]หากคุณไม่ต้องการยุ่งกับเรื่องนั้น_
sagiksp

2

UpperHandBot, Python 3

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

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

บอทนี้พยายามรวบรวมก้อนหิมะมากกว่าคู่ต่อสู้และ ณ จุดนั้นก็เริ่มโยน ถ้า ณ จุดใด UHB ไม่มีลูกบอลหิมะมากกว่าคู่ต่อสู้ก็จะ:

  • เป็ดถ้าฝ่ายตรงข้ามมีลูกบอลหิมะและมีเป็ดเหลืออยู่
  • มิฉะนั้นให้โหลดซ้ำ (เว้นแต่ว่า UHB จะมีค่าสูงสุดแล้วก็จะโยนแทนแม้ว่าฉันจะไม่คิดว่าสถานการณ์นี้จะเกิดขึ้นก็ตาม)

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

ฉันตั้งชื่อบอตนี้ว่า "Yggdrasil" เพราะจริง ๆ แล้วมองไปข้างหน้าลงในโครงสร้างของเกมและทำการประเมินค่าสถานะซึ่งมันสามารถคำนวณกลยุทธ์ผสมที่เหมาะสมที่สุดได้ เพราะมันต้องอาศัยกลยุทธ์ผสมมันเป็นอย่างมากที่ไม่ได้กำหนด ฉันไม่รู้ว่าสิ่งนี้จะทำอย่างไรในการแข่งขันที่แท้จริง

บางสิ่งเกี่ยวกับบอทนี้:

  • แกนกลางเป็นฟังก์ชั่นวนซ้ำที่คำนวณค่าและกลยุทธ์ผสมที่เหมาะอย่างยิ่งสำหรับทุกสถานะของเกม ตอนนี้ฉันได้ตั้งไว้ให้ดู 4 ขั้นตอนข้างหน้า
  • มันเล่นอย่างสุดขีดอย่างมากในหลาย ๆ กรณีบอทนี้เทียบเท่ากับ "การสุ่มเลือกด้วยกรรไกรกระดาษ" มันยึดพื้นและหวังว่าฝ่ายตรงข้ามจะได้เปรียบในเชิงสถิติ หากบอทนี้สมบูรณ์แบบ (ซึ่งไม่ใช่) ที่ดีที่สุดที่คุณสามารถทำได้คือ 50% ชนะและขาดทุน 50% เป็นผลให้ไม่มีฝ่ายตรงข้ามที่มันเต้นอย่างสม่ำเสมอ แต่ก็ไม่มีใครแพ้อย่างสม่ำเสมอ

ฉันยังไม่เข้าใจชื่อ ... : P
HyperNeutrino

@HyperNeutrino Yggdrasilเป็นต้นไม้ในตำนานและในกรณีนี้ฉันหมายถึงต้นไม้เกม
PhiNotPi

ใช่แล้วฉันรู้สึกว่าฉันควรจะจำสิ่งนี้ได้ : P Nice!
HyperNeutrino

2

ความเจ็บปวดใน Nash (C ++)

เรียกว่าเพราะความจริงที่ว่าฉันต้องเขียนตัวแก้สมดุลของแนชของฉันเองเป็นความเจ็บปวดที่แท้จริง ฉันประหลาดใจที่ไม่มีห้องสมุดใด ๆ ที่ใช้การแก้ปัญหาของ Nash ได้!

#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) แต่สิ่งนี้นำไปสู่ปัญหาที่เห็นได้ชัด ดำเนินการลดผลตอบแทนจากการรวบรวมลูกบอลมากขึ้นเรื่อย ๆ ดังนั้นแทนที่จะทำเช่นนี้จะวิเคราะห์ทรีเกมทั้งหมดทำงานย้อนหลังตั้งแต่ 1,000 รอบและเติมเต็มการประเมินมูลค่าจริงตามวิธีที่แต่ละเกมสามารถเลื่อนออกได้

ผลที่ได้คือฉันไม่รู้เลยว่ากลยุทธ์นี้ใช้อะไรยกเว้นพฤติกรรม "ชัดเจน" สองสามอย่างที่มีรหัสตายตัว (โยนลูกบอลหิมะหากคุณมีลูกบอลมากกว่าฝ่ายตรงข้ามที่มีลูกบอล + เป็ดและโหลดถ้าคุณทั้งคู่ออกไป ก้อนหิมะ) หากใครต้องการที่จะวิเคราะห์ชุดข้อมูลที่สร้างฉันคิดว่ามีพฤติกรรมที่น่าสนใจในการค้นพบ!

การทดสอบนี้กับ "บันทึกหนึ่ง" แสดงให้เห็นว่ามันชนะแน่นอนในระยะยาว แต่เพียงเล็กน้อยเท่านั้น (514 ชนะ, แพ้ 486, 0 ​​เสมอในชุดแรกของ 1,000 เกมและชนะ 509, ขาดทุน 491, 0 วาดในวินาที)


สิ่งสำคัญ!

นี่จะใช้งานได้ทันที แต่นั่นไม่ใช่ความคิดที่ดี ใช้เวลาประมาณ 9 นาทีบนแล็ปท็อปผู้พัฒนาพอประมาณของฉันเพื่อสร้างทรีเกมที่สมบูรณ์ แต่มันจะบันทึกความน่าจะเป็นครั้งสุดท้ายลงในไฟล์เมื่อมันถูกสร้างขึ้นและหลังจากนั้นแต่ละรอบก็แค่สร้างตัวเลขสุ่มและเปรียบเทียบกับ 2 ไบต์ดังนั้นมันจึงเร็วมาก

หากต้องการทางลัดทั้งหมดเพียงดาวน์โหลดไฟล์นี้ (3.5MB) และวางไว้ในไดเรกทอรีที่มีไฟล์เรียกทำงาน

หรือคุณสามารถสร้างมันขึ้นมาได้ด้วยตัวเองโดยเรียกใช้:

./pain_in_the_nash 1000 50 25

ซึ่งจะบันทึกหนึ่งไฟล์ต่อเทิร์นจนกว่าการบรรจบกัน โปรดทราบว่าแต่ละไฟล์คือ 3.5MB และจะมาบรรจบกันที่เทิร์น 720 (เช่น 280 ไฟล์, ~ 1GB) และเนื่องจากเกมส่วนใหญ่ไม่ได้เข้าใกล้เทิร์น 720 ดังนั้นไฟล์ก่อนคอนเวอร์เจนซ์จึงมีความสำคัญต่ำมาก


เป็นไปได้ไหมที่จะทำให้โปรแกรมส่งออกผลลัพธ์สุดท้ายเท่านั้น? ขอบคุณ!
HyperNeutrino

@HyperNeutrino เอาต์พุตอื่น ๆ ทั้งหมดควรเป็น stderr ดังนั้นจึงไม่มีผลกระทบใด ๆ แต่ฉันได้อัปเดตเพื่อแสดงความคืบหน้าเฉพาะเมื่อทำงานในโหมดประมวลผลล่วงหน้า ตอนนี้จะเขียนถึง stdout เมื่อทำงานตามปกติเท่านั้น ฉันขอแนะนำให้ทำตามคำแนะนำ "สำคัญ" เนื่องจากมิฉะนั้นแล้วมันก็จะวนไปรอบ ๆ ในรอบแรกเป็นเวลาหลายนาที (อย่างน้อยด้วยการประมวลผลล่วงหน้าคุณจะเห็นความคืบหน้า)
เดฟ

ตกลง. ฉันจะทำตามคำแนะนำนั้นขอบคุณ!
HyperNeutrino

ฉันจะขอบคุณถ้าคุณสามารถอัปโหลดไฟล์ข้อมูลเพราะใช้เวลาตลอดไปในการสร้างไฟล์ทั้งหมด หากคุณสามารถทำเช่นนั้นได้ว่าจะดี :)
HyperNeutrino

@HyperNeutrino ตกลงมันใช้เวลาตลอดไปในการอัปโหลดบนอินเทอร์เน็ตที่น่ากลัวของฉัน แต่ไฟล์ 3.5MB ที่รวมอยู่ที่นี่: github.com/davidje13/snowball_koth_pitn/blob/master/ ...... (ใส่ไว้ในไดเรกทอรีเดียวกัน)
เดฟ

1

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

สามารถรันจาก bash บน Linux ได้หรือไม่?
HyperNeutrino

@HyperNeutrino ฉันรู้ว่ามันสามารถทำได้ใน macOS แต่ฉันไม่รู้ว่ามันทำบน Linux หรือไม่ หากคุณสามารถตรวจสอบได้มันจะดีมาก ลองswiftคำสั่งแล้วตรวจสอบว่ามันใช้งานได้หรือไม่
Mr. Xcoder

ดูเหมือนว่าจะไม่มีอยู่จริง มีแพ็คเกจด้วย แต่มันไม่ใช่ภาษาสวิฟท์ ดังนั้นฉันอาจไม่ทดสอบสิ่งนี้จนกว่าฉันจะได้รับสิ่งที่ทำงานขอโทษ
HyperNeutrino

ตัวรวบรวมที่เป็นไปได้เพียงอย่างเดียวคือ 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

MonteBot, C ++

ฉันใช้รหัสจากkothนี้และแก้ไขมันสำหรับความท้าทายนี้ ใช้อัลกอริธึมการค้นหาต้นไม้แบบแยกคู่ UCT Monte Carlo มันควรจะอยู่ใกล้กับแนชดุล

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

MonteBot.cppบันทึกเพื่อ วิ่ง
g++ -o -std=c++11 MonteBot MonteBot.cpp

คำสั่งให้เรียกใช้: ./MonteBot <args>


1

procrastinator - งูหลาม 3

Procrastinator จะผัดวันประกันพรุ่งโดยการเล่นบันทึกสองรอบแรก ทันใดนั้นสัตว์ประหลาดที่ตื่นตระหนกต้องการหลีกเลี่ยงการสูญเสียสงครามทรัพยากร

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

"Procrastinator" ดังนั้นทุกคนใน PPCG ที่ตั้งใจจะทำการบ้านจริง ๆ ? (อย่าปฏิเสธคนที่อ่านข้อความนี้และฉัน)
HyperNeutrino

1
"Monkey Gratification ทันที" คุณเคยเห็น TEDTalk แล้วหรือยัง :)
HyperNeutrino


0

ParanoidBot และ PanicBot - ActionScript3 ( RedTamarin )

จากการไม่เจาะจงภาษาเฉพาะ (พร้อมส่วนขยายเพื่อให้อาร์กิวเมนต์บรรทัดคำสั่ง) hails ขี้เซา ParanoidBot และพันธมิตรที่น่าเบื่อของเขา PanicBot

ParanoidBot

ParanoidBot กำลังสูญเสียความคิดและมีกลยุทธ์เฉพาะที่ไม่จำเป็นต้องพึ่งพา ขั้นแรกมันจะปล่อยก้อนหิมะจนกว่าจะถึงขีด จำกัด จากนั้นหลังจากที่มีการเตือนเป็ดสามตัวความหวาดระแวงก็เข้ามาและบอทก็พยายามที่จะกักเก็บก้อนหิมะเพิ่มเติมระหว่างเป็ดสุ่ม หลังจากเติมเต็มอุปทานของมัน 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 ก็ตอบโต้ด้วยความกลัวตามสัญชาตญาณ หลังจากวิ่งหนีออกมาจากเป็ดด้วยความหวาดกลัวด้วยความหวาดกลัว 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(); }



นี่คือหนึ่งในน้อยกว่า 15 รายการที่ใช้ AS3 ที่นี่ใน PPCG อยู่มาวันหนึ่งบางทีภาษาที่แปลกใหม่อาจจะเป็นปริศนาที่จะครอบงำ


สิ่งนี้สามารถเรียกใช้จากทุบตีบน Linux?
HyperNeutrino

ฉันไม่ได้ทดสอบสิ่งนั้น แต่ใช่มันควรจะเป็น RedTamarin ปฏิบัติการ (redshell) ถูกสร้างขึ้นสำหรับ Windows, Mac และ Linux: http://redtamarin.com/tools/redshell หากหนึ่งในบอทด้านบนถูกบันทึกไว้ในไฟล์ชื่อsnow.asสิ่งต่อไปนี้ควรทำงานเป็น bash:$ ./redshell snow.as -- 0 50 50 25 25

มันทำให้ฉันได้รับอนุญาตปฏิเสธข้อผิดพลาดเมื่อฉันพยายามเรียกใช้นี้
HyperNeutrino

@HyperNeutrino chmod +x redshellเป็นเพื่อนของคุณที่นี่ ...
Erik the Outgolfer

อาจจะ chmod 777 ทุกอย่าง? อาจมีการแก้ไขปัญหาบางอย่างในเว็บไซต์ RedTamarin เช่นกัน

0

Defender, Python

โหลดซ้ำเมื่อไม่มีผู้เล่นลูกบอลหิมะ ถ้ามันมีก้อนหิมะมันก็จะพ่น หากมันไม่มีลูกบอลหิมะ แต่ฝ่ายตรงข้ามก็ทำเช่นนั้นมันจะช่วยเป็ดถ้าทำได้

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.