Tichu is a card game in which players take turn playing sets of cards from a deck consisting of a standard 52-card deck, plus 4 additional cards:

  • the dragon, which has a greater value than any other card
  • the phoenix, which can act as a wildcard
  • the dog, which passes the turn to your partner
  • the Mah Jong, which has value 1 (and the person holding it plays first)

The first player (who is said to have "lead") can choose to play one of the following types of card combinations:

  • a single (e.g. 6)
  • a pair (JJ)
  • a triple (555)
  • a full house (QQQ33) - a triple and a pair
  • a straight (56789) - 5 or more consecutive cards
  • a tractor (223344) - any consecutive sequence of pairs

Subsequent players are then only allowed to play a set of cards of the same type, but strictly higher. For example, QQ can be played on top of JJ, but QQKK cannot (it is a tractor, not a pair). Full houses are ordered by the triple (e.g. 77722 > 44499), and straights and tractors must be the same length (456789 cannot be played on top of 23456). Aces are high.

There is one exception: any 4 of the same card is a bomb, and can be played on top of anything except a higher bomb.1

The dragon can be played by itself on top of any single or with lead (but nowhere else). The phoenix, aside from being a wildcard, can also be played on top of any single except the dragon.2 The dog can only be played by itself with lead and immediately ends the turn.3

Your challenge is to determine whether a given Tichu play is valid, given the previous play.

You may accept both plays in any order as either lists of integers or strings -- in either case, you may choose any mapping of cards to integers/characters. If there was a previous play, it will always be valid, and if not (i.e. the player has lead), the first input will be the empty array/string (matching the type of the other input). The cards are not guaranteed to be given in any particular order.

Your output must be selected from a set of exactly two distinct values, one of which indicates the play is legal and one that indicates it is not.

There is no need to test for whether the set of cards actually exists in the deck (e.g. 77766 followed by 88877 is impossible because there are only four 7's) -- such cases will never be given.

In the following test cases, 234567890JQKA represent 2 through ace, and RPD1 represent the dragon, phoenix, dog, and Mah Jong respectively. The empty string is shown here as -. These plays are legal:

6 J
555 KKK
44499 77722
23456 56789
223344 QQKKAA
49494 72727
66 7P
P6 77
58304967 6P0594J7
5P304967 680594J7
57446765 788657P5
- D
- 1
- 12345
3344556677889900 JJJJ
5555 7777

And these are not:

9 3
66 55
888 444
44 888
77722 44499
44499 777
44499 777JJJ
45678 34567
34567 456789
556677 334455
5566 778899
72727 49494
77 RP
77 6P
P7 66
680594J7 5P304967
6P0594J7 58304967
57446765 3645P536
1 D
2 D
2 1
- 1234
7777 5555
- 223355

1: actually, a straight flush is also a bomb, but since this is the only place in the game that the suit of the cards matter, I have chosen to leave it out for simplicity's sake

2: the value of the phoenix played on top of a card with value n is actually n+0.5 (a phoenix on a 9 is a 9 and a half); since this requires knowledge of additional history to adjudicate, no test cases involve a single played on top of a single phoenix

3: so the first input will never be dog

  • \$\begingroup\$ @Arnauld Not all are valid combinations (e.g. 777JJJ, 1234, 223355). However, RP is not valid: "The dragon can be played by itself on top of any single or with lead (but nowhere else)." \$\endgroup\$ – Doorknob Sep 25 at 15:41
  • \$\begingroup\$ Indeed, I was just about to edit my comment to say that all first hands seem to be always valid. \$\endgroup\$ – Arnauld Sep 25 at 15:43
  • 1
    \$\begingroup\$ @Arnauld Ah - yes, you may assume all first hands are valid. \$\endgroup\$ – Doorknob Sep 25 at 15:46
  • \$\begingroup\$ It may be worth mentioning that a Phenix may not be used as a wildcard in a bomb. 5555 777P should be an invalid play, but both current answers mark it as valid. \$\endgroup\$ – Jitse Sep 30 at 8:24

JavaScript (ES6),  274  273 bytes

Takes input as (a)(b), where \$a\$ and \$b\$ are arrays of integers with:

  • \$1\$ = dog
  • \$3\$ = Mah Jong
  • \$4..16\$ = 2 ... Ace
  • \$18\$ = dragon
  • \$19\$ = phoenix

Returns false for valid or true for invalid.


Try it online!


To evaluate a hand, we build an array o[] made of a leading \$9\$, initially followed by 16 zeros:

//       D  -  1  2  3  4  5  6  7  8  9  T  J  Q  K  A
o = [ 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];

For each card rank in the hand, the corresponding slot in o[] is incremented. Once joined back to a string, we can apply the following regular expressions to detect each type of hand:

 hand        | pattern     | full regex              | example
 single      | 1           | /9(0*)(1)0*$/           | 90000100000000000
 pair        | 2           | /9(0*)(2)0*$/           | 90000002000000000
 3-of-a-kind | 3           | /9(0*)(3)0*$/           | 90003000000000000
 full house  | (20*)3|30*2 | /9(0*)((20*)3|30*2)0*$/ | 90020000030000000
 straight 5+ | 1{5,}       | /9(0*)(1{5,})0*$/       | 90000111110000000
 tractor     | 22+         | /9(0*)(22+)0*$/         | 90000000000022200
 bomb        | 4           | /9(0*)(4)0*$/           | 90000000000000004

If the hand does not trigger any of these regular expressions, it's invalid.

The value of the hand is given by the number of zeros just after the leading \$9\$ (the higher, the better). A special case is a full house where the rank of the triple is higher than that of the pair, in which case we need to add the length of the (20*) pattern.

The phoenix (i.e. the wildcard) is simply replaced with each possible card rank, starting with the highest one, until a match is detected.


Python 3, 466 455 403 401 399 bytes

R=lambda H,n=3:n==len(H)*(len({*H})<2)and[15]!=H[1:]
s=lambda H:[H[2:],H[:1]][R(H[:3])]
def n(J,K):j,k=[[16in H,R(H,4),len(H)>4and all(15!=t>T-2for t,T in zip(H,H[1:])),[R(H[2:])*R(H[:2],2),R(H[3:],2)][R(H[:3])],R(H),R(H,2),R(H,1),1].index(1)for H in[J,K]];return k!=7and[17]==J or[j==k*[J<K,s(J)<s(K)][2<j<5],J<K][k==1]
f=lambda P:any(n(*[sorted([L,i][i>0]for i in H)for H in P])for L in range(16))

Try it online! Input is list of hands where hand is list of integers1 with the following value mapping:

  • 0: Phoenix
  • 1: Mah Jong
  • 2-13: 2 to Ace
  • 14: Dragon
  • 15: Dog
  • 16: Empty string

1: Although in the linked TIO tests are expressed with list of strings due to convenience which are translated according to value mapping before calling f


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.