# Mahjong Checker

Mahjong is a tabletop game played using tiles. It features three "number" suits (pins, sous, mans, represented as p, s and m) from 1 to 9, and one "honor" suit z of seven distinct tiles. Note that contrary to western card games, tiles are not unique. To complete a hand and win, the 13 tiles in your hand are combined with 1 newly drawn tile and must result in one of the following winning configurations:

• 4 sets and 1 pair, self-explanatory
• seven pairs, where all pairs are distinct (twice the same pair wouldn't qualify)
• kokushi musou: one of each of the 1 and 9 of each number suit, one of every seven honors, the remaining tile forming a pair (e.g. 19m19p19s11234567z)

A pair is any of the two same tiles: 11m, 33s, 44p, 55z, etc.

A set consists of 3 tiles of the same suit. It can either be a run: 3 number tiles (p, s or m) in a connected run like 123s or 234s, but not 1m 2p 3s or 234z; or a triplet of any suit, not necessarily numbers, like 111z, 222m.

So honor tiles (non-numbers, represented by z) can only form pairs or triplets, but not runs. 567z is not a set, 555z is a valid set, 55z is a valid pair.

A single tile can only be counted as part of one set or pair: there is no sharing or reusing.

Given a sorted hand of 13 tiles and one tile, check whether the 14 tiles make up a completed hand.

## Input & Output

• You are given a sequence of numbers and letters, a space, then a tile of a number and a letter
• Output True/1 if the set is a match, else False/0

## Others:

• You are allowed to input the sequence and tile+letter as a list/array

## Test Cases:

Truthy

222888m444p2277z 7z
234m45789p45688s 6p
11m4477p8899s116z 6z
19m19p19s1234567z 6z
123345567789m3p 3p


Falsey

1122335778899m 1m
888m55s11222333z 4z
234m2233445566p 4p
19m139p19s123567z 4z
11m4477p8899s666z 6z


Credits to Unihedron for the puzzle!

## Scoring

This is code-golf, so shortest code wins!

• Can we take input as 1m 2m 3m etc? Feb 9 at 4:32
• yes you can although im not sure it that changes the challenge too much Feb 9 at 4:36
• Borderline dupe of mahjong solver, since the main task is to check if a 14-tile set is a winning hand (the linked challenge just requires to do the same thing for every possible 14th tile). FYI, there has been four different mahjong-related challenges already. Feb 9 at 6:30
• Since points (fan / han) related rules are not considered in this question. Is there any reason to place the last tile independently from the others?
– tsh
Feb 9 at 7:08
• @Bubbler The linked question have a slightly different rules: This one includes 7 pairs and 13 orphans while that one don't. The shantin question seems using a similar rule, while it could be more complex than the one you linked.
– tsh
Feb 9 at 8:38

# JavaScript (Node.js), 248246 245 bytes

s=>s.sort().every((t,i)=>t==s[i^1]&t!=s[n=12,i+2])|A(s)|[...new Set(s)].length>n
A=y=>y.some(t=>R(y,t,t,t)|t[n+=t>'2'&t<'9'&t<A,1]<A&R(y,t,t-1+t,t-2+t))|y==y&!y
R=(s,p,q,r,z=0,k=s.filter(y=>y!=p||z++))=>p?z&&R(k,q,r):A(s)


Try it online!

f is the main function, relying on A modifying n for non-1919191234567 check

A check if it's 3+3+3+3+2

R removes some triplet and go on Aing

# Javascript (SpiderMonkey), 807 bytes

[a,[b,c]]=readline(j=m=0).split
d=[...h='mpsz'].map(k=>!~(i=a.indexOf(k))?'':a.slice([j,j=i+1],i))
d[i=h.indexOf(c)]=[...d[i]].concat(b).sort().join
if(~(''+d).search(1+9+,){3}1+2+3+4+5+6+7|d.every(k=>/^(?:(.)\1(?!\1))*$/.test(k)))print(1);else A:{a=/(.)(\1*)/g;e=d.pop();while(n=a.exec(e))if(!n){print(0);break A}else if(n.length==1)if(m){print(0);break A}else m=1 f=l=>{if(!l.length)return;var[h,n,b]=l;if((a=n-h)>1)return 1;i=l.indexOf(+n+1);if(a==1)return!~i?1:f(l.slice(2,i)+l.slice(i+1));if(~i){c=l.slice(1,i)+l.slice(i+1,i=l.indexOf(+n+2))+l.slice(i+1);if(~i&&!f(c))return}return n-b||f(l.slice(3))};m=d.filter(k=>{if(!(c=k.length%3))return f(k);else if(c==2){r=/(.)\1/g;while(n=r.exec(k))if(!f(k.slice(0,n.index)+k.slice(r.lastIndex)))return m=1,0}return 1}).length?0:m print(m?1:0)}  Try it online! First js post! • Not a big deal, but the typical etiquette is to wait a week or more before answering your own question. Feb 9 at 5:43 # JavaScript, 348 bytes a=>[[(P=f=>w=>f(w)&&1-w.some((n,i)=>n>1&!f(W=[...w],W[i]-=2))/2)(w=>w.some(n=>([p,q]=[q,(n-p-q)%3],1/q<0),p=q=0)),P(w=>w.some(n=>n%3)),],[w=>w.some(n=>n&-3)],[w=>w.some((n,i)=>i%8==1^n>0),w=>w.some((n,i)=>0<i&i<8^n>0)]].some(([i,I=i])=>i(nm)+i(np)+i(ns)+I(nz)<1,n=t=>[...a.matchAll(\\d(?=\\d*${t}))].map(i=>s[i]++,s=Array(12).fill(0))&&s)


Passed all testcases. Though it may still be buggy. Comment any failed testcases, and I will try to fix them.

a=>[[
// given count of each tiles in same suit
// return 1 if it is not a valid hand
// return 0.5 if it is a valid hand with a pair
// return 0 if it is a valid hand without a pair
(P=f=>w=>f(w)&&1-w.some((n,i)=>n>1&!f(W=[...w],W[i]-=2))/2)
// for m, p, s
(w=>w.some(n=>([p,q]=[q,(n-p-q)%3],1/q<0),p=q=0)),
// for z
P(w=>w.some(n=>n%3)),
],[
// for 7 pairs rule
w=>w.some(n=>n&-3)
],[
// for 13 orphan rule
w=>w.some((n,i)=>i%8==1^n>0),
w=>w.some((n,i)=>0<i&i<8^n>0)
]].some(([i,I=i])=>i(nm)+i(np)+i(ns)+I(nz)<1,
// Parse 112233456666m -> [0, 2, 2, 2, 1, 1, 4, 0, 0, 0, 0, 0]
n=t=>[...a.matchAll(\\d(?=\\d*\${t}))].map(i=>s[i]++,s=Array(12).fill(0))&&s
);