你在等什么?(麻将求解器)


14

感谢@MartinBüttner在聊天中的讨论

麻将是一款在亚洲非常受欢迎的瓷砖游戏。它通常由四个玩家玩,并且游戏的目标是成为第一个使用磁砖完成有效手牌的人。对于这个挑战,我们将考虑游戏的简化版本-PPCG麻将。

在PPCG麻将,有三套衣服- mps-和瓦片从编号19。正好有每个区块的四个副本,和瓷砖是由它的编号,然后它的套装(如表示3m9s)。

一张完整的PPCG麻将手包括四组,每组三个,一对,共14块瓷砖。

三个一组可以是:

  • 三个相同的图块(例如4s 4s 4s,但不是4m 4p 4s),或者
  • 同一套西装中三个连续的拼贴的序列(例如1s 2s 3s6p 7p 8p但不是3s 4m 5m3p 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 3s7p 8p仅可形成序列,其余图块仅可形成三胞胎。因此,不能形成一对,也没有输出。

在第三个示例中,手分成1m2m3m 2m3m4m 3m3m 1s1s 9s9s9s。通常这将是一个等待3m 1s,但是由于使用了全部四个3m,因此唯一可用的等待是1s

在第四个示例中,所有m图块都完成了手形。例如,对于1m,可能有1m1m1m 1m2m3m 4m5m6m 7m8m9m 9m9m一只完整的手。

尝试计算出第四示例和第五示例的其余部分:)

计分

这是,因此以最少字节为单位的解决方案将获胜。有标准漏洞


9
感谢您实际上在做麻将,而不是(IMO烦人的)纸牌使用西方人在听到“麻将”一词时似乎会想到的瓷砖。
2014年

@Quincunx有趣的事实:之所以出现这个挑战,是因为我想用麻将纸牌的ASCII表示来做一个挑战(在某些时候我可能仍然会做...)。我确实称它为“麻将纸牌”。;)
Martin Ender 2014年

2
@Quincunx:我不认为这是他们的错。游戏开发者将他们的“麻将单人纸牌”游戏称为“麻将”是错误的。
Joe Z. 2014年

那七双呢?十三个孤儿?您可以用荣誉来做更复杂的事情:)如果我创建一个代码高尔夫球,要求计算手的shan杆数(拿到天牌之前需要的最小瓷砖数-准备取胜),您认为这是没有用的吗?
V. Courtois

@VCourtois已经有一段时间了,但是我记得特别排除了七对,十三名孤儿,荣誉和已经打过电话,以免给新手带来太多挑战。我认为我考虑过之后再进行一次棚户挑战,但最终没有这样做-如果您想发布一个,我认为这是一个很好的挑战。
Sp3000

Answers:


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 2014年

@FryAmTheEggman错过了这一点。谢谢!
2014年

@ Sp3000这是Python 2。在2.7.8上对我来说效果很好。
2014年

@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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.