Morra เกมแห่งขุนนางแห่งราชา


28

พื้นหลัง

เกมของMorraเป็นเกมที่ง่าย ในเวอร์ชั่น "ดั้งเดิม" ผู้เล่นหลายคนพร้อมกันโยนหมายเลข 0-5 ด้วยมือขณะที่เดายอดรวมของมือทุกคน รุ่นที่ฉันจะใช้ที่นี่ได้รับการแก้ไขเพื่อเพิ่มศักยภาพสำหรับกลยุทธ์ที่ไม่สำคัญและมีการอธิบายไว้ด้านล่าง:

  • มีผู้เล่นสองคน
  • เหมือนกรรไกรหินกระดาษผู้เล่นเคลื่อนที่ไปพร้อม ๆ กัน
  • ในแต่ละเทิร์นผู้เล่นแต่ละคนเลือกหมายเลข 0-5 และเดาว่าคู่ต่อสู้เลือก 0-5 ซึ่งหมายความว่าตัวเลขสองตัวจะถูกส่งออกในแต่ละเทิร์น เพื่อชี้แจงชัดเจนตัวเลขทั้งสองควรอยู่ในช่วง 0-5 โดยรวม
  • หากคุณเดาตัวเลือกของคู่ต่อสู้ได้อย่างถูกต้อง แต่ฝ่ายตรงข้ามไม่คาดเดาอย่างถูกต้องคุณจะได้คะแนนจำนวนหนึ่งเท่ากับผลรวมของตัวเลขสองตัวที่เล่น ตัวอย่างเช่นหากตัวเลขที่เล่นเป็น 3 และ 5 การทายถูกต้องจะมีค่า 8 คะแนน
  • หากผู้เล่นทั้งสองหรือไม่เดาถูกต้องจะไม่มีการให้คะแนน
  • คนที่มีคะแนนมากที่สุดหลังจากรอบ 1,000 ชนะเกมนั้น

การแข่งขัน

ทัวร์นาเมนต์จะดำเนินการในลักษณะวนรอบและจะดำเนินการโดยสร้างคู่ที่เป็นไปได้ของการแข่งขัน สำหรับชัยชนะแต่ละครั้งผู้เข้าแข่งขันจะได้รับ 2 คะแนนชัยชนะ ผลเสมอกันใน 1 คะแนนชัยชนะ ไม่มีการเก็บคะแนนชัยชนะสำหรับการแพ้

ผู้ชนะเลิศการแข่งขันจะต้องเป็นผู้เข้าแข่งขันที่มีคะแนนชัยชนะมากที่สุดกับผู้อื่น


วิธีเข้าร่วม

จะมีสองวิธีในการส่งบอทเข้าแข่งขัน วิธีแรกและเป็นวิธีที่ได้รับความนิยมอย่างมากคือการใช้งานอินเทอร์เฟซ Java ที่มาจากคอนโทรลเลอร์ วิธีที่สองคือการเขียนโปรแกรมอิสระ

เรามาครอบคลุมวิธีการจาวาก่อน อินเทอร์เฟซที่คุณจะต้องใช้คือPlayerและกำหนดสองวิธี:public String getName()ระบุบอทของคุณและpublic int[] getMove(String[] args)ใช้เวลาเป็นแถวหกสตริงargs mychoices myguesses myscore opponentchoices opponentguesses opponentscoreตัวอย่างมีดังต่อไปนี้:

042 045 0 324 432 6

ซึ่งหมายความว่าฉันเลือก 0 ในรอบแรกและเดาว่าคู่ต่อสู้ของฉันกำลังจะขว้าง 0 คู่ต่อสู้ของฉันขว้าง 3 และเดาฉันจะโยน 4 ในรอบที่สามคู่ต่อสู้ของฉันเดาถูกว่าฉันจะโยน 2 หมายถึงว่าเขาได้รับ 2 + 4 = 6 คะแนน

วิธีการของคุณจะส่งกลับอาร์เรย์ของจำนวนเต็มสองจำนวนซึ่งเป็นทางเลือกของคุณและเดาตามลำดับ ตัวอย่าง{4,2}สำหรับการเลือก 4 และเดา 2

นี่คือตัวอย่างของบอท Java ที่สมบูรณ์เขียนเป็นวิธีการ หากคุณต้องการการส่งของคุณจะต้องรวมสิ่งที่เกิดขึ้นในgetMoveวิธีการเท่านั้น

import java.util.Random;
/**
 * A simple example Morra bot to get you started.
 */
public class ExampleBot implements Player
{
    public String getName()
    {
        return "ExampleBot";
    }

    public int[] getMove(String [] args)
    {
        //easiest way I know to break down to create a move history
        //(just contains their throw history)
        char[] theirThrowsC = args[3].toCharArray();
        int[] theirThrows = new int[theirThrowsC.length];
        for(int i = 0; i < theirThrowsC.length; i++)
        {
            theirThrows[i] = Integer.parseInt(Character.toString(theirThrowsC[i]));
        }

        //get my score
        int myScore = Integer.parseInt(args[2]);

        Random r = new Random();
        int guess = r.nextInt(6);
        if(theirThrows.length > 0)
        {
            guess = theirThrows[theirThrows.length-1];
        }

        //throws a random number, guesses what they threw last
        return new int[] {r.nextInt(6),guess}; 
    }

    public static int otherMethod(int example) //you can write additional static methods
    {
        return 0;
    }
}

เป็นโปรแกรมอิสระ

ขณะนี้ฉันมีข้อ จำกัด ในการสนับสนุนภาษาเพิ่มเติม นอกจาก Java ฉันสามารถยอมรับโปรแกรมที่เขียนใน Python 3.4, Perl 5 หรือ Ruby 2.1.5 หากมีภาษาที่หลายคนดูเหมือนจะต้องการฉันจะทำอย่างดีที่สุดเพื่อเพิ่ม

อินพุตไปยังโปรแกรมของคุณจะเป็นข้อโต้แย้งในบรรทัดคำสั่ง มันอาจมีลักษณะเช่นนี้:

perl awesomebot.plx 042 045 0 324 432 6

ผลลัพธ์ของโปรแกรมของคุณควรเป็นทางเลือกของคุณตามด้วยการเดาของคุณแต่ละรายการตามด้วยช่องว่าง

โปรดรวมคำตอบของคุณในคำสั่งที่แน่นอนที่จำเป็นในการเรียกใช้ โปรดทราบว่าฉันใช้ Windows 8.1


กฎพิเศษ

ออมทรัพย์รัฐและหมดเวลา

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

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

กฎการส่งเพิ่มเติม

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

ผู้ควบคุม

รุ่นปัจจุบันของตัวควบคุมจะพบได้ที่นี่ มันเขียนใน Java 8 ไฟล์ "Tournament" เป็นตัวควบคุมหลักซึ่งมีรายชื่อคู่แข่ง (ถ้าคุณต้องการโฮสต์การแข่งขันของคุณเอง)


ลีดเดอร์บอร์ด

ฉันไม่สามารถอัปเดตกระดานแต้มนำได้บ่อยนัก ฉันค่อนข้างยุ่งในสุดสัปดาห์นี้ โดย "ค่อนข้างยุ่ง" ฉันหมายถึงไม่สามารถเข้าถึงคอมพิวเตอร์จาก 6:30 น. ถึง 21:30 น. นี่คือคะแนนหลังจากวิ่ง 5 ครั้ง บอท "สะท้อน" ยังคงถูกริบเพราะเหตุผลบางอย่าง (อาจเป็นความผิดของฉันฉันยังไม่ได้ตรวจสอบ)

  170 - Quinn and Valor                         
  158 - Historian                               
  142 - DeltaMax                                
  140 - MorraCowbell                            
  132 - Extrapolator                            
  115 - Rainbolt                                
  102 - Popularity                              
  100 - Interpolator                            
   83 - CounterBot                              
   80 - Basilisk                                
   76 - Erratica                                
   65 - Trendy                                  
   63 - Scholar                                 
   62 - RandomGuesser                           
   60 - KingFisher                              
   59 - NullifierBot                            
   55 - EvolvedBot                              
   48 - Confused          

