7_2.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { LoadInput } from "../common.ts";
  2. import { type CamelCardsHand, CamelCardRanking } from "./7_common.ts";
  3. const tests = [
  4. "32T3K 765",
  5. "T55J5 684",
  6. "KK677 28",
  7. "KTJJT 220",
  8. "QQQJA 483",
  9. ];
  10. const tests_reddit = [
  11. "2345A 1",
  12. "Q2KJJ 13",
  13. "Q2Q2Q 19",
  14. "T3T3J 17",
  15. "T3Q33 11",
  16. "2345J 3",
  17. "J345A 2",
  18. "32T3K 5",
  19. "T55J5 29",
  20. "KK677 7",
  21. "KTJJT 34",
  22. "QQQJA 31",
  23. "JJJJJ 37",
  24. "JAAAA 43",
  25. "AAAAJ 59",
  26. "AAAAA 61",
  27. "2AAAA 23",
  28. "2JJJJ 53",
  29. "JJJJ2 41",
  30. ];
  31. const input = await LoadInput(7);
  32. // Parse the input
  33. const parsedHands = ParseInput(input);
  34. // Sort the cards
  35. const sortedHands = parsedHands.sort(SortHands);
  36. // Find the input
  37. let totalWinnings = 0;
  38. sortedHands.forEach((hand, idx) => {
  39. totalWinnings += hand.Bid * (idx + 1);
  40. });
  41. console.log(`The total winnings are $${totalWinnings}`);
  42. /**
  43. * Parse the input
  44. *
  45. * @param {string[]} handList An array of strings represnting hands of Camel Cards and their bids
  46. * @returns {CamelCardsHand[]} An array of parsed Camel Card hands
  47. */
  48. function ParseInput(handList: string[]): CamelCardsHand[] {
  49. const output: CamelCardsHand[] = [];
  50. handList.forEach((hand) => {
  51. const [handStr, bidStr] = hand.split(" ");
  52. output.push({
  53. Hand: handStr,
  54. Bid: Number(bidStr),
  55. Rank: FindCamelCardRank(handStr),
  56. });
  57. })
  58. return output;
  59. }
  60. /**
  61. * Assign a base rank to a hand of Camel Cards
  62. *
  63. * Assigns a Camel Card Ranking to a hand of cards based on the
  64. * strength of the hand on its own.
  65. *
  66. * @param {string} hand The hand a player drew
  67. * @returns {CamelCardRanking} The ranking of the hand
  68. */
  69. function FindCamelCardRank(hand: string): CamelCardRanking {
  70. const cardFacesSorted = ReplaceJokers(hand).split("").sort().join("");
  71. const fiveOfKind = new RegExp("(.)\\1{4}", "g");
  72. const fourOfKind = new RegExp("(.)\\1{3}", "g");
  73. const threeOfKind = new RegExp("(.)\\1{2}", "g");
  74. const twoOfKind = new RegExp("(.)\\1{1}", "g");
  75. // Can't get these two to work in one regex, so I'll split them for now
  76. const fullHouse_A = new RegExp("(.)\\1(.)\\2{2}", "g");
  77. const fullHouse_B = new RegExp("(.)\\1{2}(.)\\2", "g");
  78. if (cardFacesSorted.replace(twoOfKind, "").length == 5) {
  79. return CamelCardRanking.HIGH_CARD;
  80. }
  81. else if(cardFacesSorted.replace(fiveOfKind, "").length == 0) {
  82. return CamelCardRanking.FIVE_OF_A_KIND;
  83. }
  84. else if(cardFacesSorted.replace(fourOfKind, "").length == 1) {
  85. return CamelCardRanking.FOUR_OF_A_KIND;
  86. }
  87. else if(cardFacesSorted.replace(fullHouse_A, "").length == 0
  88. || cardFacesSorted.replace(fullHouse_B, "").length == 0) {
  89. return CamelCardRanking.FULL_HOUSE;
  90. }
  91. else if(cardFacesSorted.replace(threeOfKind, "").length == 2) {
  92. return CamelCardRanking.THREE_OF_A_KIND;
  93. }
  94. else if(cardFacesSorted.replace(twoOfKind, "").length == 1) {
  95. return CamelCardRanking.TWO_PAIR;
  96. }
  97. else if(cardFacesSorted.replace(twoOfKind, "").length == 3) {
  98. return CamelCardRanking.ONE_PAIR;
  99. }
  100. else {
  101. throw new Error(`Unsure how we reached this condition!\nSorted hand was: ${cardFacesSorted}`);
  102. }
  103. }
  104. /**
  105. * Compare two hands of Camel Cards for sorting
  106. */
  107. function SortHands(a: CamelCardsHand, b: CamelCardsHand): number {
  108. if (a.Rank < b.Rank) { return -1; }
  109. if (a.Rank > b.Rank) { return 1; }
  110. // Replace the letters with something easier to compare
  111. const aHand = a.Hand.replaceAll("A", "Z").replaceAll("K", "Y").replaceAll("Q", "X").replaceAll("J", "0").replaceAll("T", "V");
  112. const bHand = b.Hand.replaceAll("A", "Z").replaceAll("K", "Y").replaceAll("Q", "X").replaceAll("J", "0").replaceAll("T", "V");
  113. for (let i = 0; i < 5; i++) {
  114. if (aHand[i] < bHand[i]) {
  115. return -1;
  116. }
  117. if (aHand[i] > bHand[i]) {
  118. return 1;
  119. }
  120. }
  121. return 0;
  122. }
  123. function ReplaceJokers(hand: string): string {
  124. // Make sure the hand has a joker card, but is not only joker cards, before attempting the replacement
  125. if (!/J/.test(hand) && hand != "JJJJJ") { return hand; }
  126. /** Track how many jokers we need to add back to the hand */
  127. const jokerCount = hand.replace(/[^J]/g, "").length;
  128. /** An array containing the cards in the hand less any jokers */
  129. const handFaces = hand.replaceAll("J", "").split("");
  130. /** An object for counting the amounts of each face in the hand */
  131. const faceMatches: { [face: string]: number; } = {"": -1};
  132. /* Count the amount of each face in the string */
  133. handFaces.forEach((face) => {
  134. if (faceMatches[face] == undefined) {
  135. faceMatches[face] = 0;
  136. }
  137. faceMatches[face]++;
  138. });
  139. /* Find the most occurring face in the hand */
  140. let highestCard:string = "";
  141. for (const face in faceMatches) {
  142. if (faceMatches[face] > faceMatches[highestCard]) {
  143. highestCard = face;
  144. }
  145. }
  146. return handFaces.join("") + highestCard.repeat(jokerCount);
  147. }