95 lines
2.6 KiB
TypeScript
95 lines
2.6 KiB
TypeScript
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<string, number>;
|
|
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<string, number>)
|
|
|
|
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<string, number>;
|
|
|
|
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);
|
|
} |