-
Notifications
You must be signed in to change notification settings - Fork 4
/
ScoreCalculator.ts
147 lines (144 loc) · 5.28 KB
/
ScoreCalculator.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import groupBy from 'lodash/groupBy';
import isEqual from 'lodash/isEqual';
import orderBy from 'lodash/orderBy';
import sum from 'lodash/sum';
import { Player } from '../types/Player';
import { ScoringCategory, ScoringCategoryDescriptions } from '../types/Scoring';
export interface IScoreCalculator {
calculateUpperSectionTotal: (player: Player) => number;
calculateUpperSectionBonus: (player: Player) => number;
calculateLowerSectionTotal: (player: Player) => number;
calculateTotal: (player: Player) => number;
calculators: Record<ScoringCategory, (dice: number[]) => number>;
}
const groupDice = (dice: number[]) => {
// Group dice by their value, then order them by the count of each value showing (descending)
const grouped = groupBy(dice);
let result: number[][] = [];
for (let d = 1; d <= 6; d++) {
if (grouped[d]) result.push(grouped[d]);
}
result = orderBy(result, (arr) => arr.length, "desc");
return result;
};
export const ScoreCalculator: IScoreCalculator = {
calculateUpperSectionTotal: (player: Player) => {
const upperTotal = ScoringCategoryDescriptions
.filter((scd) => scd.section === "Upper")
.map((scd) => player.scoring[scd.category] ?? 0)
.reduce((total, previous) => (total ?? 0) + (previous ?? 0)) ?? 0;
return upperTotal;
},
calculateUpperSectionBonus: (player: Player) => {
const upperTotal = ScoreCalculator.calculateUpperSectionTotal(player);
const upperBonus = upperTotal >= 63 ? 50 : 0;
return upperBonus;
},
calculateLowerSectionTotal: (player: Player) => {
const lowerTotal = ScoringCategoryDescriptions
.filter((scd) => scd.section === "Lower")
.map((scd) => player.scoring[scd.category] ?? 0)
.reduce((total, previous) => (total ?? 0) + (previous ?? 0)) ?? 0;
return lowerTotal;
},
calculateTotal: (player: Player) => {
const upperTotal = ScoreCalculator.calculateUpperSectionTotal(player);
const upperBonus = ScoreCalculator.calculateUpperSectionBonus(player);
const lowerTotal = ScoreCalculator.calculateLowerSectionTotal(player);;
const finalTotal = upperTotal + upperBonus + lowerTotal;
return finalTotal;
},
calculators: {
"ones": (dice: number[]) => {
// Sum all dice showing 1
return sum(dice.filter((d) => d === 1));
},
"twos": (dice: number[]) => {
// Sum all dice showing 2
return sum(dice.filter((d) => d === 2));
},
"threes": (dice: number[]) => {
// Sum all dice showing 3
return sum(dice.filter((d) => d === 3));
},
"fours": (dice: number[]) => {
// Sum all dice showing 4
return sum(dice.filter((d) => d === 4));
},
"fives": (dice: number[]) => {
// Sum all dice showing 5
return sum(dice.filter((d) => d === 5));
},
"sixes": (dice: number[]) => {
// Sum all dice showing 6
return sum(dice.filter((d) => d === 6));
},
"onePair": (dice: number[]) => {
// Sum of two identical dice. As there may be two pairs, take the highest scoring pair.
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice[0].length >= 2) score = groupedDice[0][0] * 2;
if (groupedDice.length >= 2 &&
groupedDice[1].length >= 2 &&
groupedDice[1][0] > groupedDice[0][0]) score = groupedDice[1][0] * 2;
return score;
},
"twoPairs": (dice: number[]) => {
// Sum of two pair, otherwise 0
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice.length >= 2 &&
groupedDice[0].length >= 2 &&
groupedDice[1].length === 2) score = groupedDice[0][0] * 2 + groupedDice[1][0] * 2;
return score;
},
"threeOfAKind": (dice: number[]) => {
// Sum of 3 identical dice, otherwise 0
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice[0].length >= 3) score = groupedDice[0][0] * 3;
return score;
},
"fourOfAKind": (dice: number[]) => {
// Sum of 4 identical dice, otherwise 0
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice[0].length >= 4) score = groupedDice[0][0] * 4;
return score;
},
"smallStraight": (dice: number[]) => {
// Score 15 if small straight [1, 2, 3, 4, 5] is showing
const orderedDice = orderBy(dice);
let score = 0;
if (isEqual(orderedDice, [1, 2, 3, 4, 5])) score = 15;
return score;
},
"largeStraight": (dice: number[]) => {
// Score 20 if large straight [2, 3, 4, 5, 6] is showing
const orderedDice = orderBy(dice);
let score = 0;
if (isEqual(orderedDice, [2, 3, 4, 5, 6])) score = 20;
return score;
},
"fullHouse": (dice: number[]) => {
// Sum of all dice, if there is a separate three of a kind and one pair
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice.length === 2 &&
groupedDice[0].length === 3 &&
groupedDice[1].length === 2) score = sum(dice);
return score;
},
"chance": (dice: number[]) => {
// Sum all dice, regardless of value
return sum(dice);
},
"yatzy": (dice: number[]) => {
// Score 50 if all dice are the same, otherwise 0
let score = 0;
const groupedDice = groupDice(dice);
if (groupedDice[0].length === 5) score = 50;
return score;
},
},
};