Стратегія натхнення


19

Я міг знайти лише проблеми з кодовим гольфом для Mastermind, тож ось версія кодового виклику, яку я хотів би взяти на себе.

Оптимальну стратегію для звичайної гри Mastermind (ММ (4,6)) знайшли Кояма та Лай у 1993 році, маючи середню кількість здогадів = 5625/1296 ~ 4,34. MM (5,8) досі не розв'язаний, але, за оцінками, він має середню кількість здогадок ~ 5,5.

Ваше завдання - створити стратегію ММ (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. Перший тай-брейк: Найменша максимальна кількість здогадок. Другий тай-брейк: Перша опублікована відповідь. Конкурс закінчується 1 серпня 2014 р. 0:00 GMT .


Приклад стратегії для ММ (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

Незабаром я опублікую перевірку стратегії на базі Java (5,8) для вашої зручності.


Мені важко зрозуміти, як застосовується прикладна стратегія ММ (2,3). Чи можете ви розмістити зразок гри, що пояснює стратегію?

@tolos Я додав всі 9 :)
MrBackend

Було б чудово, якби і ваш перевіряючий міг вивести бал!
Не те, що Чарльз

@Charles Зробимо!
MrBackend

2
Чи готові ви змінити свою граматику, щоб дозволити однакову відповідь на кілька комбінацій ключових ключів? Як {AB:{10|01:BB}}? У мене є відповідь, але вона досить наївна, і завдяки деревній структурі граматики вона зовсім не масштабується (4 отвори, 3 кольори) генерує стратегію 147 Мб, яку я міг би значно скоротити , поєднуючи частини дерево).
Мартін Ендер

Відповіді:


6

Java

Мій алгоритм для ММ (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 of 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. Результат сильно залежить від того, як ви обчислите відповідь (..). Нарешті я адаптував вираз Дональда Кнута.

Стратегію MM(5,8) можна знайти тут . У GitHub є деякі проблеми з відображенням довгих ліній, тому натисніть кнопку Raw .


привіт, як красиво надрукувати текст github, щоб результати можна було перетворити на посібник з пошуку .. від першої початкової точки «HHCAA» .. та наступного кроку залежно від відповіді ... і до наступного тощо. Поточний неформатний текстовий формат не легший для ручного сканування .. техніка, якою я займаюся, це як проаналізувати вкладені дужки та отримати гарний макет таблиці, який простіше прослідкувати до кінця, подібно до пункту з написом у запитанні для ММ (2,3). Дякую. Сподіваюся, ви зможете зрозуміти, що я хочу. (віддайте перевагу python розбору str)
ihightower

2

Рубін

EDIT: Додано трохи логіки, щоб виключити неможливі здогадки. Отже, стратегії зараз відповідають заданому формату і є набагато більш керованими.

Тож ось одна спроба втілити це. Це досить наївно (і не дуже розбірливо - допомагає прочитати гілку 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)займає 376 КБ і її можна знайти тут . У GitHub є деякі проблеми з відображенням довгих ліній, тому натисніть кнопку Raw .

Тепер, якщо я отримаю верифікатор, я також можу вам сказати, який мій фактичний результат. :)


Відчуття поганості в тому, що верифікатор ще не опублікований, але він на своєму шляху ... У вашій (першій) стратегії MM (2,3) щось не так, наприклад, якщо рішення AB: Guess = AA; відповідь = 10; здогадка = ВВ; відповідь = 10, для якої немає стратегії. Я розгляну ваші пропозиції щодо групування відповідей, але це не повинно бути необхідним для хороших стратегій, оскільки набір можливих рішень не відповідає різним відповідям.
MrBackend

@MrBackend Добрий улов, я пропустив справу там. Це слід виправити зараз. Що стосується граматики, то, звичайно, це не потрібно для хороших стратегій, але я подумав, що ви, можливо, хочете трохи знизити планку. ;) Якщо люди можуть надсилати більш прості записи (наприклад, моя), у вас може бути трохи більше шансів отримати цікаві матеріали, які поступово вдосконалюються, замість того, щоб з самого початку включати всю комбінаторику.
Мартін Ендер

Ось угода: я закінчую поточний верифікатор і публікую його (як веб-додаток - він занадто великий, щоб тут вставити). На жаль, це може бути занадто суворо, оскільки він вважає неможливим відповідь на помилку. Після цього я адаптую його для підтримки декількох відповідей і просто надсилаю попередження про неможливі відповіді. Сказавши це, ваша стратегія 1,3 ГМ (4,4) повинна містити безліч неможливих відповідей та / або не зменшуючих здогадок, оскільки орієнтовний розмір гідної стратегії ММ (5,8) - це кілька мег.
MrBackend

@MrBackend Звичайно, мої стратегії містять безліч неможливих відповідей. Ось що я мав на увазі під «комбінаторикою я не займаюся». ;) Якщо у вас занадто багато клопоту, щоб підтримати це та групувати відповіді, не хвилюйтеся, тоді я розберуся на пропущення неможливих здогадок.
Мартін Ендер

@MrBackend Добрі новини, я виправив це. :) Я сподіваюся, що стратегія діє зараз. Повідомте мене, чи є ще проблеми з цим.
Мартін Ендер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.