335 lines
8.1 KiB
TypeScript
335 lines
8.1 KiB
TypeScript
const sample = await Deno.readTextFile("sample.txt");
|
|
const input = await Deno.readTextFile("input.txt");
|
|
|
|
const cardsSolution1 = [
|
|
"2",
|
|
"3",
|
|
"4",
|
|
"5",
|
|
"6",
|
|
"7",
|
|
"8",
|
|
"9",
|
|
"T",
|
|
"J",
|
|
"Q",
|
|
"K",
|
|
"A",
|
|
] as const;
|
|
|
|
const cardsSolution2 = [
|
|
"J",
|
|
"2",
|
|
"3",
|
|
"4",
|
|
"5",
|
|
"6",
|
|
"7",
|
|
"8",
|
|
"9",
|
|
"T",
|
|
"Q",
|
|
"K",
|
|
"A",
|
|
] as const;
|
|
type CardListSolution1 = typeof cardsSolution1;
|
|
type CardListSolution2 = typeof cardsSolution2;
|
|
type CardList = CardListSolution1 | CardListSolution2;
|
|
type Card = typeof cardsSolution1[number];
|
|
type Cards = string;
|
|
|
|
type Hand = [
|
|
[Card, number],
|
|
[Card, number],
|
|
[Card, number],
|
|
[Card, number],
|
|
[Card, number],
|
|
];
|
|
type GroupedHand = Partial<Record<Card, number>>;
|
|
type CardCounts = number[];
|
|
|
|
const getCardValue = (
|
|
cards: CardList,
|
|
card: Card,
|
|
): number => cards.indexOf(card) + 1;
|
|
|
|
const groupHand = (hand: Hand): GroupedHand =>
|
|
hand.reduce(
|
|
(acc, [card]) => ({ ...acc, [card]: (acc[card] || 0) + 1 }),
|
|
{} as GroupedHand,
|
|
);
|
|
const countSameCards = (
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): CardCounts => {
|
|
if (!withJokers) return Object.values(groupedHand).sort((a, b) => b - a);
|
|
const groupedHandWithoutJokers = { ...groupedHand } satisfies GroupedHand;
|
|
delete groupedHandWithoutJokers.J;
|
|
return Object.values(groupedHandWithoutJokers).sort((a, b) => b - a);
|
|
};
|
|
|
|
class JokerCounter {
|
|
constructor(private jokers: number) {}
|
|
|
|
getMax() {
|
|
const jokers = Math.max(0, this.jokers);
|
|
this.jokers = this.jokers - jokers;
|
|
return jokers;
|
|
}
|
|
|
|
getDelta(max: number, current: number) {
|
|
const jokersNeeded = Math.abs(max - current);
|
|
const currentNumberOfJokers = this.jokers;
|
|
const hasEnoughJokers = currentNumberOfJokers >= jokersNeeded;
|
|
if (!hasEnoughJokers) {
|
|
this.jokers = 0;
|
|
return currentNumberOfJokers + current;
|
|
}
|
|
this.jokers = this.jokers - jokersNeeded;
|
|
return max;
|
|
}
|
|
|
|
get(count = 1) {
|
|
const jokers = Math.max(0, this.jokers - count);
|
|
this.jokers = this.jokers - jokers;
|
|
return jokers;
|
|
}
|
|
}
|
|
|
|
const handTypes = [
|
|
function fiveOfAKind(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
return jokers.getDelta(5, cardCounts[0]) >= 5;
|
|
},
|
|
function fourOfAKind(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
return jokers.getDelta(4, cardCounts[0]) >= 4;
|
|
},
|
|
function fullHouse(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
return (jokers.getDelta(3, cardCounts[0]) === 3 &&
|
|
jokers.getDelta(2, cardCounts[1]) === 2);
|
|
},
|
|
function threeOfAKind(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
return jokers.getDelta(3, cardCounts[0]) === 3 &&
|
|
jokers.getDelta(1, cardCounts[1]) === 1 &&
|
|
jokers.getDelta(1, cardCounts[2]) === 1;
|
|
},
|
|
function twoPair(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
const numberOfPairs = cardCounts.filter((count) => count === 2).length;
|
|
if (numberOfPairs === 2) return true;
|
|
if (numberOfPairs === 1) return jokers.get(2) === 2;
|
|
return jokers.get(2) === 2 && jokers.get(2) === 2;
|
|
},
|
|
function onePair(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
const hasOnePair = cardCounts.filter((count) => count === 2).length === 1;
|
|
if (hasOnePair) return true;
|
|
return jokers.get(2) === 2;
|
|
},
|
|
function highCard(
|
|
cardCounts: CardCounts,
|
|
groupedHand: GroupedHand,
|
|
withJokers = false,
|
|
): boolean {
|
|
const jokers = new JokerCounter(withJokers ? groupedHand.J || 0 : 0);
|
|
const length = cardCounts.length;
|
|
return jokers.getDelta(5, length) === 5;
|
|
},
|
|
];
|
|
|
|
const handTypeName = [
|
|
"fiveOfAKind",
|
|
"fourOfAKind",
|
|
"fullHouse",
|
|
"threeOfAKind",
|
|
"twoPair",
|
|
"onePair",
|
|
"highCard",
|
|
] as const;
|
|
|
|
const compareHands = (
|
|
cards: CardList,
|
|
) =>
|
|
(
|
|
[, hand1, , handType1]: [Cards, Hand, number, number],
|
|
[, hand2, , handType2]: [Cards, Hand, number, number],
|
|
): -1 | 0 | 1 => {
|
|
if (handType1 < handType2) {
|
|
return -1;
|
|
} else if (handType1 > handType2) {
|
|
return 1;
|
|
} else {
|
|
const hand1TotalValue = hand1.reduce(
|
|
(s, [card]) => s + getCardValue(cards, card),
|
|
0,
|
|
);
|
|
const hand2TotalValue = hand2.reduce(
|
|
(s, [card]) => s + getCardValue(cards, card),
|
|
0,
|
|
);
|
|
console.log(
|
|
"hand1",
|
|
handType1,
|
|
hand1,
|
|
hand1TotalValue,
|
|
);
|
|
console.log(
|
|
"hand2",
|
|
handType2,
|
|
hand2,
|
|
hand2TotalValue,
|
|
);
|
|
|
|
return hand1TotalValue > hand2TotalValue ? 1 : -1;
|
|
}
|
|
// for (let i = 0; i < 5; i += 1) {
|
|
// console.log(
|
|
// "hand1",
|
|
// handType1,
|
|
// hand1[i][0],
|
|
// getCardValue(cardsSolution1, hand1[i][0]),
|
|
// );
|
|
// console.log(
|
|
// "hand2",
|
|
// handType2,
|
|
// hand2[i][0],
|
|
// getCardValue(cardsSolution1, hand2[i][0]),
|
|
// );
|
|
// if (hand1[i] > hand2[i]) return 1;
|
|
// if (hand1[i] < hand2[i]) return -1;
|
|
// }
|
|
// return 0;
|
|
// }
|
|
};
|
|
|
|
const solvePart1 = (data: string): number => {
|
|
return data
|
|
.split("\n")
|
|
.filter(Boolean)
|
|
.map((line) => line.split(" "))
|
|
// Convert lines to hands and bids
|
|
.map((
|
|
[hand, bid],
|
|
): [Cards, Hand, number] => {
|
|
const [card1, card2, card3, card4, card5] = hand.toUpperCase().split("")
|
|
.map((
|
|
card,
|
|
): [Card, number] => [
|
|
card as Card,
|
|
getCardValue(cardsSolution1, card as Card),
|
|
]);
|
|
return [
|
|
hand,
|
|
[card1, card2, card3, card4, card5],
|
|
Number(bid),
|
|
];
|
|
// Determine hand type per hand
|
|
}).map((
|
|
[cards, hand, bid],
|
|
): [Cards, Hand, number, number] => [
|
|
cards,
|
|
hand,
|
|
bid,
|
|
handTypes.length -
|
|
(handTypes.findIndex((handType) => {
|
|
return handType(countSameCards(groupHand(hand)), groupHand(hand));
|
|
}) + 1),
|
|
])
|
|
// Sort the hands by hand type and card value
|
|
.sort(compareHands(cardsSolution1)).map((
|
|
result,
|
|
index,
|
|
): [Cards, Hand, number, number, number, number] => [
|
|
...result,
|
|
index + 1,
|
|
(index + 1) * result[2],
|
|
])
|
|
.reduce((s, [, , , , , v]) => s + v, 0);
|
|
};
|
|
|
|
console.log("Sample:", solvePart1(sample)); // 6440
|
|
console.log("Input", solvePart1(input)); // 251927063
|
|
|
|
const solvePart2 = (data: string): number => {
|
|
const r = data
|
|
.split("\n")
|
|
.filter(Boolean)
|
|
.map((line) => line.split(" "))
|
|
// Convert lines to hands and bids
|
|
.map((
|
|
[hand, bid],
|
|
): [Cards, Hand, number] => {
|
|
const [card1, card2, card3, card4, card5] = hand.toUpperCase().split("")
|
|
.map((
|
|
card,
|
|
): [Card, number] => [
|
|
card as Card,
|
|
getCardValue(cardsSolution2, card as Card),
|
|
]);
|
|
return [
|
|
hand,
|
|
[card1, card2, card3, card4, card5],
|
|
Number(bid),
|
|
];
|
|
// Determine hand type per hand
|
|
}).map((
|
|
[cards, hand, bid],
|
|
): [Cards, Hand, number, number] => [
|
|
cards,
|
|
hand,
|
|
bid,
|
|
handTypes.length -
|
|
(handTypes.findIndex((handType) => {
|
|
return handType(
|
|
countSameCards(groupHand(hand), true),
|
|
groupHand(hand),
|
|
true,
|
|
);
|
|
}) + 1),
|
|
])
|
|
// Sort the hands by hand type and card value
|
|
.sort(compareHands(cardsSolution2)).map((
|
|
result,
|
|
index,
|
|
): [Cards, Hand, number, number, number, number] => [
|
|
...result,
|
|
index + 1,
|
|
(index + 1) * result[2],
|
|
]);
|
|
console.log(
|
|
"r",
|
|
r,
|
|
);
|
|
return r.reduce((s, [, , , , , v]) => s + v, 0);
|
|
};
|
|
|
|
console.log("Sample:", solvePart2(sample));
|
|
// console.log("Input", solvePart2(input));
|