กลยุทธ์บงการ


19

ฉันสามารถพบกับความท้าทายเกี่ยวกับรหัสสำหรับ Mastermind ได้เท่านั้นดังนั้นนี่เป็นเวอร์ชันที่ท้าทายรหัสที่ฉันอยากทำกับตัวเอง

กลยุทธ์ที่ดีที่สุดสำหรับเกม Mastermind ปกติ MM (4,6) ถูกค้นพบโดย Koyama และ Lai ในปี 1993 โดยมีการเดา # โดยเฉลี่ย = 5625/1296 ~ 4.34 MM (5,8) ยังคงไม่ได้รับการแก้ไข แต่คาดว่าจะมีค่าเฉลี่ย # ของการเดา ~ 5.5

งานของคุณคือการสร้างกลยุทธ์ MM (5,8) นั่นคือสำหรับ 5 หลุมและ 8 สีครอบคลุมpow(8,5) = 32768โซลูชั่นที่แตกต่างที่เป็นไปได้ทั้งหมด เห็นได้ชัดว่ามันไม่จำเป็นต้องดีที่สุด คุณมีสองทางเลือก:

  1. โพสต์โปรแกรมกำหนดขึ้นที่สร้างกลยุทธ์ โปรแกรมจะต้องสามารถคอมไพล์ได้ / รันได้บน Windows 7, Mac OS X หรือ Linux โดยไม่ต้องใช้ซอฟต์แวร์พิเศษใด ๆ
  2. เผยแพร่กลยุทธ์ของคุณ (พร้อมกับชื่อ StackExchange ของคุณ) ที่ใดที่หนึ่งบนอินเทอร์เน็ตและโพสต์ URL ที่นี่

ในทั้งสองกรณีให้ระบุคะแนน (ดูด้านล่าง) ในส่วนหัวของคำตอบ

กลยุทธ์จะต้องเข้ารหัสตามไวยากรณ์ต่อไปนี้:

strategy : guessing-strategy | known-solution-strategy
guessing-strategy : '{' guess ':' branches '}'
known-solution-strategy : guess
guess : color color color color color
color : 'A'..'H'
branches : '{' branch (',' branch)* '}'
branch : reply ':' strategy
reply : number-of-blacks number-of-whites
number-of-blacks : number-of-key-pegs
number-of-whites : number-of-key-pegs
number-of-key-pegs : '0'..'5'

อัลกอริทึมที่ใช้ในการตัดสินใจจำนวนหมุดสีดำ / ขาวมีการอธิบายไว้ในhttp://en.wikipedia.org/wiki/Mastermind_(board_game)

โปรดทราบว่าการตอบ "50" (เช่นการคาดเดาที่ถูกต้อง) นั้นจะบอกเป็นนัยและไม่ได้เป็นส่วนหนึ่งของไวยากรณ์

เกณฑ์การให้คะแนน: N = ผลรวมของจำนวนการเดาสำหรับแต่ละพา ธ / โซลูชัน 32768 กลยุทธ์ที่มีค่า N ต่ำสุดชนะ First tie-break: จำนวนการทายต่ำสุดที่ต่ำที่สุด tie-break ที่สอง: คำตอบที่โพสต์แรก การแข่งขันจบลง1 สิงหาคม 2014 00:00 GMT


ตัวอย่างของกลยุทธ์สำหรับ MM (2,3) ด้วยคะแนน = 21:

{AB:{10:{AC:{10:AA,01:CB,00:BB}},02:BA,01:{BC:{01:CA}},00:CC}}

เมื่อใช้กลยุทธ์นี้เกมที่เป็นไปได้ 9 เกมจะเป็นดังนี้:

  • AB 20
  • AB 10, AC 20
  • AB 10, AC 10, AA 20
  • AB 10, AC 01, CB 20
  • AB 10, AC 00, BB 20
  • AB 02, BA 20
  • AB 01, BC 20
  • AB 01, BC 01, CA 20
  • AB 00, CC 20

ในไม่ช้าฉันจะโพสต์เครื่องมือตรวจสอบกลยุทธ์ MM (5,8) บน Java เพื่อความสะดวกของคุณ


ฉันมีความยากลำบากในการทำความเข้าใจวิธีการใช้กลยุทธ์ตัวอย่าง MM (2,3) คุณสามารถโพสต์เกมตัวอย่างอธิบายกลยุทธ์ได้หรือไม่

@tolos ฉันเพิ่มทั้งหมด 9 :)
MrBackend

มันจะดีมากถ้าผู้ตรวจสอบของคุณสามารถให้คะแนนได้เช่นกัน!
ไม่ใช่ว่า Charles

@Charles จะทำ!
MrBackend

