Skip to content

Commit b1e4847

Browse files
committed
Tidied code for 2023 Day 7
1 parent 8233049 commit b1e4847

File tree

3 files changed

+196
-145
lines changed

3 files changed

+196
-145
lines changed

src/routes/2023/day/07/hand.js

Lines changed: 12 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,27 @@
1-
const VALUE_MAP = {
2-
2: '02',
3-
3: '03',
4-
4: '04',
5-
5: '05',
6-
6: '06',
7-
7: '07',
8-
8: '08',
9-
9: '09',
10-
T: '10',
11-
J: '11',
12-
Q: '12',
13-
K: '13',
14-
A: '14'
15-
};
1+
import { handValue } from './pointsHand';
2+
import { cardValues } from './pointsCard';
163

17-
const JOKER_VAUE_MAP = JSON.parse(JSON.stringify(VALUE_MAP));
18-
JOKER_VAUE_MAP['J'] = '01';
19-
20-
function cardValue(card, joker) {
21-
if (joker === true) {
22-
return JOKER_VAUE_MAP[card];
23-
}
24-
return VALUE_MAP[card];
25-
}
26-
27-
function cardValuesString(cards, joker) {
28-
return cards
29-
.split('')
30-
.map((card) => cardValue(card, joker))
31-
.join('');
32-
}
33-
34-
function countCardsInHand(hand) {
35-
const handSplit = hand.split('');
36-
37-
const cardCounts = [];
38-
for (const card in VALUE_MAP) {
39-
cardCounts.push(handSplit.filter((c) => c === card).length);
40-
}
41-
42-
return cardCounts;
43-
}
44-
45-
function countPairs(cardCounts) {
46-
return cardCounts.filter((count) => count === 2).length;
47-
}
48-
49-
const HAND_VALUE_MAP = {
50-
five_of_a_kind: 6,
51-
four_of_a_kind: 5,
52-
full_house: 4,
53-
three_of_a_kind: 3,
54-
two_pairs: 2,
55-
one_pair: 1,
56-
high_card: 0
57-
};
58-
59-
function handValue(hand, joker) {
60-
const cardCounts = countCardsInHand(hand);
61-
const maxNumberOfCards = Math.max(...cardCounts);
62-
63-
let jokerCount = 0;
64-
if (joker === true) {
65-
jokerCount = cardCounts[9];
66-
}
67-
68-
// Hard-coded cases
69-
// Full house
70-
if (maxNumberOfCards === 5) {
71-
return HAND_VALUE_MAP['five_of_a_kind'];
72-
}
73-
74-
// Four of a kind
75-
if (maxNumberOfCards === 4) {
76-
if (jokerCount) {
77-
// 1 or 4 jokers
78-
return HAND_VALUE_MAP['five_of_a_kind'];
79-
}
80-
return HAND_VALUE_MAP['four_of_a_kind'];
81-
}
82-
83-
// Full house OR three of a kind
84-
if (maxNumberOfCards === 3) {
85-
// Full house
86-
if (countPairs(cardCounts) === 1) {
87-
if (jokerCount) {
88-
// 2 or 3 jokers
89-
return HAND_VALUE_MAP['five_of_a_kind'];
90-
}
91-
return HAND_VALUE_MAP['full_house'];
92-
}
93-
94-
// Three of a kind
95-
if (jokerCount) {
96-
// 1 or 3 jokers
97-
return HAND_VALUE_MAP['four_of_a_kind'];
98-
}
99-
return HAND_VALUE_MAP['three_of_a_kind'];
100-
}
101-
102-
// Two pairs OR one pair
103-
if (maxNumberOfCards === 2) {
104-
// Two pairs
105-
if (countPairs(cardCounts) === 2) {
106-
if (jokerCount === 2) {
107-
return HAND_VALUE_MAP['four_of_a_kind'];
108-
}
109-
if (jokerCount === 1) {
110-
return HAND_VALUE_MAP['full_house'];
111-
}
112-
return HAND_VALUE_MAP['two_pairs'];
113-
}
114-
115-
// One pair
116-
if (jokerCount === 1 || jokerCount === 2) {
117-
return HAND_VALUE_MAP['three_of_a_kind'];
118-
}
119-
return HAND_VALUE_MAP['one_pair'];
120-
}
121-
122-
// Nothing special ("high card")
123-
if (maxNumberOfCards === 1) {
124-
if (jokerCount === 1) {
125-
return HAND_VALUE_MAP['one_pair'];
126-
}
127-
return HAND_VALUE_MAP['high_card'];
128-
}
129-
throw "It seems something went wrong, and your hand didn't contain cards.";
4+
function completeValueAsInteger(hand, joker) {
5+
return Number(handValue(hand, joker) + cardValues(hand, joker));
1306
}
1317

132-
// function handValueString(hand) {
133-
// return handValue(hand).toString()
134-
// }
135-
1368
export class Hand {
1379
constructor(handString, joker) {
13810
const split = handString.split(' ');
139-
this.cards = split[0];
14011
this.rank = -1;
141-
this.bid = Number(split[1]);
142-
143-
// Pre-calculate value
144-
const hand = new String(handValue(this.cards, joker));
145-
const card = cardValuesString(this.cards, joker);
146-
this.value = Number(hand + card);
12+
this.bid = Number(split[1]);
13+
this.value = completeValueAsInteger(split[0], joker);
14714
}
14815

14916
difference(hand) {
15017
return this.value - hand.value;
15118
}
15219

153-
setRank(rank) {
154-
this.rank = rank;
155-
}
20+
setRank(rank) {
21+
this.rank = rank;
22+
}
15623

157-
score() {
158-
return this.rank * this.bid;
159-
}
24+
score() {
25+
return this.rank * this.bid;
26+
}
16027
}

src/routes/2023/day/07/pointsCard.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Stringified integer representations of the value of a card.
3+
*/
4+
export const CARD = {
5+
2: '02',
6+
3: '03',
7+
4: '04',
8+
5: '05',
9+
6: '06',
10+
7: '07',
11+
8: '08',
12+
9: '09',
13+
T: '10',
14+
J: '11',
15+
Q: '12',
16+
K: '13',
17+
A: '14'
18+
};
19+
20+
const CARD_JOKER = JSON.parse(JSON.stringify(CARD));
21+
CARD_JOKER['J'] = '01';
22+
23+
/**
24+
* Calculates the value of one card. Has a flag for if joker rules are on or not.
25+
*/
26+
function cardValue(card, joker) {
27+
if (joker === true) {
28+
return CARD_JOKER[card];
29+
}
30+
return CARD[card];
31+
}
32+
33+
/**
34+
* Calculates the value of all cards in a hand, as a length-ten stringified integer.
35+
*/
36+
export function cardValues(cards, joker) {
37+
return cards
38+
.split('')
39+
.map((card) => cardValue(card, joker))
40+
.join('');
41+
}

src/routes/2023/day/07/pointsHand.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { CARD } from './pointsCard';
2+
3+
/**
4+
* Creates an array of counts of how many cards there are of each type.
5+
*/
6+
function countCardsInHand(hand) {
7+
const handSplit = hand.split('');
8+
9+
const cardCounts = [];
10+
for (const card in CARD) {
11+
cardCounts.push(handSplit.filter((c) => c === card).length);
12+
}
13+
14+
return cardCounts;
15+
}
16+
17+
/**
18+
* Counts the number of pairs in a hand.
19+
*/
20+
function countPairs(cardCounts) {
21+
return cardCounts.filter((count) => count === 2).length;
22+
}
23+
24+
/**
25+
* Calculates statistics about a hand.
26+
*/
27+
function handStatistics(hand, joker) {
28+
const cardCounts = countCardsInHand(hand);
29+
const maxCardCount = Math.max(...cardCounts);
30+
const pairs = countPairs(cardCounts);
31+
32+
let jokerCount = 0;
33+
if (joker === true) {
34+
jokerCount = cardCounts[9];
35+
}
36+
return { maxCardCount: maxCardCount, pairs: pairs, jokerCount: jokerCount };
37+
}
38+
39+
/**
40+
* Integer representations of the value of a hand.
41+
*/
42+
const HAND = {
43+
FiveOfAKind: 6,
44+
FourOfAKind: 5,
45+
FullHouse: 4,
46+
ThreeOfAKind: 3,
47+
TwoPairs: 2,
48+
OnePair: 1,
49+
HighCard: 0
50+
};
51+
52+
function pointsFiveOfAKind(jokerCount) {
53+
return HAND['FiveOfAKind'];
54+
}
55+
56+
function pointsFourOfAKind(jokerCount) {
57+
// We can have 1 or 4 jokers; both of which allow us to change to five of a kind.
58+
if (jokerCount) {
59+
return HAND['FiveOfAKind'];
60+
}
61+
return HAND['FourOfAKind'];
62+
}
63+
64+
function pointsFullHouse(jokerCount) {
65+
// We can have 2 or 3 jokers; both of which change us to be a five of a kind.
66+
if (jokerCount) {
67+
return HAND['FiveOfAKind'];
68+
}
69+
return HAND['FullHouse'];
70+
}
71+
72+
function pointsThreeOfAKind(jokerCount) {
73+
// We can have 1 or 3 jokers; both of which change us to be a four of a kind.
74+
if (jokerCount) {
75+
return HAND['FourOfAKind'];
76+
}
77+
return HAND['ThreeOfAKind'];
78+
}
79+
80+
function pointsTwoPairs(jokerCount) {
81+
// We can have 1 or 2 jokers, giving us a four of a kind or a full house.
82+
if (jokerCount === 2) {
83+
return HAND['FourOfAKind']; // Change a pair into the other pair
84+
}
85+
if (jokerCount === 1) {
86+
return HAND['FullHouse']; // Change the lone joker into one of the pairs
87+
}
88+
return HAND['TwoPairs'];
89+
}
90+
91+
function pointsOnePair(jokerCount) {
92+
// We can have 1 or 2 jokers; both of which change us to be a three of a kind.
93+
if (jokerCount) {
94+
return HAND['ThreeOfAKind'];
95+
}
96+
return HAND['OnePair'];
97+
}
98+
99+
function pointsHighCard(jokerCount) {
100+
// We can have 1 joker; which allows us to make a pair.
101+
if (jokerCount) {
102+
return HAND['OnePair'];
103+
}
104+
return HAND['HighCard'];
105+
}
106+
107+
/**
108+
* Calculates the point value of a hand based on three statistics about it.
109+
*/
110+
function points(maxCardCount, pairs, jokerCount) {
111+
// Every case is hard-coded, including the interaction with jokers. I couldn't think
112+
// of a nicer way to do it that would also be quick... maybe there is one, though!
113+
if (maxCardCount === 5) {
114+
return pointsFiveOfAKind(jokerCount);
115+
}
116+
if (maxCardCount === 4) {
117+
return pointsFourOfAKind(jokerCount);
118+
}
119+
if (maxCardCount === 3) {
120+
if (pairs === 1) {
121+
return pointsFullHouse(jokerCount);
122+
}
123+
return pointsThreeOfAKind(jokerCount);
124+
}
125+
if (maxCardCount === 2) {
126+
if (pairs === 2) {
127+
return pointsTwoPairs(jokerCount);
128+
}
129+
return pointsOnePair(jokerCount);
130+
}
131+
if (maxCardCount === 1) {
132+
return pointsHighCard(jokerCount);
133+
}
134+
throw "It seems something went wrong, and your hand didn't contain cards.";
135+
}
136+
137+
/**
138+
* Calculates the value of a hand as a stringified integer.
139+
*/
140+
export function handValue(hand, joker) {
141+
const { maxCardCount, pairs, jokerCount } = handStatistics(hand, joker);
142+
return new String(points(maxCardCount, pairs, jokerCount));
143+
}

0 commit comments

Comments
 (0)