Rozwiązywanie Mastermind w 6 lub mniej ruchach

24

Twoim celem jest napisanie programu, który rozwiąże dowolną zagadkę Mastermind w 6 lub mniej ruchach.

tło

Mastermind to gra planszowa. Celem gry jest dokładne odgadnięcie kombinacji (kolorów i kolejności) 4 kolorowych kołków ukrytych przez drugiego gracza. Kiedy zgadujesz, drugi gracz odpowiada między 0 a 4 białymi lub czerwonymi kołkami. Czerwony kołek to właściwy kolor i lokalizacja. Biały kołek to miejsce, w którym kolor jest reprezentowany w pozostałych kawałkach, ale znajduje się w niewłaściwym miejscu. Jeśli domniemane są zduplikowane kolory, w sekrecie zostanie przyznany tylko jeden kołek na odpowiedni kolor. (Tak więc - jeśli sekret zawierał 1 niebieski, a przypuszczenie zawierało 2 bluesa z jednym we właściwym miejscu, podano jeden czerwony kołek). Istnieje 6 różnych kolorów i można użyć duplikatów.

Na przykład gra może wyglądać następująco: (Zakładając, że rozwiązaniem jest czerwony zielony zielony niebieski niebieski)

1:  Blue Purple Black Green - 2 white pegs
2:  Green Red Black Blue    - 2 white pegs, 1 red peg
3:  Green Green Green Blue  - 3 red pegs
4:  Red Green Green Blue    - 4 red pegs

Zasady są rozszerzone na Wikipedii

Wymagania

  • Program musi czytać ze standardowego wejścia i zapisywać na standardowe wyjście
  • Będę używał liczb dla uproszczenia zamiast kolorów. Kombinacja zgadywania będzie 4 liczbami od 1 do 6
  • Muszą wypisywać swoje domysły jako serię 4 liczb oddzielonych spacjami od 1 do 6 kończących się nową linią. Na przykład:

    1 5 2 2 \ n

  • Program otrzyma następnie jako dane wejściowe po odgadnięciu 2 liczb całkowitych od 0 do 4 oddzielonych spacją i kończących znakiem nowej linii. Pierwszy to ilość białych kołków, drugi ilość czerwonych kołków.

  • Na wejściu „0 4” (4 czerwone kołki) program musi się zakończyć
  • Program musi być w stanie rozwiązać dowolną łamigłówkę w mniej niż 6 turach (twój program daje wynik, a następnie odpowiedź wejściowa to 1 kolej). Nie ma premii (z powodu złożoności dowodu) za możliwość rozwiązania go w mniejszym stopniu.
  • Rozwiązanie musi być całkowicie wewnętrzne i zawarte w źródle. Dozwolone są tylko biblioteki standardowe. Rozwiązanie nie może zatem polegać na żadnych innych plikach (takich jak słowniki) ani na Internecie.

Przykład wejścia / wyjścia

> is your programs output
< is the responding input
Solution is 1 5 6 6

> 1 2 3 4
< 0 1
> 4 1 6 6
< 1 2
> 1 6 5 6
< 2 2
> 1 5 6 6
< 0 4 

Punktacja

  • To jest czysty i prosty Code Golf . Najkrótsze rozwiązanie w bajtach wygrywa.

To jest moje pierwsze pytanie Code Golf. Przepraszam, że zrobiłem coś złego, ale starałem się jak najlepiej zapewnić, że nie ma absolutnie żadnych dwuznaczności i zapobiec jak największej liczbie przepisów prawniczych, jak to możliwe. Jeśli byłem niejednoznaczny lub niejasny, zadawaj pytania.

Lochok
źródło
1
W twoim przykładzie wejście / wyjście nie powinno 1 2 3 4powrócić 0 1?
Gaffi
1
A w tekście przykładowym, czy „Zielony Zielony Zielony Niebieski” nie powinien również podawać białego kołka (dla pierwszego Zielonego)? EDYCJA - Wikipedia wyjaśnia, że ​​nie należy podawać bieli, jak napisałeś. Myślę jednak, że w pytaniu należy wyraźnie określić zasady dotyczące bieli / czerni.
ugoren
Jak wolno będzie działać?
przestał obracać przeciwnie do
@Gaffi - Całkowicie słuszne - naprawione
lochok
1
Reguły dotyczące białych kołków nie są tutaj określone. Załóżmy, że wybrałeś 1234 i myślę, że 5611. Oba moje 1 mają właściwy kolor w niewłaściwym miejscu, więc po sposobie, w jaki określiłeś zasady, powiedziałbym, że dostaję 2 białe. Ale nie - Wikipedia mówi, że jest 1 biała. Niepoprawna metoda jest również łatwiejsza do zaprogramowania (ale Steven Rumbalski poprawnie wdrożył reguły Wikipedii).
ugoren

Odpowiedzi:

8

Python 2 Python 3, 359 365 338 znaków

