마스터 마인드 전략


19

Mastermind에 대한 코드 골프 과제 만 찾을 수 있었으므로 여기에 내가 가지고 싶었던 코드 도전 버전이 있습니다.

일반적인 마스터 마인드 게임 MM (4,6)에 대한 최적의 전략은 1993 년 Koyama와 Lai에서 발견되었으며 평균 추측 횟수는 5625/1296 ~ 4.34입니다. MM (5,8)은 여전히 ​​해결되지 않았지만 평균 추측 횟수는 ~ 5.5 인 것으로 추정됩니다.

당신의 임무는 pow(8,5) = 32768가능한 모든 뚜렷한 솔루션을 다루는 5 개의 구멍과 8 개의 색을위한 MM (5,8) 전략을 만드는 것입니다. 분명히, 그것은 최적의 것이 될 필요는 없습니다. 두 가지 선택이 있습니다.

  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이 가장 낮은 전략이 이깁니다. 첫 타이-브레이크 : 가장 낮은 최대 추측 수입니다. 두 번째 동점 : 첫 번째로 게시 된 답변입니다. 대회는 2014 년 8 월 1 일 0:00 GMT로 종료됩니다 .


점수 = 21 인 MM (2,3)에 대한 전략의 예 :

{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

귀하의 편의를 위해 Java 기반 MM (5,8) 전략 검증 도구를 곧 게시하겠습니다.


MM (2,3)의 예제 전략이 어떻게 적용되는지 이해하는 데 어려움을 겪고 있습니다. 전략을 설명하는 샘플 게임을 게시 할 수 있습니까?

@tolos 9 개 모두 추가했습니다.
MrBackend

검증자가 점수를 출력 할 수 있다면 좋을 것입니다!
찰스

@ 찰스 윌!
MrBackend 2016 년

2
여러 주요 페그 조합에 동일한 응답을 허용하도록 문법을 기꺼이 바꾸시겠습니까? 처럼 {AB:{10|01:BB}}? 내가 대답을 할 수 있지만, 그것은 (전혀 아니라 4 개 홀, 3 색을 확장하지 않는 문법의 트리 구조 꽤 순진 인해입니다, 내가 줄일 수있는 147메가바이트 전략, 생성 크게 의 부품을 결합하여을 나무).
Martin Ender

답변:


6

자바

MM (5,8)에 대한 내 알고리즘의 점수는 177902 178006 182798 182697 이며 최대 깊이는 8 9 이며 몇 초만 필요합니다 (느린 컴퓨터에서).

이 알고리즘에서 찾은 score = 21 인 MM (2,3)에 대한 전략의 출력 예는 다음과 같습니다.

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

내 알고리즘에는 흥미로운 것이 없습니다. 발명이 없습니다. 방금 인터넷에서 찾은 레시피를 따라이 Java 코드로 압축했습니다. 내가 한 유일한 최적화는 어떤 방식으로 코드 줄을 최적화하려고합니다. 다음과 같이 진행됩니다.

  1. 가능한 모든 코드의 초기 세트 S0을 현재 세트 S로 만듭니다.
  2. Codebreaker는 S에 대한 (욕심 많은) 좋은 추측을 찾습니다. 각 추측은 S의 파티션 P로 이어지고, 각 부분 집합 S '는 추측에 대해 동일한 응답을 갖는 모든 코드 (S에서)를 수집합니다. 좋은 추측은 추측에 대한 대부분의 정보를 제공하는 것과 같이 좋은 분할을 갖습니다.
  3. 좋은 추측과 그 P를 취하십시오. P의 비어 있지 않은 S '마다 코드 브레이커 재귀를 적용하십시오 (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. S 대신 S0에서 좋은 추측을하는 것이 합리적입니다. 그러나 현재 코드 에서이 접근법을 따르지 않습니다.
  3. 내 탐욕스러운 검색은 인위적으로 200 회 시도됩니다.
  4. 나는 "추측에 대한 대부분의 정보를 제공하는"것은 매우 정확하지 않다는 것을 알고 있습니다. 간단한 아이디어는 서브 세트가 가장 많은 파티션을 선택하는 것입니다.
  5. 결과는 회신을 계산하는 방법 (..)에 따라 크게 달라집니다. 마침내 나는 도널드 크 누스의 표현에 적응했다.

에 대한 전략은 MM(5,8) 여기에서 찾을 수 있습니다 . GitHub에는 긴 줄을 표시하는 데 문제가 있으므로 Raw 버튼을 클릭하십시오 .


안녕하세요 github 텍스트 를 예쁘게 인쇄하여 결과를 조회 가이드로 바꿀 수 있습니다. 첫 번째 시작 지점 'HHCAA'에서 .. 및 답장에 따라 다음 단계 및 다음 단계 등. 현재 원시 텍스트 형식은 수동으로 스캔하기가 쉽지 않습니다. 내가 따르는 기술은 중첩 된 괄호를 구문 분석하고 질문의 글 머리 기호 목록과 마찬가지로 끝까지 쉽게 따라갈 수있는 멋진 테이블 레이아웃을 얻는 방법입니다. MM (2,3)의 경우 감사합니다. 내가 무엇을하고 있는지 이해할 수 있기를 바랍니다. (str을 파싱하기 위해 파이썬을 선호하십시오)
py를

2

루비

편집 : 불가능한 추측을 제외하는 몇 가지 논리가 추가되었습니다. 따라서 전략은 이제 주어진 형식을 따르며 훨씬 더 관리하기 쉽습니다.

그래서 여기에 한 가지 시도가 있습니다. 꽤 순진합니다 (그리고 읽기 쉽지 않습니다-if / elsif / else 분기를 아래에서 위로 읽는 데 도움이 됨).

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 버튼을 클릭하십시오 .

이제 검증자를 받으면 실제 점수가 무엇인지 알려줄 수도 있습니다. :)


