Help me cheat at Cheat

Cheat is a card game where you're trying to get rid of your hand. A turn looks something roughly like this:

1. Determine what card rank you have to play that turn. In most variants this is one rank higher than the previous turn.
2. Play 1-4 cards, face down. These don't have to match the valid rank. If they don't, it's considered a cheat. At this point any player may challenge you.
• If nobody challenges you, play continues to the next player.
• If somebody challenges you and you didn't cheat, they have to take all of the played cards.
• If somebody challenges you and you did cheat, you have to take all of the played cards.

The strategy is mostly a mix of bluffing and card counting. I'm bad at math, though, so I'm going to cheat at Cheat and bring a bot with me.

Input

The input will be, in any order or format you want:

1. The player's hand. If I know that they must have cards (for example, I've seen them pick them up), those will be listed in ascending order. Any unknowns will be listed afterwards as ?. For example, if they have six cards and I know that two are 1's and one is a 4, a valid representation of the hand is 114???. ?'s will always be listed after known cards. 141??? and 11???4 are both invalid inputs and your code doesn't need to handle them.
2. The cards I definitely know are not part of their hand (I have them, I saw someone else pick them up, etc). The list will be in ascending order of rank. The list may be empty.
3. The cards the player claimed to play. If they claimed to play 3 7's, a possible input here would be 777. The cards will always be the same rank.

The number of played cards will always be 1-4 and the ranks will always be 0-9. A rank will never appear more than four times in their hand + not their hand.

This is an example valid input:

33577??
01555688
55


This is an example invalid input:

35377?? # Out of order
7779    # Five 7's in total
23      # Two separate ranks played


Output

A truthy value if we should definitely challenge. A falsey value if we might not want to challenge.

We always challenge if we know they cheated. We know they cheated if they played cards they couldn't possibly have:

12

3  # They don't have any 3's or ?'s
-------------
12?

33 # Fewer ?'s than played cards
-------------
123?
333
33 # Since we have three 3's they can't have two


The other time we challenge is if they play their last cards. Even if the play's legit it would end the game so we might as well challenge it.

1

1 # They'd win, so challenge anyway


More Test Cases

Truthy (should challenge)

11445678?

22
-------------
????????
4
4444
-------------
12345678

9


Falsey (should not challenge)

1?
222
2
-------------
12?
22
22
-------------
?????

1111


Scoring

Shortest code wins.

• This is bullsh*t :) – jacksonecac Oct 26 '16 at 18:58
• How many people are playing? – jacksonecac Oct 26 '16 at 18:59
• @jacksonecac you're only looking at one play, so you don't have to worry about how many players there are. – Hovercouch Oct 26 '16 at 19:21
• This should be a king-of-the-hill challange – Vlo Oct 26 '16 at 20:41
• Shouldn't you be challenging in the second falsey case? – StephenTG Oct 27 '16 at 13:35

Pyth, 22 bytes

!}zS+*hzhS,/Q\?-4l@z+w


JavaScript (ES6), 93 bytes

(h,s,c,g=(a,z=c[0])=>a.split(z).length-1)=>h.length==g(c)|g(h,'?')+g(h)<g(c)|g(h)+g(s)+g(c)>4


Accepts three strings of cards (characters 0-9 or ?); returns 1 for challenge, 0 otherwise. Explanation:

(h,s,c,                 Input parameters
g=(a,z=c[0])=>         Helper function defaults to claimed card
a.split(z).length-1   Count cards matching (g(c) == c.length)
)=>h.length==g(c)       Does hand length equal claim length
|g(h,'?')+g(h)<g(c)   Could hand not contain claimed cards
|g(h)+g(s)+g(c)>4     Are there too many claimed cards


C#6, 134 bytes

using System.Linq;
bool f(string a,string b,string c)=>a.Length==1|a.Count(x=>x==c[0]|x=='?')<c.Length|b.Count(x=>x==c[0])+c.Length>4;


(Note: In line with OP's example, this answer returns false even if opponent has more than one card and is playing all of his hand. In terms of strategy this should return true.)

Explanation:

a: Player's hand
b: Cards I definitely know...
c: Cards claimed to play

Ungolfed:

bool f(string a, string b, string c) =>
a.Length == 1                               // Last card played
| a.Count(x => x == c[0] | x == '?' )       // Count no. of cards that surely/may match claimed cards
// Assuming all claimed cards are the same, just compare with c[0]
< c.Length                              // I'm sure you don't have that many cards...
| b.Count(x => x == c[0]) + c.Length > 4;   // ... Or that there can't be that many cards in play

• Shouldn't a.Length==1 be a.Length==c.Length. You seem to imply something like that in your note, but I don't know which OP's example you are referring to that makes it possible that more cards are being played then the amount of cards the opponent has in their hand. – Kevin Cruijssen Nov 10 '17 at 12:28

Java 8, 169 135 bytes

(a,b,c)->{int C=c.length();char x=c.charAt(0);return a.length()==C|(a+0).split(x+"|\\?",-1).length<=C|5-(b+0).split(""+x,-1).length<C;}


Explanation:

Try it here.

(a,b,c)->{            // Method with three String parameters and boolean return-type
int C=c.length();   //  The amount of cards played by your opponent this turn
char x=c.charAt(0); //  The card played by your opponent this turn
return a.length()==C//  Return true if your opponent played his entire hand (final round)
|(a+0).split(x+"|\\?",-1).length
//   Or the amount of the played card and '?' in your opponent's hand
<=C             //   is smaller than the amount of cards played
|5-(b+0).split(""+x,-1).length
//   Or if 4 minus the amount of the played card are part of the
//   cards you definitely know,
<C;              //   is smaller than the amount of cards played
}                     // End of method


NOTE: (s+0).split("c",-1).length-1 gives the amount of times the character c occurs in String s. So the comments in the explanation above are correct, but the code might seem different because of it. The (a+0).split(x+"|\\?",-1).length-1<C is golfed to (a+0).split(x+"|\\?",-1).length<=C and the 4-(b+0).split(""+x,-1).length-1<C is golfed to 5-(b+0).split(""+x,-1).length<C.