mccarthyism

A subversive card game

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
import Card from './Card';
import cmpCard from './cmpCard';
import realRank from './realRank';

class Hand {
	size: number;
	overrideSize: boolean;
	matchFunc: (cards: Card[]) => Card | null;
	constructor(size: number, overrideSize: boolean, matchFunc: (cards: Card[]) => Card | null) {
		this.size = size;
		this.overrideSize = overrideSize;
		this.matchFunc = matchFunc;
	}
	match(cards: Card[]) {
		return cards.length === this.size && this.matchFunc(cards);
	}
}

const chkFlush = (cards: Card[]) => cards.every((card: Card) => card.suit === cards[0].suit);
const chkStraight = (cards: Card[]) => {
	let ok = true;
	for (let i = 1; i < 5; ++i)
		ok = ok && realRank(cards[0]) + i === realRank(cards[i]);
	if (ok) return true;
	// 3 4 5 6 2
	ok = cards[4].rank === 2;
	for (let i = 0; i < 4; ++i)
		ok = ok && cards[0].rank === 3 + i;
	if (ok) return true;
	return false;
};

const hands = [
	// Straight Flush
	new Hand(5, true, (cards: Card[]) => chkFlush(cards) && chkStraight(cards) ? cards[4] : null),
	// Four of a Kind
	new Hand(5, true, (cards: Card[]) =>
		(cards[0].rank === cards[3].rank ? cards[3] : null) ||
		(cards[1].rank === cards[4].rank ? cards[4] : null)
	),
	// Full House
	new Hand(5, false, (cards: Card[]) =>
		(cards[0].rank === cards[2].rank && cards[3].rank === cards[4].rank ? cards[2] : null) ||
		(cards[0].rank === cards[1].rank && cards[2].rank === cards[4].rank ? cards[4] : null)
	),
	// Straight
	new Hand(5, false, (cards: Card[]) => chkStraight(cards) ? cards[4] : null),
	// Flush
	new Hand(5, false, (cards: Card[]) => chkFlush(cards) ? cards[4] : null),
	// Pair
	new Hand(2, false, (cards: Card[]) => cards[0].rank === cards[1].rank ? cards[1] : null),
	// Single
	new Hand(1, false, (cards: Card[]) => cards[0])
];

const parseHand = (cards: Card[]) => {
	for (let i = 0; i < hands.length; ++i) {
		const card = hands[i].match(cards);
		if (card) return {id: i, card};
	}
	return null;
};

export default function canPlay(prev: Card[] | null, cur: Card[]) {
	const curHand = parseHand(cur);
	if (!curHand) return false;
	if (!prev) return true;
	const prevHand = parseHand(prev)!;
	if (!hands[curHand.id].overrideSize && cur.length !== prev.length) return false;
	if (curHand.id !== prevHand.id) return curHand.id < prevHand.id;
	return cmpCard(prevHand.card, curHand.card) < 0;
};