Чого ти чекаєш? (Маджонг-вирішувач)


14

Ідея завдяки @ MartinBüttner з обговорення в чаті

Маджонг - гра в плитки, яка надзвичайно популярна в Азії. Зазвичай він грається з чотирма гравцями, і мета гри - це першою особою, яка виконає дійсну руку за допомогою плитки. Для цього завдання ми розглянемо спрощену версію гри - PPCG маджонг.

У PPCG маджонг, є три костюми - m, pі s- і плитки пронумеровані від 1до 9. Кожній плитці є рівно чотири примірники, і плитки позначаються її номером, а за підписом (наприклад 3m, 9s).

Завершена рука маджонгу PPCG складається з чотирьох наборів з трьох і пари, загалом 14 плиток.

Набір з трьох може бути будь-яким:

  • Три однакові плитки (наприклад 4s 4s 4s, але не 4m 4p 4s), або
  • Послідовність трьох послідовних плиток в одному костюмі (наприклад, 1s 2s 3sчи 6p 7p 8pні, 3s 4m 5mабо 3p 5p 7p). Послідовності не обертаються (тому 9m 1m 2mнедійсні).

Пара - це просто дві однакові плитки (наприклад 5s 5s).

Змагання

Ваша програма отримає пробіл, розділений пробілом у 13 плиток, причому кожна плитка з’явиться не більше чотирьох разів. Ви можете написати або повну програму, або функцію, яка виконує рядок.

Ваше завдання - знайти всі можливі 14-ту плитку ("чекає"), яка при додаванні до неї склала б завершену руку маджонгу PPCG. Виведені плитки повинні бути розділені пробілом, але можуть бути в будь-якому порядку. Допускається провідна або відстала пробіли.

Ваша програма повинна працювати в розумний проміжок часу, не довше хвилини.

Приклади

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 

У першому прикладі 1m 4s 7p 3mвсі утворюють існуючі трійні, залишаючи самотника 9sдля формування пари.

У другому прикладі 2s 3sі 7p 8pможе утворювати лише послідовності, а решта плиток можуть утворювати лише трійки. Отже, жодна пара не може бути сформована, і виходу немає.

У третьому прикладі рука розпадається на 1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s. Зазвичай це було б очікування 3m 1s, але, як усі чотири 3mвикористовувались, єдине доступне очікування - це 1s.

У четвертому прикладі всі mплитки завершують руку. Наприклад, для 1m, можна було б, 1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9mяка є завершеною рукою.

Спробуйте опрацювати решту четвертого прикладу та п'ятого прикладу :)

Оцінка балів

Це , тому рішення в найменших байтах виграє. Застосовуються стандартні лазівки .


9
Дякую, що насправді ви займаєтесь маджонгом, а не (дратуючий ІМО) пасьянс, використовуючи плитку, про яку, здається, західники думають, коли чують слово "Маджонг".
Джастін

@Quincunx Цікавий факт: Цей виклик стався тому, що я хотів зробити виклик з представленням ASCII пасьянсу Маджонга (що я все-таки можу зробити в якийсь момент ...). Я все-таки називав це "пасьянсом Маджонг". ;)
Мартін Ендер

2
@Quincunx: Я не думаю, що це їхня вина. Це вина розробників ігор, що називали свої ігри "Маджонг-пасьянс" "Маджонг" та більше нічого.
Джо З.

А як щодо семи пар? тринадцять сиріт? ви могли б зробити щось ще складніше з відзнакою :) Чи вважаєте ви, що це недоцільно, якщо я створю кодовий гольф, який просить обчислити шентен (необхідна мінімальна кількість плиток, перш ніж отримати tenpai - готовий виграти) руки?
В. Куртуа

@VCourtois Минув час, але я пригадую, зокрема, виключаючи сім пар, тринадцять сиріт, відзнаку та вже здійснені дзвінки, щоб не ускладнювати виклик для нових людей у ​​грі. Я думаю, що пізніше після цього я подумав зробити виклик Shanten, але ніколи цього не зробив - якщо ви хочете опублікувати його, я думаю, що це складе чудовий виклик.
Sp3000

Відповіді:


4

Пітон, 312 281 байт

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 приймає рядок як вхідний і повертає рядок як вихідний.

Невелика кількість плиток (27) дозволяє досить швидко перевірити, чи кожна з них завершує руку. Проблема стає у тому, щоб перевірити, чи дійсна рука. Функція використовує простий алгоритм зворотного відстеження, який враховує всі можливі варіанти наборів і перевіряє, чи є який-небудь з них складений в повну руку.

Руки представлені як гістограми плитки, тобто список підрахунків плиток (для всіх плиток, а не лише тих, що є в руці.) Це дозволяє легко перевірити, чи є у нас певна кількість певної плитки, і якщо ми мати послідовність суміжних плиток (прокладка між плитками різних костюмів запобігає послідовності різнокольорових костюмів.)


Ах, ти побив мене: P Як би там не було, ти можеш використовувати mapв декількох місцях, як-от:H(map((S+t).count,T))
FryAmTheEggman

@FryAmTheEggman пропустив це. Спасибі!
Елл

@ Sp3000 Це Python 2. Це дивно; він працює добре для мене 2.7.8.
Елл

@Ell Works в 2.7.8 - 2.7.5 не сподобалось 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
)

Пояснив

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 
)

Тест в консолі 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)))

Вихідні дані

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.