from itertools import*
from collections import*
m=h=set(product(*['123456']*4))
def f(g,k):b=sum(x==y for x,y in zip(g,k));return'%s %s'%(sum(min(g.count(c),k.count(c))for c in set(g))-b,b)
while len(m)>1:g=min(h,key=lambda g:max(Counter(f(g,k)for k in m).values()));print(*g);s=input();m=set(x for x in m if s==f(g,x))
print(*(m.pop()))

Zabawne, zajęło mi wiele edycji, aby zdać sobie sprawę, że mam pięcioznakową nazwę zmiennej.

Nie lubię długiego importu. Wydaje mi się, że powinienem być w stanie zaimplementować zamiennik collections.Counter, który oszczędziłby import.

Nie podoba mi się też print(*(m.pop()))koniec. Wydaje się, że powinien zniknąć w pętli while, ale nie mogę znaleźć sposobu na zrobienie tego bez przedłużenia go.

Steven Rumbalski
źródło
TypeError: join() takes exactly one argument (2 given)na return j(sum(min(g.count(c),k.count(c))for c in set(g))-b,b). również sum () zwraca wartość int, podczas gdy j (str.join) powinien przyjąć iterowalną wersję
Blazer
Moja struktura pętli pozbywa się finału printi myślę, że jest nieco krótszy. Również lepiej odpowiada żądanemu zachowaniu (zatrzymując się na „4 0”, a nie kiedy znasz odpowiedź). I len(m)>1== m[1:]. Import jest rzeczywiście denerwujący - from a,b import *byłby miły.
ugoren
ten program wydaje się kończyć, gdy tylko uzna to za właściwe. raz go uruchomiłem i zatrzymało się na 5 domysłów, to nie było poprawne. następnym razem zatrzymał się na 4 zgadywaniach i było poprawne, ale nawet nie podałem 4 0jeszcze danych wejściowych , co jest w obiektywnych kryteriach, a innym razem wyjdzie z wyjątkiem:print(*(m.pop())) KeyError: 'pop from an empty set'
Blazer
@Blezer. Jakie są przypadki testowe, które spowodowały ten wynik?
Steven Rumbalski
@ Blazer: 4 0to cztery białe kołki. Myślę, że masz odwróconą punktację.
Steven Rumbalski
7

Haskell, 317 304

q[0,4]_=error""
q[n,m]l=(\s->all(==4-m)[g s,n+g(u(foldr((u((.drop 1).(++)).).break.(==)))$unzip s)]).c(u(/=)).zip l
u=uncurry;m=map;g=length;c=filter
f a=[head.c(($(zipWith q(take n a)$f a)).all.flip($)).sequence$replicate 4[1..6]|n<-[0..]]
main=interact$unlines.m(unwords.m show).f.m(m read.words).lines

Uwielbiam pisać czysto funkcjonalne interaktywne programy! Ale oczywiście ten styl ma pewne ograniczenia: kończy się teraz z błędem, ale nie określono, że to nie jest w porządku. Musiałbym przefakturować całość w IOmonadę, aby uzyskać wyjście bez błędów.

przestał się obracać w lewo
źródło
Czy gwarantuje prawidłowe odgadnięcie w 6 ruchach?
ugoren
Uh Myślałem, że tak (działa na przykładzie OP i innych rozwiązań), ale wyczerpujące testy pokazują, że czasem potrzeba 7 domysłów. Będę musiał jeszcze nad tym popracować!
przestał obracać przeciwnie do
5

Python, 385 357 znaków, Rozwiązuje w 5 ruchach

Im bardziej to zmieniam, tym bardziej staje się podobny do Stevena Rumbalskiego ... Główną różnicą jest to, że pracuje z ciągami, a nie liczbami całkowitymi.
Zaimplementowano algorytm Knutha (mam nadzieję, teraz poprawnie).
Pożyczył funkcję punktacji od Stevena Rumbalskiego.
Wygenerowanie pierwszego zgadnięcia zajmuje dużo czasu, później poprawia się.
Kodowanie go ( g=len(A)==1296 and [1,1,2,2] or ...) ułatwia życie, jeśli chcesz go przetestować.
Nie liczę 4 znaków nowej linii + par tabulatorów, które można zastąpić średnikami.

from collections import*
from itertools import*
a=B=A=list(product(*[range(1,7)]*4))
r=lambda g,k:(sum(x==y for x,y in zip(g,k)),sum(min(g.count(c),k.count(c))for c in set(g)))
while a!=4:
    g=A[1:]and min(B,key=lambda x:max(Counter(r(x,i)for i in A).values()))or A[0]
    print"%d "*4%tuple(g)
    b,a=map(int,raw_input().split())
    A=[x for x in A if r(g,x)==(a,a+b)]
ugoren
źródło
"%d "*4%tuple(g)
gnibbler
from collections import*
gnibbler
a,b=map(int,raw_input())
gnibbler
product(*[range(1,7)]*4)
gnibbler
Counter(r(x,i)for i in A).values()
gnibbler