เครดิต

ขอบคุณ Rainbolt และ Peter Taylor สำหรับความช่วยเหลือจากผู้ควบคุม


1
@ MartinBüttner Ruby 2.1.5 ได้ถูกเพิ่มเข้ามา
PhiNotPi

โรบินกลมทำงานอย่างไร Player1 เทียบกับ Player2 1,000 ครั้ง, Player1 เทียบกับ Player3 1000 ครั้ง ฯลฯ ... หรือว่าเป็น Player1 กับ Player2 หนึ่งครั้งจากนั้นก็จะเล่นเป็น player1 กับผู้เล่น 3 อีกครั้งหนึ่ง ...
Vajura

@Vajura การแข่งขันเดี่ยวประกอบด้วย 1 การต่อสู้ระหว่างแต่ละคู่ การรบหนึ่งครั้งมี 1,000 รอบด้วยคะแนนรวมสูงสุดเมื่อสิ้นสุดการรบเพื่อตัดสินว่าใครจะได้รับคะแนนชัยชนะสองคะแนน กระดานคะแนนปัจจุบันแสดงคะแนนชัยชนะทั้งหมดหลังจากการแข่งขัน 40 ครั้ง
PhiNotPi

ขออภัยสำหรับความล่าช้าในการปรับปรุงบอร์ด ฉันยุ่งมากในสุดสัปดาห์นี้ คาดหวังและอัปเดตในคืนนี้และพรุ่งนี้เช้า
PhiNotPi

ว้าวฉันไม่ได้คาดหวังว่าบอทของฉันจะทำได้ดี นอกจากนี้ตัวเลขหมายถึงอะไรโดยชุดผลลัพธ์แรก ... จำนวนชัยชนะ?
mbomb007

คำตอบ:


17

Morra Cowbell

สำหรับใครก็ตามที่กำลังมองหานัยสำคัญในชื่อของบอทนี้ชื่อMorraทำให้ฉันคิดถึงSpace Italianดังนั้นฉันจึงคิดว่าฉันต้องการชื่อที่เล่นบนนั้น ผู้สมัครอื่น ๆ รวมถึงMorra หลอกคุณและMorra สำหรับฉัน

นี่คือคลาสเต็มที่ใช้Playerอินเทอร์เฟซ คำอธิบายด้านล่าง

import java.util.Random;

public class MorraCowbell implements Player {
    private final Random rnd = new Random();

    public String getName() {
        return "MorraCowbell";
    }

    public int[] getMove(String[] args) {
        int[] prior = new int[36];
        for (int i = 0; i < 36; i++) prior[i] = 1;
        // args: myChoices myGuesses myScore opponentChoices opponentGuesses opponentScore
        if (args.length == 6 && args[3].length() == args[4].length()) {
            for (int i = 0; i < args[3].length(); i++) prior[6*(args[3].charAt(i) - '0') + (args[4].charAt(i) - '0')]++;
        }

        int[] weights = new int[6];
        for (int r = 0; r < 6; r++) {
            for (int s = 0; s < 6; s++) {
                for (int t = 0; t < 6; t++) {
                    weights[r] += (r + s) * ((r + s == 5 ? 1 : 0) + (r == t ? -1 : 0)) * prior[s * 6 + t];
                }
            }
        }

        // Find the best window.
        int[][] magic = new int[][] {
            { 7776, 6480, 5400, 4500, 3750, 3125 }, { 3125, 2500, 2000, 1600, 1280, 1024 }, { 1875, 1500, 1200, 960,
            768, 640 }, { 1125, 900, 720, 576, 480, 400 }, { 1620, 1296, 1080, 900, 750, 625 }, { 1296, 1080, 900, 750,
            625, 500 }, { 750, 625, 500, 400, 320, 256 }, { 675, 540, 432, 360, 300, 250 }, { 648, 540, 450, 375, 300,
            250 }, { 375, 300, 250, 200, 160, 128 }, { 375, 300, 240, 200, 160, 128 }, { 450, 375, 300, 240, 192, 160,
            128 }, { 324, 270, 225, 180, 150, 125 }, { 270, 225, 180, 144, 120, 100, 80 }, { 225, 180, 150, 120, 96,
            80 }, { 225, 180, 144, 120, 96, 80 }, { 324, 270, 216, 180, 150, 125, 100, 80, 64 }, { 135, 108, 90, 72, 60,
            50 }, { 135, 108, 90, 75, 60, 50, 40, 32 }, { 108, 90, 75, 60, 48, 40, 32 }, { 54, 45, 36, 30, 25, 20, 16 },
            { 54, 45, 36, 30, 24, 20, 16 }
        };
        long bestN = 0;
        int bestD = 1, bestIdx = -1, bestA[] = null;
        for (int[] A : magic) {
            for (int i = 0; i < A.length - 5; i++) {
                long n = 0;
                int d = 0;
                for (int j = 0; j < 6; j++) {
                    n += weights[j] * A[i + j];
                    d += A[i + j];
                }
                if (n * bestD > bestN * d) {
                    bestN = n;
                    bestD = d;
                    bestIdx = i;
                    bestA = A;
                }
            }
        }

        int r = rnd.nextInt(bestD);
        for (int i = 0; i < 6; i++) {
            r -= bestA[bestIdx + i];
            if (r < 0) return new int[] { i, 5 - i };
        }

        // Just a precaution: this should be unreachable.
        return new int[] { 0, 5 };
    }
}

คำอธิบาย

ฉันเริ่มต้นด้วยการวิเคราะห์เกมด้วยนิ้วที่น้อยลง สิ่งที่ไม่ใช่เรื่องง่ายที่ง่ายที่สุดคืออนุญาตให้โทร0หรือ1และมีตารางผลตอบแทนต่อไปนี้ (ค่าเป็นผลตอบแทนสำหรับผู้เล่นแถว):

       (0,0) (0,1) (1,0) (1,1)
      +-----------------------
(0,0) |  0     0    -1     0
(0,1) |  0     0     0     1
(1,0) |  1     0     0    -1
(1,1) |  0    -1     1     0

(0,0)กลยุทธ์ที่ถูกครอบงำด้วย(0,1)ดังนั้นเราจึงสามารถลดตารางเพื่อ

       (0,1) (1,0) (1,1)
      +-----------------
(0,1) |  0     0     1
(1,0) |  0     0    -1
(1,1) | -1     1     0

ตอนนี้(1,0)กลยุทธ์ถูกครอบงำ(0,1)ดังนั้นเราจึงสามารถลดตารางเป็น

       (0,1) (1,1)
      +-----------
(0,1) |  0     1
(1,1) | -1     0

และตอนนี้(1,1)ถูกครอบงำด้วย(0,1)ดังนั้นเราจึงจบลงด้วย

       (0,1)
      +-----
(0,1) |  0  

ดังนั้นการเล่นเสมอ(0,1)คือความสมดุลของแนช แต่สิ่งที่อยากรู้คือมันไม่ใช่สิ่งเดียว นี่เป็นเกมผลรวมศูนย์ที่สมมาตรดังนั้นผลตอบแทนที่คาดหวังคือ 0 และกลยุทธ์ผสมที่ผสมผสานกัน(0,1)และ(1,0)ที่ใด(0,1)จะเลือกอย่างน้อย 50% ของเวลาที่ประสบความสำเร็จในผลตอบแทนที่ ดังนั้นเราจึงมีพื้นที่หนึ่งมิติของสมดุลของแนช