2
คุณยินดีที่จะเปลี่ยนไวยากรณ์ของคุณเพื่อให้การตอบสนองเดียวกันกับการรวมกันของหมุดกุญแจหลายชุดหรือไม่? ชอบ{AB:{10|01:BB}}ไหม ฉันมีคำตอบ แต่มันค่อนข้างไร้เดียงสาและเนื่องจากโครงสร้างของไวยากรณ์มันไม่ได้ปรับขนาดได้เลย (4 หลุม 3 สีสร้างกลยุทธ์ 147MB ซึ่งฉันสามารถตัดลงได้อย่างมากโดยการรวมส่วนต่าง ๆ ของ ต้นไม้).
Martin Ender

คำตอบ:


6

ชวา

อัลกอริทึมของฉันสำหรับ MM (5,8) คะแนนด้วย177902 178006 182798 182697ด้วยความลึกสูงสุด8 9และต้องการเพียงไม่กี่วินาที (บนคอมพิวเตอร์ที่ช้า)

ตัวอย่างผลลัพธ์ของกลยุทธ์สำหรับ MM (2,3) ที่มีคะแนน = 21 พบโดยอัลกอริทึมนี้มีลักษณะดังนี้:

{BC:{00:AA,01:AB:{01:CA},02:CB,10:AC:{00:BB,01:BA,10:CC}}}

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

  1. สร้างชุดเริ่มต้น S0 ของรหัสที่เป็นไปได้ทั้งหมดเพื่อให้เป็นชุดปัจจุบัน S
  2. Codebreaker พบการเดาที่ดีสำหรับ S. การเดาแต่ละครั้งนำไปสู่พาร์ติชัน P ของ S ซึ่งแต่ละชุดย่อย S 'รวบรวมรหัสทั้งหมด (จาก S) ที่มีการตอบกลับแบบเดียวกันในการเดา การเดาที่ดีมีพาร์ติชันที่ดีเนื่องจากเป็นข้อมูลที่ให้เดาได้มากที่สุด
  3. ใช้การเดาที่ดีและ P สำหรับแต่ละอันที่ไม่ได้ใช้ S 'ใน P จะใช้ตัวถอดรหัสซ้ำ (ขั้นตอนที่ 2)

@MrBackend: การเขียนตัวตรวจสอบเป็นเรื่องยากฉันเดา ;-)

import java.util.TreeMap;
import java.util.Vector;

public class MM {
    Vector<String> codeset = new Vector<String>();
    String guess;
    TreeMap<Integer, MM> strategy = new TreeMap<Integer, MM>();

    public String toString() {
        String list="";
        for (Integer reply: strategy.keySet()) {
            if (strategy.get(reply)!=null) list+=(list.length()>0?",":"")+(reply<10?"0":"")+reply+":"+strategy.get(reply);
        }
        if (list.length()>0) return guess+":{"+list+"}"; else return guess;
    }

    MM() { }

    MM(int h, int c) {
        for (int i = 0; i < Math.pow(c, h); i++) {
            String code = "";
            for (int j = 0, p=i; j < h; j++) {
                code+="ABCDEFGH".charAt(p%c);
                p/=c;
            }
            codeset.add(code);
        }
    }

    int replyAccordingToDonaldKnuth(String secret, String guess) {
        int black=0;
        int totalHitsBlackAndWhite=0;
        for (char n = 'A'; n <= 'H'; n++) {
            int a=0, b=0;
            for (int i = 0; i < secret.length(); i++) {
                if (secret.charAt(i)==n) a++;
                if ( guess.charAt(i)==n) b++;
            }
            totalHitsBlackAndWhite+=Math.min(a, b);
        }
        for (int i = 0; i < secret.length(); i++) {
            if (secret.charAt(i) == guess.charAt(i)) black++;
        }
        return 10 * black + (totalHitsBlackAndWhite-black);
    }

    int reply(String secret, String guess) {
        return replyAccordingToDonaldKnuth(secret, guess);
    }

    MM codebreaker(Vector<String> permuts) {
        int fitness=0;
        MM protostrategy=null;
        for (int greedy = 0; greedy < Math.min(permuts.size(), 200); greedy++) {
            MM tmp=partition(permuts, permuts.get(greedy));
            int value=tmp.strategy.size();
            if (fitness<=value) {
                fitness=value;
                protostrategy=tmp;
                protostrategy.guess=permuts.get(greedy);
            }
        }
        if (protostrategy!=null) {
            for (Integer reply: protostrategy.strategy.keySet()) {
                protostrategy.strategy.put(reply, codebreaker(protostrategy.strategy.get(reply).codeset));
            }
        }
        return protostrategy;
    }

