Chiến lược chủ mưu


19

Tôi chỉ có thể tìm thấy các thử thách chơi gôn mã cho Mastermind, vì vậy đây là phiên bản thử thách mã mà tôi muốn tự mình thực hiện.

Một chiến lược tối ưu cho trò chơi Mastermind bình thường, MM (4,6), đã được Koyama và Lai tìm thấy vào năm 1993, có số lần đoán trung bình = 5625/1296 ~ 4,34. MM (5,8) vẫn chưa được giải quyết, nhưng ước tính có số lần đoán trung bình ~ 5,5.

Nhiệm vụ của bạn là tạo ra một chiến lược MM (5,8), dành cho 5 lỗ và 8 màu, bao gồm tất cả các pow(8,5) = 32768giải pháp khác biệt có thể. Rõ ràng, nó không phải là một tối ưu. Bạn có hai lựa chọn:

  1. Đăng một chương trình xác định tạo ra chiến lược. Chương trình phải có thể biên dịch / chạy được trên Windows 7, Mac OS X hoặc Linux mà không cần thêm bất kỳ phần mềm miễn phí nào.
  2. Xuất bản chiến lược của bạn (cùng với tên StackExchange của bạn) ở đâu đó trên Internet và đăng URL tại đây.

Trong cả hai trường hợp, hãy nêu điểm số (xem bên dưới) trong tiêu đề của câu trả lời.

Chiến lược phải được mã hóa theo ngữ pháp sau:

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'

Thuật toán được sử dụng để quyết định số lượng chốt khóa đen / trắng được mô tả trong http://en.wikipedia.org/wiki/Mastermind_(board_game)

Lưu ý rằng câu trả lời "50" (nghĩa là đoán đúng) được ngụ ý và không phải là một phần của ngữ pháp.

Chấm điểm: N = tổng số lần đoán cho mỗi trong số 32768 đường dẫn / giải pháp. Chiến lược với N thấp nhất sẽ thắng. Tie-break đầu tiên: Số lần đoán tối đa thấp nhất. Tie-break thứ hai: Câu trả lời đầu tiên được đăng. Cuộc thi kết thúc vào ngày 1 tháng 8 năm 2014 0:00 GMT .


Một ví dụ về chiến lược cho MM (2,3) với số điểm = 21:

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

Sử dụng chiến lược này, 9 trò chơi có thể sẽ diễn ra như sau:

  • 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

Tôi sẽ sớm đăng một trình xác minh chiến lược MM (5,8) dựa trên Java để thuận tiện cho bạn.


Tôi đang gặp khó khăn trong việc hiểu cách áp dụng chiến lược ví dụ về MM (2,3). Bạn có thể đăng một trò chơi mẫu giải thích chiến lược?

@tolos Tôi đã thêm tất cả 9 :)
MrBackend

Sẽ thật tuyệt nếu trình xác minh của bạn cũng có thể tạo ra một số điểm!
Không phải Charles

@Charles Sẽ làm được!
MrBackend

2
Bạn có sẵn sàng thay đổi ngữ pháp của mình để cho phép cùng một câu trả lời cho nhiều tổ hợp chốt khóa không? Thích {AB:{10|01:BB}}? Tôi có một câu trả lời, nhưng nó khá ngây thơ và do cấu trúc cây của ngữ pháp nên nó không có quy mô tốt (4 lỗ, 3 màu, tạo ra một chiến lược 147 MB, mà tôi có thể cắt giảm đáng kể bằng cách kết hợp các phần của cái cây).
Martin Ender

Câu trả lời:


6

Java

Thuật toán của tôi cho điểm MM (5,8) với 177902 178006 182798 182697 với độ sâu tối đa 8 9 và chỉ cần vài giây (trên máy tính chậm của tôi).

Một ví dụ đầu ra của chiến lược cho MM (2,3) với số điểm = 21 được tìm thấy bởi thuật toán này trông như thế này:

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

Không có gì thú vị với thuật toán của tôi. Không có phát minh. Tôi chỉ làm theo các công thức được tìm thấy trong mạng và nén chúng vào mã Java này. Tối ưu hóa duy nhất tôi đã làm là cố gắng tối ưu hóa các dòng mã (theo một cách nào đó). Nó như thế này:

  1. Tạo tập S0 ban đầu của tất cả các mã có thể là tập hiện tại S.
  2. Codebreaker tìm ra một dự đoán tốt (tham lam) cho S. Mỗi lần đoán dẫn đến một phân vùng P của S, trong đó mỗi tập con S 'thu thập tất cả các mã (từ S) có cùng một câu trả lời cho dự đoán. Một dự đoán tốt có một phân vùng tốt, như một dự đoán cung cấp hầu hết thông tin cho dự đoán.
  3. Hãy đoán đúng và P. của nó Đối với mỗi S 'không trống trong P áp dụng đệ quy mã hóa (bước 2).

@MrBackend: Viết một trình xác minh là khó, tôi đoán vậy. ;-)

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