มันน่าจะเป็นกรณีที่แม้ว่าฉันไม่ได้รับการพิสูจน์แล้วว่าการn-finger Morra มีnpolytope มิติสมดุลของแนชซึ่งเป็นกลยุทธ์ที่ผสมระหว่างคู่ที่n+1 (pick, guess)pick + guess = n

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

QuinnAndValor แสดงให้เห็นถึงช่องโหว่ของการสันนิษฐานว่าผู้เล่นคนอื่นกำลังใช้กลยุทธ์ที่หลากหลาย ด้วยการตรวจจับผู้เล่นที่ใช้กลยุทธ์จากสมดุลของแนชมันสามารถเปลี่ยนเป็นโหมดการเดินแบบสุ่มซึ่งเล่นกลยุทธ์ที่ไม่สมดุลมันจะรับผิดชอบโดยเฉลี่ยที่จะสูญเสีย แต่ต้องได้รับความเป็นผู้นำเพียงครั้งเดียวแล้ว pick + guess = nมันสามารถเปลี่ยนกลับไปเล่นคู่ที่ ดังนั้นสมดุลของแนชสำหรับเกมเดียวไม่ได้พูดถึงเรื่องสมดุลของแนชในเกมซ้ำซึ่งช่วยให้กลยุทธ์ที่ซับซ้อนมากขึ้น


4
เป็นไปได้หรือไม่ที่เวทย์มนตร์ของคุณมีส่วนหนึ่งของหมายเลขแฮมมิง ? แน่นอนว่ามันไม่ได้มีทั้งหมด แต่มีหลายคน ( หรือทั้งหมด ) อยู่ในรายการบนเว็บไซต์นั้น
GiantTree

@GiantTree พวกเขาเป็นหมายเลข Hamming ทั้งหมด การสังเกตที่น่าสนใจ
Peter Taylor

ไม่น่าแปลกใจที่บอทของคุณกำลังแฮม : D
mbomb007

11

Quinn and Valor (อัพเดท)

Quinn และ Valor เป็นทีมแรนเจอร์ชั้นยอด ด้วยหน้าไม้และก้ามปูพวกเขาก็แยกความกล้าออกจากกันเพื่อท้าทายพวกเขา

import java.util.ArrayList;
import java.util.List;

interface Champion extends Player {
}

/*
 * Quinn and Valor are an elite ranger team. With crossbow and claw, they ...
 */
public class QuinnAndValor implements Champion {

    private final Champion quinn = new Quinn();
    private final Champion valor = new Valor();

    private int checker;
    private int myScore, opScore;
    private boolean ulted;
    private boolean teemoDetected;
    private boolean quinnNeverLose, valorNeverLose;
    private int quinnScore, valorScore;
    private int quinnRound, valorRound;

    public QuinnAndValor() {
        checker = check() ? 0 : 1;
    }

    // Check if is a fine use
    private static boolean check() {
        return Thread.currentThread().getStackTrace()[3].getClassName().equals(
                "Tournament");
    }

    @Override
    public String getName() {
        return quinn + " and " + valor;
    }

    @Override
    public int[] getMove(String[] args) {
        // Punish for bad usage
        switch (checker) {
        case 1:
            checker++;
            return new int[] { -1, -1 };
        case 2:
            checker++;
            return null;
        case 3:
            throw new Error("Mua he he heh!");
        default:
            if (checker > 0)
                throw new Error("Mua he he heh!");
            break;
        }

        int round = args[0].length();
        if (round == 0) {
            // Buy starting items
            myScore = opScore = 0;
            teemoDetected = false;
            quinnNeverLose = valorNeverLose = true;
            quinnScore = valorScore = quinnRound = valorRound = 0;
            ((Valor) valor).reset();
        }

        if (ulted = useUltimate(args)) {
            valorRound++;
            return valor.getMove(args);
        } else {
            quinnRound++;
            return quinn.getMove(args);
        }
    }

    /*
     * Quinn's ultimate has a lengthy cool-down, especially at lower ranks, so
     * we have to use it only when needed.
     */
    private boolean useUltimate(String[] args) {
        int round = args[0].length();
        int lastMyScore = myScore;
        int lastOpScore = opScore;
        myScore = Integer.parseInt(args[2]);
        opScore = Integer.parseInt(args[5]);
        int score = (myScore - opScore) - (lastMyScore - lastOpScore);
        if (ulted) {
            valorScore += score;
            valorNeverLose &= score >= 0;
        } else {
            quinnScore += score;
            quinnNeverLose &= score >= 0;
        }

        if (round < 100) {
            // Haven't hit level 6 yet
            return false;
        }

        if (myScore > opScore) {
            // We're already winning. Press on with strategy impossible to lose
            if (quinnNeverLose && quinnRound >= 50)
                return false;
            if (valorNeverLose && valorRound >= 50)
                return true;
        } else if (myScore < opScore) {
            // Although Quinn can blind others to counter them, she can be
            // counter be Teemo who also has blind! Don't fall for this!
            if (!teemoDetected) {
                teemoDetected = true;
                for (int i = round - 20; i < round; i++)
                    if (args[3].charAt(i) + args[4].charAt(i) != 'e')
                        teemoDetected = false;
            }
            if (teemoDetected)
                return true;
        }

        if (valorRound < 100) {
            // If we never use our ultimate, how can we know how strong it is?
            return true;
        }

        if (quinnScore < 0 && valorScore < 0)
            return valorRound < quinnRound;
        else
            return quinnScore * valorRound < valorScore * quinnRound;
    }

    @Override
    public String toString() {
        return getName();
    }

    /*
     * Quinn is a female Demacian elite ranger.
     * 
     * @see Valor
     */
    public static class Quinn implements Champion {
        @Override
        public String getName() {
            return "Quinn";
        }

        /*
         * Magic!
         */
        @Override
        public int[] getMove(String[] args) {
            int t = (int) ((Math.sqrt(Math.random() * 168 + 1) - 1) / 2);
            return new int[] { 5 - t, t };
        }

        @Override
        public String toString() {
            return getName();
        }
    }

    /*
     * Valor is Quinn's Demacian eagle.
     * 
     * @see Quinn
     */
    public static class Valor implements Champion {
        @Override
        public String getName() {
            return "Valor";
        }

        private int lastRound;
        private double[][] c;

        public void reset() {
            lastRound = 0;
            c = new double[6][6];
        }

        /*
         * Magic!
         */
        @Override
        public int[] getMove(String[] args) {
            int round = args[0].length();
            int[] b = new int[6];
            for (int i = round - 12; i < round; i++)
                b[args[0].charAt(i) - '0']++;
            {
                double deWeight = Math.pow(0.95, round - lastRound);
                for (int i = 0; i < 6; i++)
                    for (int j = 0; j < 6; j++)
                        c[i][j] *= deWeight;
                double weight = 1;
                for (int i = round - 1; i >= lastRound; i--) {
                    c[args[3].charAt(i) - '0'][args[4].charAt(i) - '0'] += weight;
                    weight *= 0.95;
                }
            }
            lastRound = round;

            List<int[]> pq = new ArrayList<>(1);
            double e = Integer.MIN_VALUE;
            for (int i = 0; i < 6; i++)
                for (int j = 0; j < 6; j++) {
                    double f = 0;
                    for (int k = 0; k < 6; k++)
                        f += (i + j) * c[j][k];
                    for (int k = 0; k < 6; k++)
                        f -= (i + k) * c[k][i];
                    // recently played moves are dangerous
                    f -= b[i] * b[i] * ((round + 11) / 12);
                    if (f >= e) {
                        if (f > e) {
                            pq.clear();
                            e = f;
                        }
                        pq.add(new int[] { i, j });
                    }
                }
            return pq.get((int) (Math.random() * pq.size()));
        }