    MM partition(Vector<String> permuts, String code) {
        MM protostrategy=new MM();
        for (int c = 0; c < permuts.size(); c++) {
            int reply=reply(permuts.get(c), code);
            if (!protostrategy.strategy.containsKey(reply)) protostrategy.strategy.put(reply, new MM());
            if (permuts.get(c)!=code) protostrategy.strategy.get(reply).codeset.add(permuts.get(c));
        }
        return protostrategy;
    }

    public static void main(String[] args) {
        MM mm = new MM(5,8);
        System.out.println("{"+mm.codebreaker(mm.codeset)+"}");
    }
}

ข้อสังเกตบางส่วน:

  1. ไม่จำเป็นต้องมีการตรวจสอบความสอดคล้องเนื่องจากชุด S และพาร์ติชันของพวกเขาสร้างในลักษณะที่สอดคล้องกัน (อัตโนมัติ -)
  2. การเลือกเดาที่ดีจาก S0 (แทน S) เป็นไปได้ แต่ฉันไม่ปฏิบัติตามวิธีนี้ในรหัสปัจจุบัน
  3. การค้นหาโลภของฉันถูกตัดแต่งโดยใช้ความพยายาม 200 ครั้ง
  4. ฉันรู้ว่า "การให้ข้อมูลมากที่สุดสำหรับการเดา" นั้นไม่แม่นยำมากนัก แนวคิดง่าย ๆ คือเลือกพาร์ติชันที่มีจำนวนชุดย่อยมากที่สุด
  5. ผลลัพธ์จะขึ้นอยู่กับว่าคุณคำนวณการตอบกลับอย่างไร (.. ) ในที่สุดฉันก็ปรับการแสดงออกของ Donald Knuth

กลยุทธ์สำหรับสามารถพบได้ที่นี่MM(5,8) GitHub มีปัญหาบางอย่างที่แสดงเส้นที่ยาวดังนั้นคลิกที่ปุ่มRaw


สวัสดีวิธีการพิมพ์ ข้อความgithub ที่สวยงามเพื่อให้ผลลัพธ์สามารถเปลี่ยนเป็นคู่มือการค้นหา .. จากจุดเริ่มต้นแรก 'HHCAA' .. และขั้นตอนต่อไปขึ้นอยู่กับการตอบกลับ ... และต่อไปเรื่อย ๆ รูปแบบข้อความดิบในปัจจุบันมันไม่ง่ายสำหรับการสแกนด้วยตนเอง .. เทคนิคที่ฉันใช้อยู่คือวิธีการแยกวงเล็บที่ซ้อนกันและรับเค้าโครงตารางที่ดีที่ง่ายต่อการติดตามจนถึงจุดสิ้นสุดคล้ายกับรายการหัวข้อย่อยในคำถาม สำหรับ MM (2,3) ขอขอบคุณ. หวังว่าคุณจะเข้าใจสิ่งที่ฉันเป็นหลังจาก (ชอบ python ในการแยกวิเคราะห์ str)
ihightower

2

ทับทิม

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

ดังนั้นนี่คือความพยายามหนึ่งที่จะทำให้เรื่องนี้ดำเนินต่อไป มันค่อนข้างไร้เดียงสา (และไม่ค่อยชัดเจน - ช่วยในการอ่าน if / elsif / สาขาอื่นจากล่างขึ้นบน)

Holes, Colors = ARGV.map &:to_i

ColorChars = ('A'..'H').to_a

def is_possible(guess, blacks, result)
    blacks == guess.chars.zip(result.chars).count {|chars| chars[0] == chars[1]}
end

def print_strategy(known_colors, remaining_permutations, next_color)
    char = ColorChars[next_color]
    if remaining_permutations
        guess = remaining_permutations[0]
        print guess
        if remaining_permutations.length > 1
            print ':{'
            (Holes-1).times do |i|
                new_permutations = (remaining_permutations - [guess]).select { |perm| is_possible(guess, i, perm) }
                next if new_permutations.empty?
                print "#{i}#{Holes-i}:"                
                print '{' if new_permutations.length > 1
                print_strategy(known_colors, new_permutations, next_color)
                print '}' if new_permutations.length > 1
                print ',' if i < Holes-2
            end
            print '}'
        end
    elsif known_colors.length == Holes
        print_strategy(known_colors, known_colors.chars.permutation.map(&:join).uniq, next_color)
    elsif next_color == Colors-1
        print_strategy(known_colors+char*(Holes - known_colors.length), remaining_permutations, next_color+1)
    else
        print char*Holes, ':{'

        (Holes - known_colors.length + 1).times do |i|
            break if i == Holes
            print "#{i}0:"
            print '{' if next_color < Colors-2 || i > 0 || known_colors.length > 0
            print_strategy(
                known_colors+char*i,
                remaining_permutations,
                next_color+1
            )
            print '}' if next_color < Colors-2 || i > 0 || known_colors.length > 0
            print ',' if i < (Holes - known_colors.length) && i < Holes-1
        end
        print '}'
    end
