Na co czekasz? (Mahjong solver)


14

Pomysł dzięki @ MartinBüttner z dyskusji na czacie

Mahjong to gra w płytki, która jest niezwykle popularna w Azji. Zwykle gra się w czterech graczy, a celem gry jest bycie pierwszą osobą, która ukończy ważne rozdanie za pomocą płytek. Do tego wyzwania rozważymy uproszczoną wersję gry - mahjong PPCG.

W PPCG mahjong, istnieją trzy garnitury - m, pi s- a płytki są ponumerowane od 1do 9. Są to dokładnie cztery kopie każdej płytki i płytki są oznaczone przez jego numer, a następnie jego kolorze (np 3m, 9s).

Ukończona ręka mahjonga PPCG składa się z czterech zestawów trzech i pary, co daje w sumie 14 płytek.

Zestaw trzech może być:

  • Trzy z tego samego kafelka (np. 4s 4s 4sAle nie 4m 4p 4s), lub
  • Ciąg trzech kolejnych płyt w tym samym kolorze (np 1s 2s 3salbo 6p 7p 8pale 3s 4m 5mi 3p 5p 7p). Sekwencje nie są zawijane (więc 9m 1m 2msą nieprawidłowe).

Para to po prostu dwie identyczne płytki (np 5s 5s.).

Wyzwanie

Twój program otrzyma rozdzielone spacją rozdanie z 13 płytek, z których każdy pojawi się nie więcej niż cztery razy. Możesz napisać pełny program lub funkcję, która pobiera ciąg znaków.

Twoim zadaniem jest znaleźć wszystkie 14 możliwe kafelki („czeka”), które po dodaniu do ręki utworzą ukończoną rękę mahjonga PPCG. Wyprowadzane płytki powinny być oddzielone spacją, ale mogą być w dowolnej kolejności. Wiodące lub końcowe białe znaki są dozwolone.

Twój program powinien działać w rozsądnym czasie, nie dłużej niż minutę.

Przykłady

Input: 1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s
Output: 9s

Input: 1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p
Output:

Input: 1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s
Output: 1s

Input: 1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m
Output: 1m 2m 3m 4m 5m 6m 7m 8m 9m

Input: 1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s
Output: 1m 4m 6s 9s 

W pierwszym przykładzie 1m 4s 7p 3mwszyscy tworzą istniejące trojaczki, pozostawiając samotnych, 9stworząc parę.

W drugim przykładzie, 2s 3si 7p 8pmogą tworzyć tylko sekwencje, a pozostałe płytki mogą tworzyć tylko trojaczki. Dlatego nie można utworzyć żadnej pary i nie ma wyjścia.

W trzecim przykładzie ręka dzieli się na 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Zwykle byłoby to oczekiwanie 3m 1s, ale ponieważ wszystkie cztery 3mzostały wykorzystane, jedynym dostępnym oczekiwaniem jest 1s.

W czwartym przykładzie wszystkie mpłytki uzupełniają układ. Na przykład, na przykład, 1mmożna mieć 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9mgotową rękę.

Spróbuj wypracować resztę czwartego przykładu i piątego przykładu :)

Punktacja

To jest , więc wygrywa rozwiązanie w najmniejszej liczbie bajtów. Obowiązują standardowe luki .


9
Dziękujemy za faktyczne wykonanie Mahjonga zamiast pasjansa (IMO) przy użyciu kafelków, o których ludzie z Zachodu myślą, ilekroć słyszą słowo „Mahjong”.
Justin

@Quincunx Zabawny fakt: Wyzwanie to powstało, ponieważ chciałem wykonać wyzwanie z reprezentacją ASCII pasjansa Mahjonga (co mogę zrobić w pewnym momencie ...). Jednak nazwałem to „Mahjong Solitaire”. ;)
Martin Ender

2
@Quincunx: Nie sądzę, że to ich wina. To wina twórców gier za nazwanie ich „Mahjong Solitaire” grami „Mahjong” i niczym więcej.
Joe Z.,

Co powiesz na siedem par? trzynaście sierot? możesz zrobić coś jeszcze bardziej złożonego z zaszczytami :) Czy uważasz, że jest to niecelowe, jeśli utworzę kodegolfa, który poprosi o obliczenie shanten (minimalna liczba płytek potrzebnych przed przygotowaniem tenpai - gotów do wygrania) ręki?
V. Courtois,

@VCourtois Minęło trochę czasu, ale pamiętam, że konkretnie wykluczyłem siedem par, trzynaście sierot, wyróżnienia i już dzwoniłem, aby nie komplikować wyzwania dla nowych graczy. Wydaje mi się, że zastanawiałem się nad późniejszym wyzwaniem shanten, ale ostatecznie tego nie zrobiłem - jeśli chcesz je opublikować, myślę, że byłoby to dobre wyzwanie.
Sp3000,

Odpowiedzi:


4

Pyton, 312 281 bajtów

def W(S):H=lambda C,n=0,t=1:sum([m<C[0]and H([c-s for c in C][:l]+C[l:],n+1,u)for m,s,l,u in(2,3,1,t),(t,2,1,4),(4-5*all(C[:3]),1,3,t)])|H(C[1:],n,t)if C[2:]and max(C)<5else n>4;T=[i+s for s in"mps"for i in"12345678900"];return" ".join(t for t in T if("1"<t)*H(map((S+t).count,T)))