Một số nhận xét:

  1. Không cần kiểm tra tính nhất quán vì các bộ S và các phân vùng của chúng được xây dựng theo cách nhất quán (tự động).
  2. Chọn một dự đoán tốt từ S0 (thay vì S) có ý nghĩa. Nhưng tôi không theo phương pháp này trong mã hiện tại.
  3. Tìm kiếm tham lam của tôi được cắt tỉa một cách giả tạo đến 200 lần thử.
  4. Tôi biết, "đưa ra hầu hết thông tin cho dự đoán" không chính xác lắm. Ý tưởng đơn giản là chọn phân vùng có số lượng tập con nhiều nhất.
  5. Kết quả phụ thuộc rất nhiều vào cách bạn tính toán trả lời (..). Cuối cùng tôi đã điều chỉnh biểu hiện của Donald Knuth.

Chiến lược cho MM(5,8) có thể được tìm thấy ở đây . GitHub có một số vấn đề hiển thị các dòng dài, vì vậy hãy nhấp vào nút Nguyên .


hi làm thế nào để in đẹp văn bản github để có thể biến kết quả thành hướng dẫn tra cứu .. từ điểm bắt đầu đầu tiên 'HHCAA' .. và bước tiếp theo tùy thuộc vào câu trả lời ... và tiếp theo, v.v. Định dạng văn bản thô hiện tại không dễ dàng hơn để quét thủ công .. kỹ thuật tôi đang làm là làm thế nào để phân tích các dấu ngoặc được lồng và có được một bố cục bảng đẹp dễ theo dõi đến cuối, tương tự như danh sách dấu đầu dòng trong câu hỏi cho MM (2,3). Cảm ơn bạn. Hy vọng bạn có thể hiểu những gì tôi sau. (thích python để phân tích str)
ihightower 30/12/17

2

Hồng ngọc

EDIT: Đã thêm một số logic để loại trừ những dự đoán không thể. Do đó, các chiến lược hiện tuân thủ định dạng đã cho và dễ quản lý hơn nhiều.

Vì vậy, đây là một nỗ lực để có được điều này. Nó khá ngây thơ (và không dễ đọc lắm - nó giúp đọc nhánh if / elsif / other từ dưới lên trên).

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

Trước tiên, tôi thử 5 của mỗi màu: AAAAA, BBBBB, vv Từ đó tôi tìm ra những màu sắc được thực sự sử dụng trong mô hình. Và sau đó tôi chỉ đơn giản là thử tất cả các hoán vị của các màu đã cho, bỏ qua các màu đã bị loại trừ bởi các chốt đen.

Đây là MM(2,3)chiến lược:

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

Chiến lược MM(5,8)mất 376KB và có thể được tìm thấy ở đây . GitHub có một số vấn đề hiển thị các dòng dài, vì vậy hãy nhấp vào nút Nguyên .

Bây giờ nếu tôi nhận được một xác minh, tôi cũng có thể cho bạn biết điểm thực tế của tôi là gì. :)


Cảm thấy tồi tệ về trình xác minh chưa được công bố, nhưng nó đang trên đường ... Có một cái gì đó sai với chiến lược (đầu tiên) MM (2,3) của bạn, ví dụ nếu giải pháp là AB: Guess = AA; trả lời = 10; đoán = BB; Trả lời = 10, mà không có chiến lược. Tôi sẽ xem xét đề xuất của bạn về việc trả lời nhóm, nhưng không cần thiết cho các chiến lược tốt, vì tập hợp các giải pháp có thể không phù hợp với các câu trả lời khác nhau.
MrBackend

@MrBackend Bắt tốt, tôi bỏ qua một trường hợp ở đó. Nó nên được chữa ngay bây giờ. Đối với ngữ pháp, tất nhiên nó không cần thiết cho tốt các chiến lược , nhưng tôi nghĩ bạn có thể sẵn sàng hạ thấp thanh một chút. ;) Nếu mọi người có thể gửi các mục đơn giản hơn (như của tôi), bạn có thể có thêm một chút may mắn khi nhận được các bài nộp thú vị dần dần được cải thiện, thay vì phải bao gồm tất cả các tổ hợp ngay từ đầu.
Martin Ender

Đây là thỏa thuận: Tôi sẽ hoàn thành trình xác minh hiện tại và xuất bản nó (dưới dạng một ứng dụng web - quá lớn để dán ở đây). Thật không may, nó có thể quá nghiêm ngặt, vì nó coi không thể trả lời lỗi. Sau đó, tôi sẽ điều chỉnh nó để hỗ trợ nhiều câu trả lời và chỉ phát ra cảnh báo cho những câu trả lời không thể. Phải nói rằng, chiến lược 1.3G MM (4,4) của bạn phải chứa rất nhiều câu trả lời không thể và / hoặc dự đoán không giảm, vì kích thước ước tính của chiến lược MM (5,8) khá là một số megs.
MrBackend

@MrBackend Tất nhiên các chiến lược của tôi chứa rất nhiều câu trả lời không thể. Đó là những gì tôi có nghĩa là "Tôi không làm tổ hợp". ;) Nếu bạn gặp quá nhiều rắc rối để hỗ trợ điều đó và nhóm các câu trả lời, đừng lo lắng, sau đó tôi sẽ xem xét bỏ qua những dự đoán không thể.
Martin Ender

@MrBackend Tin vui, tôi đã sửa nó. :) Tôi hy vọng chiến lược này là hợp lệ ngay bây giờ. Hãy cho tôi biết nếu vẫn còn bất kỳ vấn đề với nó.
Martin Ender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.