end

print '{'
print_strategy('', nil, 0)
puts '}'

ก่อนอื่นฉันลอง 5 สีแต่ละสี: AAAAA,, BBBBBฯลฯ จากนั้นฉันก็หาว่าสีใดที่ใช้ในลวดลาย จากนั้นฉันก็ลองเปลี่ยนสีตามสีที่กำหนดโดยไม่ใช้หมุดสีดำที่ถูกตัดออกไปแล้ว

นี่คือMM(2,3)กลยุทธ์:

{AA:{00:{BB:{00:CC,10:{BC:{02:CB}}}},10:{BB:{00:{AC:{02:CA}},10:{AB:{02:BA}}}}}}

กลยุทธ์สำหรับMM(5,8)ใช้เวลา 376KB และสามารถพบได้ที่นี่ GitHub มีปัญหาบางอย่างที่แสดงเส้นที่ยาวดังนั้นคลิกที่ปุ่มRaw

ตอนนี้ถ้าฉันได้รับการตรวจสอบฉันสามารถบอกคุณได้ว่าคะแนนจริงของฉันคืออะไร :)


รู้สึกไม่ดีเกี่ยวกับผู้ตรวจสอบที่ยังไม่เผยแพร่ แต่มันกำลังมา ... มีบางอย่างผิดปกติกับกลยุทธ์ (แรก) MM (2,3) ของคุณตัวอย่างเช่นหากการแก้ปัญหาคือ AB: Guess = AA; ตอบ = 10; เดา = BB; ตอบกลับ = 10 ซึ่งไม่มีกลยุทธ์ ฉันจะพิจารณาข้อเสนอแนะของคุณเกี่ยวกับการจัดกลุ่มคำตอบ แต่ไม่ควรจำเป็นสำหรับกลยุทธ์ที่ดีเนื่องจากชุดของวิธีแก้ปัญหาที่เป็นไปได้นั้นไม่ได้รวมไว้สำหรับคำตอบที่แตกต่างกัน
MrBackend

@MrBackend ดีจับฉันข้ามกรณีมี มันควรได้รับการแก้ไขแล้ว สำหรับไวยากรณ์แน่นอนว่ามันไม่จำเป็นสำหรับกลยุทธ์ที่ดีแต่ฉันคิดว่าคุณอาจเต็มใจที่จะลดระดับลงเล็กน้อย ;) หากผู้คนสามารถส่งข้อความที่เรียบง่าย (เช่นของฉัน) คุณอาจมีโชคอีกเล็กน้อยที่จะได้รับผลงานที่น่าสนใจที่ค่อยๆดีขึ้นแทนที่จะต้องรวม combinatorics ทั้งหมดตั้งแต่เริ่มต้น
Martin Ender

นี่คือข้อตกลง: ฉันจะตรวจสอบเสร็จสิ้นในปัจจุบันและเผยแพร่ (เป็นแอปพลิเคชันบนเว็บ - มีขนาดใหญ่เกินไปที่จะวางที่นี่) น่าเสียดายที่มันอาจเข้มงวดเกินไปเนื่องจากถือว่าเป็นไปไม่ได้ที่จะตอบข้อผิดพลาด หลังจากนั้นฉันจะปรับมันเพื่อรองรับการตอบกลับหลายครั้งและเพียงแค่ปล่อยเตือนสำหรับการตอบกลับที่เป็นไปไม่ได้ ต้องบอกว่ากลยุทธ์ 1.3G MM (4,4) ของคุณจะต้องมีการตอบกลับที่เป็นไปไม่ได้และ / หรือการคาดเดาที่ไม่ลดจำนวนมากเนื่องจากขนาดโดยประมาณของกลยุทธ์ MM ที่เหมาะสม (5,8) นั้นเป็นเพียงไม่กี่ megs
MrBackend

@MBBackend แน่นอนว่ากลยุทธ์ของฉันมีคำตอบที่เป็นไปไม่ได้มากมาย นั่นคือสิ่งที่ฉันหมายถึงโดย "ฉันไม่ได้ทำ combinatorics" ;) ถ้ามันเป็นเรื่องยุ่งยากเกินกว่าที่คุณจะสนับสนุนและจัดกลุ่มคำตอบไม่ต้องกังวลฉันจะมองข้ามการเดาที่เป็นไปไม่ได้
Martin Ender

@ MrBackend ข่าวดีฉันแก้ไขมันแล้ว :) ฉันหวังว่ากลยุทธ์จะใช้ได้ในขณะนี้ แจ้งให้เราทราบหากยังคงมีปัญหาใด ๆ กับมัน
Martin Ender
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.