import {isEqual, omit, zip} from "lodash"; import {anyChar, int, space, whitespace} from "parjs"; import {between, manySepBy, manyTill, stringify, then} from "parjs/combinators"; import fs from "fs"; const CardLetterScores = ['J', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'Q', 'K', 'A']; export class CamelCard { private hand: Record; public readonly score: number; constructor(protected readonly card: string) { this.hand = this.card.split('').reduce((cards, char) => { if (!cards[char]) { cards[char] = 0; } cards[char] += 1; return cards; }, {} as Record) this.score = this.calculateScore(); } public compare(other: CamelCard): number { const diff = this.score - other.score; if (diff !== 0) return diff; for (const [a, b] of zip(this.card.split(''), other.card.split('')) as [string, string][]) { if (a !== b) { return CardLetterScores.indexOf(a) - CardLetterScores.indexOf(b) } } return 0; } private redistributeJ(): void { if ('J' in this.hand) { const js = this.hand.J; const withoutJ = omit(this.hand, 'J') as Record; const [mostCommon, mostCommonValue] = Object.entries(withoutJ).reduce(([maxKey, maxValue], [key, value]) => { if (value > maxValue) return [key, value]; return [maxKey, maxValue]; }, ['J', 0]); withoutJ[mostCommon] = mostCommonValue + js; this.hand = withoutJ; } } private calculateScore(): number { this.redistributeJ(); const cards = Object.values(this.hand).sort((a, b) => b-a); if (isEqual(cards, [1, 1, 1, 1, 1])) return 1; if (isEqual(cards, [2, 1, 1, 1])) return 2; if (isEqual(cards, [2, 2, 1])) return 3; if (isEqual(cards, [3, 1, 1])) return 4; if (isEqual(cards, [3, 2])) return 5; if (isEqual(cards, [4, 1])) return 6; if (isEqual(cards, [5])) return 7; return 0; } } const cardParser = anyChar().pipe(manyTill(space()), stringify()); const bidParser = int().pipe(between(whitespace())); const parser = cardParser.pipe(then(bidParser), manySepBy(whitespace())); export class CamelCards { private cards: [CamelCard, number][] = []; constructor(input: string) { const pairs = parser.parse(input).value; this.cards = pairs.map(([cards, bid]) => [new CamelCard(cards), bid]); } get winnings(): number { this.cards.sort(([a], [b]) => a.compare(b)); return this.cards.reduce((total, [_, value], index) => total + (value * (index + 1)), 0); } } export const runDaySeven = () => { const input = fs.readFileSync('./inputs/day_seven_input.txt', 'utf-8').trimEnd(); const cards = new CamelCards(input); console.log(cards.winnings); }