검증 기가 아직 공개되지 않은 것에 대해 나쁘게 느끼고 있지만 그 길에 있습니다 ... 솔루션이 AB 인 경우와 같이 (첫 번째) MM (2,3) 전략에 문제가 있습니다. Guess = AA; 회신 = 10; 추측 = BB; reply = 10으로 전략이 없습니다. 답글 그룹화에 대한 제안을 살펴볼 것이지만, 가능한 해결책 세트가 다른 답글에 대해 분리되어 있기 때문에 좋은 전략에는 필요하지 않습니다.
MrBackend

@MrBackend 잘 잡았습니다. 거기서 사건을 건너 뛰었습니다. 지금 수정해야합니다. 문법에 관해서는 물론 좋은 전략 에는 필요하지 않지만 막대를 조금 낮추고 싶을 것이라고 생각했습니다. ;) 사람들이 내 것과 같은 더 간단한 항목을 제출할 수 있다면 처음부터 모든 조합을 포함하지 않고 점차 개선되는 흥미로운 제출을 얻는 것이 조금 더 운이 좋을 것입니다.
마틴 엔더

거래는 다음과 같습니다. 현재 검증기를 완료하고 게시합니다 (웹 앱으로 붙여 넣기에 너무 큽니다). 불행히도 불가능한 답글은 오류로 간주되므로 너무 엄격 할 수 있습니다. 그런 다음 여러 답장을 지원하고 불가능한 답장에 대한 경고 만 표시하도록 조정하겠습니다. 말했듯이, 1.3G MM (4,4) 전략은 괜찮은 MM (5,8) 전략의 예상 크기가 소수의 메그이므로 불가능한 많은 답장 및 / 또는 비 감소 추측을 포함해야합니다.
MrBackend

@MrBackend 물론 내 전략에는 수많은 답변이 들어 있습니다. 이것이 "콤비 네이터 믹스를하지 않습니다"라는 의미입니다. ;) 당신이 그것을 지원하고 답글을 그룹화하는 것이 너무 번거 롭다면 걱정하지 마십시오. 나는 불가능한 추측을 생략 할 것입니다.
마틴 엔더

@MrBackend 좋은 소식, 나는 고쳤다. :) 지금 전략이 유효하기를 바랍니다. 여전히 문제가 있으면 알려주세요.
Martin Ender
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.