        @Override
        public String toString() {
            return getName();
        }
    }
}

พวกเขามักจะเอาชนะโซลูชั่น Java ทั้งหมดในเครื่องของฉัน

แก้ไข:

ฉันยอมรับว่า Quinn และ Valor ล้มเหลวในการต่อสู้ประวัติศาสตร์ แต่ฉันยังคงมีความเชื่อมั่นที่ดีในการชนะการแข่งขัน

หลักการของฉันคือสำหรับการแก้ปัญหาใด ๆ ด้วยchoice + guess == 5ก็เล่นกับchoice + guess == 5ผู้รับการรักษาประโยชน์ของคุณ

ปรับปรุง:

อืม ... ทุกอย่างก็ซับซ้อน


1
ฉันชอบการอ้างอิง League of Legends ตอนนี้ฉันอยากจะสร้างบอท Teemo :)
mbomb007

6

นักวิชาการ

Scholar พยายามเรียนรู้จากการเคลื่อนไหวของคู่ต่อสู้โดยเลือกอันที่คาดเดาไม่ได้และคาดเดาว่าคู่ต่อสู้ของเขาใช้มากที่สุด แต่ทฤษฎีไม่ใช่ทุกอย่างดังนั้น Scholar จึงทำได้ไม่ดีนัก ...

import java.util.HashMap;

public class Scholar implements Player
{
    public static int[] pm = new int[6];
    public static int[] pg = new int[6];
    public static HashMap<Integer, Integer> lm = new HashMap<>();
    public static HashMap<Integer, Integer> lg = new HashMap<>();

    public String getName()
    {
        return "Scholar";
    }

    public int[] getMove(String[] a)
    {
        int r = a[0].length();
        for (int i = 0; i < 6; i++) { pm[i]=0; pg[i]=0; }
        for (int i = 0; i < a[3].length(); i++) {
            int m = Integer.parseInt(String.valueOf(a[4].charAt(i)));
            int g = Integer.parseInt(String.valueOf(a[3].charAt(i)));
            pm[m]++; pg[g]++;
        }
        for (int i = 0; i < pm.length; i++) { lm.put(i, pm[i]); lg.put(i, pg[i]); }

        if (r < 1) {
            return new int[] { 3, 3 };
        } else {

            int mm = lm.entrySet().stream().min((x, y) -> x.getValue() > y.getValue() ? 1 : -1).get().getKey();
            int mg = lg.entrySet().stream().max((x, y) -> x.getValue() > y.getValue() ? 1 : -1).get().getKey();
            return new int[] { mm, mg };
        }   
    }
}

6

DeltaMax

(อัปเดตเพื่อไม่ใช้ไฟล์และเพิ่มหัวข้อใหม่และแก้ไขเพื่อไม่ให้ติดขัดในส่วนแรกอีกต่อไป)

ประกอบด้วยกลยุทธ์สองสามอย่างที่เริ่มง่ายแล้วซับซ้อนมากขึ้น - ถ้าคุณเคลียร์มันจะพาคุณไปยังส่วนถัดไป

  • ส่วนที่ 1:เดา{0, 5}อย่างสม่ำเสมอ
  • ส่วนที่ 2:ตรวจสอบว่าการทาย 4 ครั้งสุดท้ายของคุณก่อให้เกิดรูปแบบคงที่เชิงเส้นหรือกำลังสองหรือไม่
  • ส่วนที่ 3:ตรวจสอบว่าคุณเดาจำนวนต่ำผิดปกติ (น้อยกว่า 1/13) แล้วเลือกหมายเลขนั้น
  • ส่วนที่ 4:วิเคราะห์กราฟขนาดใหญ่ในตัวเลือกของคุณและดูว่ามีแนวโน้มที่จะเกิดอะไรขึ้นต่อไป
  • ส่วนที่ 5:ดูรอบ 100 รอบที่ผ่านมาและเลือก(choice, guess)คู่ที่จะมีความคาดหวังที่ดีที่สุดถ่วงน้ำหนักเพื่อให้รอบล่าสุดมีความสำคัญมากขึ้น
  • ส่วนสุดท้าย:เดาแบบสุ่มโดยมีโอกาสสูงกว่าที่จะมีทางเลือกต่ำและคาดเดาสูง ถ้าคุณมาที่นี่ DeltaMax ก็ยอมแพ้และอยากจะบอกว่า "เป็นเกมที่ดี"

เมื่อต้องการค้นหาว่ามีการใช้ Strat ใดในตอนท้าย

if (myChoices.length == 999) { System.out.println(strat); }

เส้น

ขอโทษสำหรับ Java ที่น่าสยดสยองฉันใช้เวลาช่วงบ่ายของฉันไปด้วยกันและเรียนภาษาใหม่ :)

import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

public class DeltaMax implements Player
{
    private int strat = 100;

    public String getName() { return "DeltaMax"; }

    public int[] toInts(String s) {
        char [] chars = s.toCharArray();
        int[] ints = new int[chars.length];

        for (int i = 0; i < chars.length; i++){
            ints[i] = Integer.parseInt(Character.toString(chars[i]));
        }

        return ints;
    }

    public int mod6(int n) {
        n = n % 6;
        if (n < 0) { n += 6; }
        return n;
    }