W przyjmuje ciąg wejściowy i zwraca ciąg wyjściowy.

Mała liczba płytek (27) sprawia, że ​​jest wystarczająco szybki, aby sprawdzić, czy każdy z nich ukończy układ. Problem polega na sprawdzeniu, czy ręka jest ważna. Ta funkcja wykorzystuje prosty algorytm cofania, który bierze pod uwagę wszystkie możliwe wybory zestawów i sprawdza, czy któryś z nich stanowi kompletną rękę.

Ręce są reprezentowane jako histogramy kafelków, tzn. Lista ich liczby (dla wszystkich kafelków, nie tylko obecnych w ręce). Ułatwia to zarówno sprawdzenie, czy mamy określoną liczbę kafelków, jak i mieć sekwencję sąsiadujących płytek (wypełnienie między płytkami różnych kolorów zapobiega sekwencjom wielu kolorów).


Ach, pobiłeś mnie: P W każdym razie wygląda na to, że możesz użyć go mapw kilku miejscach, takich jak:H(map((S+t).count,T))
FryAmTheEggman,

@FryAmTheEggman Brakowało tego. Dzięki!
Ell

@ Sp3000 To jest Python 2. To dziwne; działa dla mnie dobrze na 2.7.8.
Ell

@Ell Działa w wersjach 2.7.8 - 2.7.5 nie podobało się 5else: P
Sp3000,

2

JavaScript (E6) 306

F=h=>(
  R=(a,p,n=1)=>(a=[...a]).splice(p,n)&&a,
  K=(t,d=3)=>
    !t[0]
    |t.some(
      (v,p)=>
        v==t[p+1]&v==t[p+d-1]&&
        K(R(t,p,d))
      ||
        ~((r=t.indexOf((x=-~v[0])+v[1]))|(s=t.indexOf(-~x+v[1])))&&
        K(R(R(R(t,s),r),p))
    ),
  o=[],
  [for(s of'mps')for(i of'123456789')h.replace(t=i+s,s,'g')[34]
  &&K([t,...h.split(' ')].sort(),2)&&o.push(t)
  ],o
)

Wyjaśniono

F=hand=>(
  Remove=(a,p,n=1)=>                // function to remove 1 or more element from an array, returning a new shorter array
    ((a=[...a]).splice(p,n), a),    // using array.splice on a new created array 

  Check=(ckHand, dim)=>  // recursive function to check hand. 
                         // removing pairs (at iteration 0) or sequence of three, if at last the hand remain empty then success
                         // parameter dim is 2 or 3 indicating how many equal elements are to be removed
    !ckHand[0]           // check if empty (element 0 does not exist)
    |ckHand.some(        // else traverse all array checking what can be removed
      (value, position)=> 
        value == ckHand[position + 1] 
        & value == ckHand[position + dim-1] &&   // look for 3 (or 2) equal elements
        Check(Remove(ckHand, position, dim), 3)   // if found, then remove elements and check again
      ||
        ~((r = ckHand.indexOf((x=-~value[0]) + value[1]))     // value[0] is number, value[1] is suit 
        |(s = ckHand.indexOf(-~x + value[1]))) &&              // look for an ascending sequence in following elements (the array is sorted)
        Check(Remove(Remove(Remove(ckHand, s), r), position),3) // if sequence found, remove elements and check again
    ),
  output=[], // start with an empty solution list
  [ // using array comprehension to implement a double loop
    for(s of'mps')        // loop for all suits
    for(i of'123456789')  // loop for all numbers
    (
       tile=i+s, // current tile 
       (hand.replace(tile,' ','g').length > 34)      // if tile is present 4 times in hand, the replaced length is 38-4 == 34
       && (                                       // else proceed with check
         ckHand = hand.split(' '), 
         ckHand.push(tile),    // in ckHand (as an array) the hand to be checked, that is base hand + current tile
         ckHand.sort(),        // sorting the array simplfy the checks
         Check(ckHand, 2)      // start checks looking for a pair
       )
       && 
         output.push(tile)   // if check ok, add tile to the solution list
    )   
  ],
  output // last expression in list is the function return value 
)

Przetestuj w konsoli FireFox / FireBug

;["1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s", "1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p",
 "1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s", "1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m",
 "1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s"].forEach(s=>console.log(s+' => '+F(s)))

Wynik

1m 1m 1m 4s 4s 4s 7p 7p 7p 3m 3m 3m 9s => 9s
1m 1m 1m 3m 3m 3m 5m 5m 5m 2s 3s 7p 8p =>
1m 2m 2m 3m 3m 3m 3m 4m 1s 1s 9s 9s 9s => 1s
1m 1m 1m 2m 3m 4m 5m 6m 7m 8m 9m 9m 9m => 1m,2m,3m,4m,5m,6m,7m,8m,9m
1m 1m 1m 5p 2m 3m 5p 7s 8s 5p 9s 9s 9s => 1m,4m,6s,9s
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.