101 Logo
onenoughtone

Code Implementation

Dice Roll Simulation Implementation

Below is the implementation of the dice roll simulation:

solution.js
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/**
* Calculate the number of distinct sequences that can be obtained with exact n rolls.
* Dynamic Programming (3D) approach.
*
* @param {number} n - Number of rolls
* @param {number[]} rollMax - Maximum consecutive occurrences for each face value
* @return {number} - Number of distinct sequences
*/
function dieSimulator(n, rollMax) {
const MOD = 1000000007;
const FACES = 6;
// Initialize DP array
// dp[i][j][k] = number of sequences of length i ending with k consecutive occurrences of face j
const dp = Array(n + 1).fill().map(() =>
Array(FACES + 1).fill().map(() =>
Array(16).fill(0)
)
);
// Base cases: for the first roll, there is one way to roll each face
for (let j = 1; j <= FACES; j++) {
dp[1][j][1] = 1;
}
// Fill DP array
for (let i = 2; i <= n; i++) {
for (let j = 1; j <= FACES; j++) {
// Case 1: Starting a new consecutive sequence (k = 1)
// We can come from any face j' != j with any valid consecutive count k'
for (let j_prev = 1; j_prev <= FACES; j_prev++) {
if (j_prev !== j) {
for (let k_prev = 1; k_prev <= rollMax[j_prev - 1]; k_prev++) {
dp[i][j][1] = (dp[i][j][1] + dp[i - 1][j_prev][k_prev]) % MOD;
}
}
}
// Case 2: Extending a consecutive sequence (k > 1)
// We can only come from the same face j with consecutive count k-1
for (let k = 2; k <= rollMax[j - 1]; k++) {
dp[i][j][k] = dp[i - 1][j][k - 1];
}
}
}
// Calculate the final answer: sum of all valid sequences of length n
let result = 0;
for (let j = 1; j <= FACES; j++) {
for (let k = 1; k <= rollMax[j - 1]; k++) {
result = (result + dp[n][j][k]) % MOD;
}
}
return result;
}
/**
* Calculate the number of distinct sequences that can be obtained with exact n rolls.
* Dynamic Programming (2D) approach.
*
* @param {number} n - Number of rolls
* @param {number[]} rollMax - Maximum consecutive occurrences for each face value
* @return {number} - Number of distinct sequences
*/
function dieSimulator2D(n, rollMax) {
const MOD = 1000000007;
const FACES = 6;
// Initialize DP arrays
// dp[i][j] = number of sequences of length i ending with face j
const dp = Array(n + 1).fill().map(() => Array(FACES + 1).fill(0));
// consecutive[i][j][k] = number of sequences of length i ending with k consecutive occurrences of face j
const consecutive = Array(n + 1).fill().map(() =>
Array(FACES + 1).fill().map(() =>
Array(16).fill(0)
)
);
// Base cases: for the first roll, there is one way to roll each face
for (let j = 1; j <= FACES; j++) {
dp[1][j] = 1;
consecutive[1][j][1] = 1;
}
// Fill DP arrays
for (let i = 2; i <= n; i++) {
for (let j = 1; j <= FACES; j++) {
// Calculate dp[i][j]
for (let j_prev = 1; j_prev <= FACES; j_prev++) {
if (j_prev !== j) {
// Add sequences ending with a different face
dp[i][j] = (dp[i][j] + dp[i - 1][j_prev]) % MOD;
} else {
// Add sequences ending with the same face, but not exceeding rollMax
for (let k = 1; k < rollMax[j - 1]; k++) {
dp[i][j] = (dp[i][j] + consecutive[i - 1][j][k]) % MOD;
}
}
}
// Update consecutive array
// consecutive[i][j][1] = sequences that switch to face j
consecutive[i][j][1] = 0;
for (let j_prev = 1; j_prev <= FACES; j_prev++) {
if (j_prev !== j) {
consecutive[i][j][1] = (consecutive[i][j][1] + dp[i - 1][j_prev]) % MOD;
}
}
// consecutive[i][j][k] for k > 1 = sequences that extend consecutive occurrences of face j
for (let k = 2; k <= rollMax[j - 1]; k++) {
consecutive[i][j][k] = consecutive[i - 1][j][k - 1];
}
}
}
// Calculate the final answer: sum of all valid sequences of length n
let result = 0;
for (let j = 1; j <= FACES; j++) {
result = (result + dp[n][j]) % MOD;
}
return result;
}
/**
* Calculate the number of distinct sequences that can be obtained with exact n rolls.
* Dynamic Programming with space optimization.
*
* @param {number} n - Number of rolls
* @param {number[]} rollMax - Maximum consecutive occurrences for each face value
* @return {number} - Number of distinct sequences
*/
function dieSimulatorOptimized(n, rollMax) {
const MOD = 1000000007;
const FACES = 6;
// Initialize DP arrays for current and previous rolls
let dp = Array(FACES + 1).fill(0);
let consecutive = Array(FACES + 1).fill().map(() => Array(16).fill(0));
// Base cases: for the first roll, there is one way to roll each face
for (let j = 1; j <= FACES; j++) {
dp[j] = 1;
consecutive[j][1] = 1;
}
// Fill DP arrays
for (let i = 2; i <= n; i++) {
// Save the previous DP arrays
const prevDp = [...dp];
const prevConsecutive = consecutive.map(row => [...row]);
// Reset current DP arrays
dp = Array(FACES + 1).fill(0);
consecutive = Array(FACES + 1).fill().map(() => Array(16).fill(0));
for (let j = 1; j <= FACES; j++) {
// Calculate dp[j]
for (let j_prev = 1; j_prev <= FACES; j_prev++) {
if (j_prev !== j) {
// Add sequences ending with a different face
dp[j] = (dp[j] + prevDp[j_prev]) % MOD;
} else {
// Add sequences ending with the same face, but not exceeding rollMax
for (let k = 1; k < rollMax[j - 1]; k++) {
dp[j] = (dp[j] + prevConsecutive[j][k]) % MOD;
}
}
}
// Update consecutive array
// consecutive[j][1] = sequences that switch to face j
for (let j_prev = 1; j_prev <= FACES; j_prev++) {
if (j_prev !== j) {
consecutive[j][1] = (consecutive[j][1] + prevDp[j_prev]) % MOD;
}
}
// consecutive[j][k] for k > 1 = sequences that extend consecutive occurrences of face j
for (let k = 2; k <= rollMax[j - 1]; k++) {
consecutive[j][k] = prevConsecutive[j][k - 1];
}
}
}
// Calculate the final answer: sum of all valid sequences of length n
let result = 0;
for (let j = 1; j <= FACES; j++) {
result = (result + dp[j]) % MOD;
}
return result;
}
// Test cases
console.log(dieSimulator(2, [1, 1, 2, 2, 2, 3])); // 34
console.log(dieSimulator(2, [1, 1, 1, 1, 1, 1])); // 30
console.log(dieSimulator(3, [1, 1, 1, 2, 2, 3])); // 181
console.log(dieSimulator2D(2, [1, 1, 2, 2, 2, 3])); // 34
console.log(dieSimulator2D(2, [1, 1, 1, 1, 1, 1])); // 30
console.log(dieSimulator2D(3, [1, 1, 1, 2, 2, 3])); // 181
console.log(dieSimulatorOptimized(2, [1, 1, 2, 2, 2, 3])); // 34
console.log(dieSimulatorOptimized(2, [1, 1, 1, 1, 1, 1])); // 30
console.log(dieSimulatorOptimized(3, [1, 1, 1, 2, 2, 3])); // 181

Step-by-Step Explanation

Let's break down the implementation:

  1. Define the States: Define the states for the dynamic programming approach, considering the current roll number, face value, and consecutive occurrences.
  2. Establish Base Cases: Set up the base cases for the first roll, where there is one way to roll each face value.
  3. Derive Recurrence Relations: Derive the recurrence relations by considering all possible previous states that can lead to the current state.
  4. Fill DP Arrays: Use dynamic programming to fill the arrays from the base cases up to the target number of rolls.
  5. Calculate Final Answer: Sum up all valid sequences of the target length to get the final answer, applying the modulo operation to avoid integer overflow.
ProblemSolutionCode
101 Logo
onenoughtone