    public int[] getMove(String [] args)
    {
       int[] myChoices = toInts(args[0]);
       int[] myGuesses = toInts(args[1]);
       int myScore = Integer.parseInt(args[2]);
       int[] opponentChoices = toInts(args[3]);
       int[] opponentGuesses = toInts(args[4]);
       int opponentScore = Integer.parseInt(args[5]);

       int rounds = myChoices.length;

       if (rounds == 0) { strat = 100; }
       Random r = new Random();

       // if (myChoices.length == 999) { System.out.println(strat); }

       if (strat == 100) { // Section 1 - {0, 5}
           if (opponentScore - myScore > 21 || (opponentScore >= myScore && rounds > 100)) {
               strat = 200;
           } else {
               return new int[] {0, 5};
           }
       }

       if (strat == 200) { // Section 2 - Mini interpolator
           int w = opponentChoices[opponentChoices.length - 4];
           int x = opponentChoices[opponentChoices.length - 3];
           int y = opponentChoices[opponentChoices.length - 2];
           int z = opponentChoices[opponentChoices.length - 1];

           if (w == x && x == y && y == z) { // Constant
               return new int[] { r.nextInt(4) + 2, w };
           }

           if (mod6(x-w) == mod6(y-x) && mod6(y-x) == mod6(z-y)) { // Linear
               return new int[] { r.nextInt(4) + 2, mod6(z + (z-y)) };
           }

           if (mod6((y-x) - (x-w)) == mod6((z-y) - (y-x))) { // Quadratic
               return new int[] { r.nextInt(4) + 2, mod6((z-y) + mod6((y-x) - (x-w))) };
           }

           strat = 300;
       }

       if (strat == 300) { // Section 3 - exploit least guessed
           int [] counts = new int[6];

           for (int i = 0; i < rounds; i++) {
               counts[opponentGuesses[i]] += 1;
           }

           int minCount = rounds;

           for (int i = 0; i < 6; i++) {
               if ((counts[i] <= 1 || counts[i] * 13 < rounds) && counts[i] < minCount) {
                   minCount = counts[i];
               }
           }

           if (minCount == rounds) {
               strat = 400;
           } else {
               ArrayList<Integer> choices = new ArrayList<Integer>();

               for (int i = 0; i < 6; i++) {
                   if (counts[i] == minCount) {
                       choices.add((Integer) i);
                   }
               }

               int choice = choices.get(r.nextInt(choices.size()));

               // {0, 0} is about the worst thing you can do, so DeltaMax tries to avoid that
               if (choice == 0) {
                   return new int[] { 0, r.nextInt(4) + 2 };
               } else {
                   return new int[] { choice, r.nextInt(6) };
               }
           }
       }

       if (strat == 400) { // Section 4 - bigrams
           if (opponentScore - myScore > 42 || (opponentScore >= myScore && rounds > 300)){
               strat = 500;
           } else {
               int[] opponentScores = new int[6];
               int opponentLast = opponentChoices[opponentChoices.length - 1];

               int[] myScores = new int[6];
               int myLast = myChoices[myChoices.length - 1];

               for (int i = 0; i < opponentChoices.length - 1; i++) {
                   if (opponentChoices[i] == opponentLast) {
                       opponentScores[opponentChoices[i+1]] += 1;
                   }

                   if (myChoices[i] == myLast) {
                       myScores[myChoices[i+1]] += 1;
                   }
               }

               int maxIndex = -1;
               int maxScore = 0;

               int minIndex = -1;
               int minScore = rounds;

               for (int i = 0; i < 6; i++) {
                   if (opponentScores[i] >= maxScore) {
                       maxScore = opponentScores[i];
                       maxIndex = i;
                   }

                   if (myScores[i] <= minScore) {
                       minScore = myScores[i];
                       minIndex = i;
                   }
               }

               if (minIndex == 0 && maxIndex == 0) {
                   return new int[] { 0, r.nextInt(4) + 2 };
               } else {
                   return new int[] { minIndex, maxIndex };
               }
           }
       }

       if (strat == 500) { // Section 5 - best expectation
           if (opponentScore - myScore > 84 || (opponentScore >= myScore && rounds > 800)){
               strat = 573;
           } else {
               int minLen = Math.min(rounds, 100);

               double bestScore = 0;
               int bestGuess = 0;
               int bestChoice = 5;

               for (int guess = 0; guess < 6; guess++) {
                   for (int choice = 0; choice < 6; choice++) {
                       double score = 0;
                       int start = rounds - minLen;

                       for (int i = start; i < rounds; i++) {
                           if (opponentGuesses[i] == choice && opponentChoices[i] != guess) {
                               score -= (choice + opponentChoices[i]) * ((double) i - start) / minLen;
                           } else if (opponentGuesses[i] != choice && opponentChoices[i] == guess) {
                               score += (choice + opponentChoices[i]) * ((double) i - start) / minLen;
                           }
                       }

                       if (score > bestScore) {
                           bestScore = score;
                           bestGuess = guess;
                           bestChoice = choice;
                       }
                   }
               }

               if (bestChoice == 0 && bestGuess == 0) {
                   return new int[] { r.nextInt(4) + 2, bestGuess };
               } else {
                   return new int[] {bestChoice, bestGuess};
               }
           }
       }

       // Section final - hope for the best
       int num = (int) Math.floor(Math.sqrt(r.nextInt(35)));
       return new int[] {5 - num, num};
    }
}

ด้วยการนำตัวควบคุมปัจจุบันไปใช้งานคุณไม่จำเป็นต้องบันทึกสิ่งต่าง ๆ ลงในไฟล์หากมีการใช้ข้อมูลสำหรับเกมเดียวเท่านั้น นั่นprivate int strat;คือดีพอ
johnchen902

@ johnchen902 ขอบคุณฉันไม่ได้ตระหนักว่าฉันสามารถทำได้ ทำให้สิ่งต่าง ๆ ง่ายขึ้นมาก
Sp3000

6

นักประวัติศาสตร์

(อัปเดต: ตรรกะเดียวกัน, รหัสที่สั้นกว่าและเร็วกว่า 100 เท่าแต่คุณสามารถใช้บอทนักประวัติศาสตร์เพียงคนเดียวในทัวร์นาเมนต์)

ใช้การสุ่มน้ำหนักในการเลือกคู่การเดาตามประสิทธิภาพของการใช้คู่นั้นกับคู่ต่อสู้ในอดีตเท่านั้น น้ำหนักคือกำลังสองที่ทำคะแนนได้

public class Historian implements Player {
    private static java.util.Random r = new java.util.Random();
    private static int[] sc=new int[36]; //reseted between games, use only one Historian bot
    public String getName() {return "Historian";}
    public int[] getMove(String [] a) {
        if (a[3].length()==0)  {sc=new int[36]; for(int i=0;i<6;i++) sc[i*6+(5-i)]=5-i;}
        else {int t=a[3].charAt(a[3].length()-1)-'0'; int g=a[4].charAt(a[3].length()-1)-'0';
            for(int i=0; i<6; i++) {sc[i*6+t]+=i+t; sc[g*6+i]-=t+g;}}
        int sum=0; for(int i=0; i<36; i++) {sum+=(sc[i]<1)?1:sc[i]*sc[i];}
        int seed=r.nextInt(sum);int mt=-1;
        while (seed>=0) {seed-=(sc[++mt]<1)?1:sc[mt]*sc[mt];}  
        return new int[] {(int)(mt/6),mt%6};} }

เต้นQuinn and Valor (ไม่ได้อีกต่อไป)Morra Cowbellและสูญเสียไป ในการแข่งขันกับที่สุดของบอทมาถึงสองHistorianQuinn and Valor


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

@ johnchen902 Morra Cowbellฉันจะต้องมีการเต้น แก้ไขโพสต์ คุณสามารถลบความคิดเห็นแม้ว่าพวกเขาจะล้าสมัย
randomra

ฉันคิดว่าฉันสามารถชนะ 75% ของการต่อสู้ของเราตอนนี้หลังจากการอัปเดตของฉัน!
johnchen902

5

Extrapolator (v1.1)

การคาดการณ์ที่รุนแรงจากหนึ่งในสมดุลของแนชในเกมที่ง่ายกว่า

ฉันสนับสนุนรูปแบบคำตอบสั้น ๆ ! (ในรูปแบบหลาม)

public class Extrapolator implements Player { 
    private static java.util.Random r = new java.util.Random();
    public String getName() { return "Extrapolator"; }
    public int[] getMove(String [] args) {
        int t=-1;
        for(int c=15,s=r.nextInt(60);s>=0;s-=c,c-=2,t++);
        return new int[] {t,5-t}; } }

ดูเหมือนว่าจะผูกกับ Magic Cow (Morra Cowbell) และเต้นรายการอื่น ๆ ที่ฉันตรวจสอบ


1
โปรดย้าย Random r ไปยังฟิลด์สแตติกดังนั้นคุณไม่ต้องกำหนดค่าเริ่มต้นทุกครั้งสิ่งนี้จะช่วยให้ประสิทธิภาพโดยรวม!
Falco

ทำไมการเปลี่ยนแปลงในการกระจาย?
Peter Taylor

4

อินเทรนด์

อินเทรนด์จะดูที่การเคลื่อนไหวที่ผ่านมาของฝ่ายตรงข้าม เดาน้ำหนักและเลือกหนึ่งขยับขึ้นเล็กน้อยจากนั้น นี่คือในรัศมีภาพทั้งหมด:

public class Trendy implements Player{public String getName(){return "Trendy";}public int[]getMove(String[]a){float h=0,c[]=new float[6];int i=0,l=a[3].length(),p=0;for(;i<l;)c[a[3].charAt(i++)-48]+=(float)i/l;for(i=0;i<6;i++)if(c[i]>h)h=c[p=i];return new int[]{(p+2)%6,p};}}    

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


7
Could you please format the code onto multiple lines? This isn't code golf...
mbomb007

7
@mbomb007 It takes up less space this way. One of the pains of KotHs in general is all the scrolling to look at entries. I've described what it does, and it's very straightforward for interested parties to format it.
Geobits

