Чего же ты ждешь? (Решатель маджонга)


14

Идея, благодаря @ MartinBüttner из обсуждения в чате

Маджонг - это игра в плитку, которая очень популярна в Азии. Как правило, в нее играют четыре игрока, и цель игры - стать первым, кто выполнил правильную руку, используя плитки. Для этой задачи мы рассмотрим упрощенную версию игры - PPCG mahjong.

В 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: Я не думаю, что это их вина. Это вина разработчиков игр за то, что они назвали свои игры "Пасьянс Маджонг" "Маджонг" и больше ничего.
Джо З.

Как насчет семи пар? тринадцать сирот? Вы могли бы сделать что-то еще более сложное с почестями :) Как вы думаете, это нецелесообразно, если я создам Codegolf, который просит вычислить shanten (минимальное количество плиток, необходимое для получения tenpai - готовый к победе) руки?
В. Куртуа

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

Ответы:


4

Python, 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 Пропустил это. Благодарность!
Ell

@ Sp3000 Это Python 2. Это странно; у меня на 2.7.8 работает нормально
Ell

@Ell Работает в 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.