4

Random Guesser

This is really straight-forward. It effectively rolls a d6, and adds another roll to the previous roll for its guess. It won't win, but it'll provide a nice benchmark.

import java.util.Random;

public class RandomGuesser implements Player {
    private final Random rnd = new Random();
    public String getName() { return "RandomGuesser"; }

    public int[] getMove(String[] args) {
        return new int[] { rnd.nextInt(6), rnd.nextInt(6) };
    }
}

4

Confused, Python 3

An unnecessarily complicated entry. Even I don't know what it does.

import sys
from random import *

if len(sys.argv) == 7:
    mn,mg,ms,on,og,os = [list(map(int, v)) for v in sys.argv[1:]]
    s,t = sum(mn+on)%5, sum(mg+og)%5
    n = [0]*3+list(range(6))*5+[5,0,5]
    m = [1,0,5,4]+n[:-2:s//9+1]
    numoptions = [n.extend(n[i+s::5+t]+[i]*i*(6+t)) for i in n[:]] and n
    guessoptions = [m.extend(m[i+t//2::8]+[i]*i*(5+s)) for i in m[:]] and m
    num = choice(numoptions)
    guess = choice(guessoptions)
else:
    num, guess = randint(0, 5), randint(0, 5)

sys.stdout.write('%u %u\n' % (num, guess))

Although this advanced algorithm seems to perform worse than random in this tournament, and uses significant memory and run-time, it has stunning results for certain values of 5 ;-)


4

Rainbolt

Takes the difference between the last two numbers our opponent guessed, adds that to our opponent's latest guess, finds the modulus, and avoids choosing that number at all costs. For example, if you guess {5,4,3} (decreasing by one) then we would avoid choosing 2 at all costs.

Takes the difference between the last two numbers our opponent chose, adds that to our opponent's latest choice, and guesses that number. For example, if you guess {1,4,5,2} (increasing by threes) then we would guess 5.

Avoids pointless or very close to pointless rolls.

public class Rainbolt implements Player {

    public String getName() { 
        return "Rainbolt"; 
    }

    public int[] getMove(String[] args) {
        int[] yourChoices = toIntArray(args[3]);
        int[] yourGuesses = toIntArray(args[4]);

        int myChoice;
        if (yourGuesses.length > 1) {
            int latest = yourGuesses[yourGuesses.length - 1];
            int secondLatest = yourGuesses[yourGuesses.length - 2];
            int numberToAvoid = (2 * latest - secondLatest + 6) % 6;
            do {
                myChoice = rollRandom();
            } while (myChoice == numberToAvoid);
        } else { 
            myChoice = rollRandom();
        }

        int myGuess;
        if (yourChoices.length > 1) {
            int latest = yourChoices[yourChoices.length - 1];
            int secondLatest = yourChoices[yourChoices.length - 2];
            myGuess = (2 * latest - secondLatest + 6) % 6;
        } else { 
            myGuess = rollRandom();
        }

        if ((myChoice + myGuess) < 3) {
            do {
                myGuess = rollRandom();
            } while ((myChoice + myGuess) < 3);
        }

        return new int[] { myChoice, myGuess };
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }

    private static int rollRandom() {
        return (int) (Math.random() * 6);
    }
}

Don't make your getMove() method static. You can't implement a non-static method like that (at least not in Java 8).
GiantTree

@GiantTree Thanks for catching that.
Rainbolt

3

Evolved Bot

I evolved this bot to be the best random based bot.

import java.util.Arrays;

public class EvolvedBot implements Player {

    private static final double MUTATION_RATE = .2;
    private static final double CROSS_OVER_RATE = .5;

    private final double[] pickProbabilities;
    private final double pickSum;
    private final double[] guessProbabilities;
    private final double guessSum;

    public EvolvedBot(){
        this(new double[]{1.0069058661897903, 0.8949716031797937, 0.5249198534098369, 0.437811964976626, 0.2630925750209125, 0.4862172884617061},
                new double[]{0.6336558074769376, 0.13700756148363913, 0.9586621925124863, 0.11223159366330251, 0.8931390659502754, 0.662974949440039});
    }

    public EvolvedBot(double[] pickProbabilities, double[] guessProbabilities) {
        this.pickProbabilities = pickProbabilities;
        this.guessProbabilities = guessProbabilities;
        pickSum = Arrays.stream(pickProbabilities).sum();
        guessSum = Arrays.stream(guessProbabilities).sum();
    }

    @Override
    public String getName() {
        return "EvolvedBot"/* + ": " + Arrays.toString(pickProbabilities) + Arrays.toString(guessProbabilities)*/;
    }

    @Override
    public int[] getMove(String[] args) {
        int[] move = new int[]{5, 5};
        double pick = Math.random() * pickSum;
        double guess = Math.random() * guessSum;
        for (int i = 0; i < 6; i++){
            if (pick >= 0) {
                pick -= pickProbabilities[i];
                if (pick < 0) {
                    move[0] = i;
                }
            }
            if (guess >= 0){
                guess -= guessProbabilities[i];
                if (guess < 0){
                    move[1] = i;
                }
            }
        }
        return move;
    }

    public EvolvedBot mutate(double mutationRate){
        double[] pickProbabilities = Arrays.copyOf(this.pickProbabilities, 6);
        double[] guessProbabilities = Arrays.copyOf(this.guessProbabilities, 6);

        for (int i = 0; i < 6; i++){
            pickProbabilities[i] = Math.max(pickProbabilities[i] + (Math.random() * 2 - 1) * mutationRate, 0);
        }

        for (int i = 0; i < 6; i++){
            guessProbabilities[i] = Math.max(guessProbabilities[i] + (Math.random() * 2 - 1) * mutationRate, 0);
        }

        return new EvolvedBot(pickProbabilities, guessProbabilities);
    }

}

3

Popularity, Python 3

Compute guess based on popular numbers used in the past by the opponent. The numbers used recently have more weight. The number choice is often the same as the guess.

import sys
from random import *

if len(sys.argv) == 7:
    mn,mg,ms,on,og,os = [list(map(int, v)) for v in sys.argv[1:]]
    n = list(range(6))
    guess = choice(n + on[-100:] + on[-20:]*8)
    num = choice(n + [guess]*6)
else:
    num, guess = randint(0, 5), randint(0, 5)

sys.stdout.write('%u %u\n' % (num, guess))

3

Interpolator

(Switched to Java since Python was causing problems)

Uses polynomial interpolation on the last 10 opponent choices to work out the opponent's next number, then does the same to its own choices and avoids choosing that number. Also, Interpolator has a slight bias against choosing 0 or 5, and its choice is sometimes affected by its guess:

  • If it guesses 0 it will never choose 0
  • If it guesses 5 it will always choose 0 or 1
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;

public class Interpolator implements Player
{
    private final int TAIL_LENGTH = 10;

    public String getName()
    {
        return "Interpolator";
    }

    public int[] toInts(String s) {
        char [] chars = s.toCharArray();
        int[] ints = new int[chars.length];

        for (int i = 0; i < chars.length; i++){
            ints[i] = Integer.parseInt(Character.toString(chars[i]));
        }

        return ints;
    }

    public int mod6(int n) {
        n = n % 6;
        if (n < 0) { n += 6; }
        return n;
    }

    public int interpolate(int[] nums){
        boolean allEqual = true;

        for (int i = 0; i < nums.length; i++){
            if (nums[i] != nums[0]){
                allEqual = false;
            }
        }

        if (allEqual) {
            return nums[0];

        } else {
            int [] newNums = new int[nums.length - 1];

            for (int i = 0; i < nums.length - 1; i++){
                newNums[i] = nums[i+1] - nums[i];
            }

            return nums[nums.length - 1] + interpolate(newNums);
        }
    }

    public int[] tail(int[] nums) {
        int minLength = Math.min(TAIL_LENGTH, nums.length);
        int[] tailArray = new int[minLength];

        for (int i = 0; i < minLength; i++){
            tailArray[i] = nums[nums.length - minLength + i];
        }

        return tailArray;
    }

    public int[] getMove(String [] args)
    {
        Random r = new Random();

        if (args[0].length() == 0){
            return new int[] {r.nextInt(5), r.nextInt(5)};
        }

        int[] myChoices = toInts(args[0]);
        int[] opponentChoices = toInts(args[3]);
        int[] opponentGuesses = toInts(args[4]);

        int guess = mod6(interpolate(tail(opponentChoices)));
        int avoid = mod6(interpolate(tail(myChoices)));

        if (guess == 5){ return new int[] {r.nextInt(2), 5}; }

        int[] choiceArray = {0, 1, 1, 2, 2, 3, 3, 4, 4, 5};
        ArrayList<Integer> choices = new ArrayList<Integer>();
        for (int i = 0; i < choiceArray.length; i++) { choices.add(choiceArray[i]); }

        choices.removeAll(Collections.singleton((Integer) avoid));
        if (guess <= 0) { choices.removeAll(Collections.singleton((Integer) 0)); }
        int choice = choices.get(r.nextInt(choices.size())); 

        return new int[] {choice, guess};
    }
}

3

CounterBot

Does not counter anyone but rather counts through 0-5 in a circle (0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4 ...)

import java.util.Random;

public class Counter implements Player {

    int lastChoice = new Random().nextInt(6); //Chooses a random starting number

    public String getName() {
        return "CounterBot";
    }

    public int[] getMove(String[] args) {
        int[] oChoices = new int[6]; //Array to store the amount of individual choices of the opponent

        for (int i = 0; i < args[3].length(); i++) {
            int index = Integer.parseInt(String.valueOf(args[3].charAt(i))); //get that choice
            oChoices[index]++; //Increment the number corresponding the choice
        }
        int guess = 0, last = 0;
        for (int i = 0; i < oChoices.length; i++) { //Increment over the choices' array
            if (oChoices[i] > last) { //If the number has been chosen more often than the one before
                last = oChoices[i]; //Set the new maximum value (later the last maximum value)
                guess = i; //Set it as the next guess
            }
        }
        lastChoice++; //Increment our choice
        lastChoice %= 6; //Make sure it's within the bounds of 0-5 ie. modulo 6 (6 modulo 6 = 0)
        return new int[]{lastChoice, guess}; //return our choice and guess
    }
}

2

Basilisk, Python

According to legend, The Basilisk is the king of the serpents. (source) I figured that's an appropriate name for a bot that plays "The Noble Game Of Kings" and is written in python. =D This bot strikes fear into the heart of the other bots, and causes death with a single glance.

import sys
import random

args = sys.argv
argc = len(args)
if argc < 6:
    sys.exit()

myChoices = args[1]
myGuesses = args[2]
myScore = args[3]
opponentChoices = args[4]
opponentGuesses = args[5]
opponentScore = args[6]

if len(myChoices) == 0:
    print (random.randint(0, 5))
    print (random.randint(0, 5))
    sys.exit()

guesses = [0, 0, 0, 0, 0, 0]
for char in opponentGuesses:
    i = int(char)
    guesses[i] += 1

#Will default towards smaller guesses to minimize opponent winnings
#For example, if the guess list is
#[5, 3, 7, 3, 4, 8]
#This will return 1. (index of the first 3)
myNextMove = guesses.index(min(guesses))

list = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
i = 0

while i < len(myGuesses) - 1:
    myGuess = int(myGuesses[i])
    opponentResponse = int(opponentChoices[i+1])
    list[myGuess][opponentResponse] += 1
    i += 1

myPreviousGuess = int(myGuesses[-1])
relevantList = list[myPreviousGuess]

#Defaults towards higher moves.
#For example, if the list is
#[3, 8, 6, 8, 0, 7]
#This will return 3 (index of the last 8)
highestValue = -1
highestIndex = -1
for i in range(len(relevantList)):
    if relevantList[i] >= highestValue:
        highestValue = relevantList[i]
        highestIndex = i


myNextGuess = highestIndex

print (myNextMove)
print (myNextGuess)

This runs on a pretty simple strategy. I'm not expecting it to win, but it was fun to write. This is also my first KoTH challenge, so I'm excited to see how well it performs.

How it picks its next move.

The Basilisk always makes the move that his opponent has guessed the least number of times. In case of a tie, he will pick the smaller number. (to minimize the opponents number of points.)

How it picks its next guess.

The Basilisk will pick the most likely response to its previous guess. For example, If last time, it guessed a 3, it will go back through all of the previous times that it has guessed a 3, and then return the most common opponent move that comes after a guess of 3. In case of a tie, it will pick the larger number (to maximize the number of points it could make.)

On a technical note, will this run correctly? Is print() sufficient, or should I use something like sys.stdout.write() like the other Pythonistas have done?


sys.stdout.write() works in either Python. print() works only in Python 3. It should be okay though.
TheNumberOne

No, print() works in either, I'm sure of it. Parentheses are optional in 2.x
DJMcMayhem

According to this, they work differently. However, the way you use it, it doesn't matter.
TheNumberOne

But does that make any difference?
DJMcMayhem

Apparently not.
TheNumberOne

2

Ditto

This turns into the opponent, but behind by one guess/choice.

import java.util.Random;

public class Ditto implements Player {
    private final Random rnd = new Random();
    public String getName() { return "Ditto"; }

    // myChoices myGuesses myScore oppChoices oppGuesses oppScore
    public int[] getMove(String[] args) {
        if(args[0] == null || args[0].isEmpty()) {
            return new int[] { rnd.nextInt(6), rnd.nextInt(6) };
        }
        int[] myChoices = toIntArray(args[0]);
        int[] myGuesses = toIntArray(args[1]);
        //int myScore = Integer.parseInt(args[2]);
        int[] oppChoices = toIntArray(args[3]);
        int[] oppGuesses = toIntArray(args[4]);
        //int oppScore = Integer.parseInt(args[5]);

        return new int[] { oppChoices[oppChoices.length-1], oppGuesses[oppGuesses.length-1] };
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }
}

1

NullifierBot, Java

Always throws 0 to minimize any opponent winnings. If the opponent ever guesses my number, they only earn what they threw.

Always guesses 5 to maximize my winnings. Since I can't get any points from my throw, I want to get as many from the opponent. I could randomly guess, but where's the fun in that?

public class NullifierBot implements Player
{
    public String getName()
    {
        return "NullifierBot";
    }

    public int[] getMove(String [] args)
    {
        // always throws 0 to minimize opponents score
        // always guesses 5 to maximize my score
        return new int[] {0, 5}; 
    }
}

I'm guessing this bot will do terribly. Any bot using odds will maybe even get every single guess right after the first.
mbomb007

@mbomb007 It's not the worst, though! Although it performs worse than your RandomBot.
Brian J

1

Erratica, Java

Not great, but it was originally designed to be mostly random, until the value of the trade-off popped out at me. Manages to lose consistently vs. Counter Bot >_<

import java.util.Random;
class Erratica implements Player
{
    private final Random rnd = new Random();

    public String getName() {
        return "Erratica";
    }

    public int[] getMove(String[] args) {
        if(args[0] == null || args[0].isEmpty())
        {
            return new int[]{rnd.nextInt(4)/3+4,rnd.nextInt(4)/3};
        }
        int[] myChoices = toIntArray(args[0]);
        int[] myGuesses = toIntArray(args[1]);
        int myScore = Integer.parseInt(args[2]);
        int[] opponentChoices = toIntArray(args[3]);
        int[] opponentGuesses = toIntArray(args[4]);
        int opponentScore = Integer.parseInt(args[5]);
        int round = opponentChoices.length + 1;
        int choice=0;
        int guess=0;
        if(round<7)
        {
            if(rnd.nextFloat()<(0.1f*(float)round-0.1f))
            {
                choice=(opponentChoices[round-2]+opponentGuesses[round-2])%6;
            }else
            {
                choice=rnd.nextInt(6);
            }
            if(rnd.nextFloat()<(0.1f*(float)round-0.1f))
            {
                guess=opponentChoices[round-2];
            }else
            {
                guess=rnd.nextInt(6);
            }
            return new int[]{choice, rnd.nextInt(6)/5*(5-choice-guess)+guess};
        }else
        {
            int lastError=Math.abs(opponentGuesses[round-2]-myChoices[round-2]);
            for(int i=round-2; i>round-8;i--)
            {
                if(lastError<rnd.nextInt(6))
                {
                    lastError++;
                }else
                {
                    lastError--;
                }
                if(lastError<0)
                    lastError+=6;

            }
            lastError = lastError%6; //shouldn't change
            switch(rnd.nextInt(4))
            {
                case 0:
                    choice=(myChoices[round-2-lastError-round/10])%6;
                    break;
                case 1:
                    choice=(myChoices[lastError+round/10])%6;
                    break;
                default:
                    choice = rnd.nextInt(6);
                    break;
            }

            lastError=Math.abs(myGuesses[round-2]-opponentChoices[round-2]);
            for(int i=round-2; i>round-8;i--)
            {
                if(lastError<rnd.nextInt(6))
                {
                    lastError++;
                }else
                {
                    lastError--;
                }
                if(lastError<0)
                    lastError+=6;
            }
            lastError = lastError%6; //shouldn't change
            switch(rnd.nextInt(4))
            {
                case 0:
                    guess=(opponentChoices[round-2-lastError-round/10])%6;
                    break;
                case 1:
                    guess=(opponentChoices[lastError+round/10])%6;
                    break;
                default:
                    guess = rnd.nextInt(4);
                    break;
            }
        }

        if(myScore>opponentScore)
            switch(rnd.nextInt(2)){
                case 0:
                    choice=5-guess;
                    break;
                case 1:
                    guess=5-choice;
                    break;
                default:
                    break;
            }
        return new int[]{choice, guess};
    }

    private static int[] toIntArray(String arg) {
        int[] result = new int[arg.length()];
        for (int i = 0; i < arg.length(); i++)
            result[i] = Character.getNumericValue(arg.charAt(i));
        return result;
    }
}

1

Echo, Ruby

mychoices, myguesses, myscore, opponentchoices, opponentguesses, opponentscore = $*

unless mychoices
 puts "0 5"
 exit
end

if mychoices.size > 990 && myscore == '0'
  nextchoice = rand(1..5)
else
  nextchoice = opponentchoices[-1].to_i
end

recentchoices = opponentchoices[/.{0,100}$/]

nextguess = (0..5).max_by do |choice|
  (recentchoices.count(choice.to_s)+1) * (nextchoice + choice)
end

puts "%s %s"%[nextchoice,nextguess]

Plays the last play the opponent made, on the theory that anybody can make a bot that they cannot predict. Guesses based on expectation value using a hundred-move sample.


I'm getting this error: echo.rb:3:in <main>': undefined method size' for nil:NilClass (NoMethodError). It seems to occur only on the first round, when there's no move history.
PhiNotPi

Odd, didn't happen when I tested. I'll edit.
histocrat

What is the relevance of the if (mychoices.size > 990 && myscore == '0') nextchoice = rand(1..5) part?
randomra

If it's about to end up in a scoreless tie (as would happen, for example, against itself), it starts playing randomly, since ~50% chance of a win is better than nothing.
histocrat

1

KING FISHER

    import java.util.Random;
public class KingFisher {

    private Random rnd = new Random();
    private int wins = 0;
    private int loses = 0;
    private int median = 0;
    private int medianMoved = 0;
    private int[] weightedLow = {40,65,80,90,95};
    private int[] weightedHigh = {5,15,30,55,95};
    private boolean highWeightMethod = true;

    public String getName() {
        return "KingFisher";
    }

    public int[] getMove(String [] args)
    {
        char[] mc  = args[0].toCharArray();
        char[] mg  = args[1].toCharArray();
        char[] oc  = args[3].toCharArray();
        char[] og  = args[4].toCharArray();
        int len = mc.length;
        int currentGuess = 0;
        int currentChoice = 0;
        if(len < 10)
            return new int[] {rnd.nextInt(6),rnd.nextInt(6)}; 
        int[] guessWeight = {0,0,0,0,0,0};
        int[] guessWeightTotal = {0,0,0,0,0,0};
        for(int a = 0; a< len;a++)
            guessWeight[oc[a]-48]++;
        if(!highWeightMethod){

            int[] whiteList = {1,1,1,1,1,1};
            for(int b = 0;b<3;b++){

                int min = 0;
                int max = 0;
                int minIndex = 0;
                int maxIndex = 0;
                for(int a = 0;a<6;a++){

                    if(whiteList[a] == 1){

                        min = guessWeight[a];
                        max = guessWeight[a];
                        minIndex = a;
                        maxIndex = a;
                        break;
                    }
                }

                for(int a = 0; a<6;a++){

                    if(whiteList[a] == 1){

                        if(guessWeight[a]<min){

                            min = guessWeight[a];
                            minIndex = a;
                        }
                        if(guessWeight[a]>max){

                            max = guessWeight[a];
                            maxIndex = a;
                        }
                    }
                }
                guessWeight[maxIndex] = min;
                guessWeight[minIndex] = max;
                whiteList[maxIndex] = 0;
                whiteList[minIndex] = 0;
            }
        }

        for(int a = 0; a< 6;a++)
            for(int b = 0; b<=a;b++)
                guessWeightTotal[a]+=guessWeight[b];
        int randInt = rnd.nextInt(guessWeightTotal[5]);
        for(int a = 0; a<6;a++){

            if(randInt < guessWeightTotal[a]){
                currentGuess = a;
                break;
            }
        }

        if(mg[len-1] == oc[len-1]){
            wins++;
            median++;
        }
        if(og[len-1] == mc[len-1]){
            loses++;
            median--;
        }
        if(median > 2){

            medianMoved++;
            median = 0;
        }
        if(median < -2){

            medianMoved--;
            median = 0;
        }

        randInt = rnd.nextInt(95);
        if((wins-medianMoved)>(loses+medianMoved)){

            for(int a = 0; a<6;a++){

                if(randInt < weightedLow[a]){
                    currentChoice = a;
                    break;
                }
            }
        }
        else{

            for(int a = 0; a<6;a++){

                if(randInt < weightedHigh[a]){
                    currentChoice = a;
                    break;
                }
            }
        }
        if(medianMoved < -5){

            highWeightMethod = !highWeightMethod;
            medianMoved = 0;
        }
        return new int[] {currentChoice,currentGuess}; 

    }
}

This guy consist out of bad guessing alghorithms that use weighted arrays mostly.


Will be in next update.
PhiNotPi

1

Uh uh. I know what you're thinking. "Is he gonna pick five or something else?" Well to tell you the truth in all this excitement I'm kinda not sure myself, but being this is a .44 Method, the most powerful method in the world and would overload your stack right away, you've gotta ask yourself one question: "Do I feel lucky?"

Well, do ya, punk?

public class DirtyHarry implements Player {

    @Override
    public String getName() {
        return "DirtyHarry";
    }

    @Override
    public int[] getMove(String[] args) {
        return new int[]{5, 5};
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.