Skip to content

Commit ab28216

Browse files
committed
enhance coin-flip.html with improved layout and progress tracking for trials
1 parent 81b8c41 commit ab28216

File tree

1 file changed

+138
-23
lines changed

1 file changed

+138
-23
lines changed

resources/coin-flip.html

Lines changed: 138 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,31 @@
77
.plots-container {
88
display: flex;
99
justify-content: space-between;
10-
gap: 0px;
10+
gap: 20px;
1111
margin-top: 20px;
1212
}
1313

1414
.plot-column {
1515
flex: 1;
1616
}
17+
18+
.controls-row {
19+
margin-bottom: 10px;
20+
}
21+
22+
.progress-container {
23+
width: 100%;
24+
margin-top: 20px;
25+
display: none;
26+
}
27+
28+
.progress-bar {
29+
width: 0%;
30+
height: 20px;
31+
background-color: #4CAF50;
32+
transition: width 0.1s ease-in-out;
33+
border-radius: 4px;
34+
}
1735
</style>
1836
</head>
1937

@@ -27,36 +45,49 @@
2745
<div id="histogram"></div>
2846
</div>
2947
</div>
48+
<div class="progress-container">
49+
<div class="progress-bar"></div>
50+
</div>
3051

3152
<script type="module">
32-
// Initialize state
53+
// State initialization
3354
const state = {
34-
flips: { heads: 0, tails: 0 },
35-
histogram: Array(11).fill(0),
55+
flips: { heads: 0, tails: 0 }, // Only current trial
56+
histogram: Array(11).fill(0), // Counts of heads (0-10) across trials
3657
isPlaying: false,
3758
playInterval: null,
38-
probability: 0.5
59+
probability: 0.5,
60+
totalTrials: 0,
61+
maxTrials: 1000
3962
};
4063

4164
// Create UI controls
4265
const controlsDiv = document.getElementById('controls');
4366
controlsDiv.innerHTML = `
44-
<div id="probabilityControls">
45-
<label for="probabilitySelect">Select Probability: </label>
46-
<select id="probabilitySelect">
47-
${[...Array(9).keys()].map(i => {
67+
<div class="controls-row">
68+
<div id="probabilityControls">
69+
<label for="probabilitySelect">Select Probability: </label>
70+
<select id="probabilitySelect">
71+
${[...Array(9).keys()].map(i => {
4872
const prob = (i + 1) / 10;
4973
return `<option value="${prob}" ${prob === 0.5 ? 'selected' : ''}>${prob}</option>`;
5074
}).join('')}
51-
</select>
75+
</select>
76+
</div>
77+
</div>
78+
<div class="controls-row">
79+
<button id="flipButton">Flip once</button>
80+
<button id="playButton">Play</button>
81+
<button id="resetButton">Reset</button>
82+
<label>
83+
<input type="checkbox" id="multiFlipToggle"> Multi-flip
84+
</label>
85+
<span id="flipCount">Total flips: 0/10</span>
86+
<span id="trialCount">Trials: 0/1000</span>
87+
</div>
88+
<div class="controls-row">
89+
<button id="completeButton">Complete 1000 trials</button>
5290
</div>
53-
<button id="flipButton">Flip once</button>
54-
<button id="playButton">Play</button>
55-
<button id="resetButton">Reset</button>
56-
<label>
57-
<input type="checkbox" id="multiFlipToggle"> Multi-flip
58-
</label>
59-
<span id="flipCount">Total flips: 0/10</span>
6091
`;
6192

6293
// Add event listeners
@@ -66,6 +97,10 @@
6697
const multiFlipToggle = document.getElementById('multiFlipToggle');
6798
const flipCountSpan = document.getElementById('flipCount');
6899
const probabilitySelect = document.getElementById('probabilitySelect');
100+
const completeButton = document.getElementById('completeButton');
101+
const trialCountSpan = document.getElementById('trialCount');
102+
const progressContainer = document.querySelector('.progress-container');
103+
const progressBar = document.querySelector('.progress-bar');
69104

70105
probabilitySelect.addEventListener('change', (event) => {
71106
const newProbability = parseFloat(event.target.value);
@@ -120,9 +155,17 @@
120155
state.isPlaying = false;
121156
state.flips = { heads: 0, tails: 0 };
122157
state.histogram = Array(11).fill(0);
158+
state.totalTrials = 0;
159+
flipButton.disabled = false;
160+
playButton.disabled = false;
161+
completeButton.disabled = false;
162+
playButton.textContent = 'Play';
123163
updateHistogram();
164+
updateCurrentRound();
165+
updateTrialCount();
124166
flipCountSpan.textContent = 'Total flips: 0/10';
125-
playButton.textContent = 'Play';
167+
progressContainer.style.display = 'none';
168+
progressBar.style.width = '0%';
126169
});
127170

128171
multiFlipToggle.addEventListener('change', () => {
@@ -134,6 +177,10 @@
134177
}
135178
});
136179

180+
completeButton.addEventListener('click', () => {
181+
completeTo1000();
182+
});
183+
137184
// Create initial plots
138185
const currentRoundTrace = {
139186
x: ['Heads', 'Tails'],
@@ -161,7 +208,11 @@
161208
}
162209
};
163210

164-
Plotly.newPlot('currentRound', [currentRoundTrace], currentRoundLayout);
211+
const plotConfig = {
212+
displayModeBar: false
213+
};
214+
215+
Plotly.newPlot('currentRound', [currentRoundTrace], currentRoundLayout, plotConfig);
165216

166217
const histogramTrace = {
167218
x: Array.from({ length: 11 }, (_, i) => i),
@@ -172,6 +223,7 @@
172223
}
173224
};
174225

226+
// Update initial histogram layout
175227
const histogramLayout = {
176228
title: 'Distribution of Heads (Across Completed Rounds)',
177229
xaxis: {
@@ -194,7 +246,7 @@
194246
}
195247
};
196248

197-
Plotly.newPlot('histogram', [histogramTrace], histogramLayout);
249+
Plotly.newPlot('histogram', [histogramTrace], histogramLayout, plotConfig);
198250

199251
// Helper function for single flip
200252
function singleFlip() {
@@ -209,17 +261,67 @@
209261
}
210262
}
211263

212-
// Helper function for multi flip
264+
// Optimized multiFlip function
213265
function multiFlip() {
214266
let newHeads = 0;
267+
// Single trial of 10 flips
215268
for (let i = 0; i < 10; i++) {
216269
if (Math.random() < state.probability) newHeads++;
217270
}
218-
state.flips = { heads: newHeads, tails: 10 - newHeads };
271+
// Update histogram directly
219272
state.histogram[newHeads]++;
273+
state.totalTrials++;
274+
275+
// Update display for current trial
276+
state.flips = { heads: newHeads, tails: 10 - newHeads };
220277
updateCurrentRound();
221278
updateHistogram();
222279
state.flips = { heads: 0, tails: 0 };
280+
281+
// Check if max trials reached
282+
if (state.totalTrials >= state.maxTrials) {
283+
disableButtons();
284+
}
285+
}
286+
287+
// Optimized completeTo1000 function
288+
function completeTo1000() {
289+
const startTrials = state.totalTrials;
290+
const remaining = state.maxTrials - startTrials;
291+
progressContainer.style.display = 'block';
292+
293+
function step(timestamp) {
294+
const batchSize = 50; // Increased batch size
295+
for (let i = 0; i < batchSize && state.totalTrials < state.maxTrials; i++) {
296+
let newHeads = 0;
297+
for (let j = 0; j < 10; j++) {
298+
if (Math.random() < state.probability) newHeads++;
299+
}
300+
state.histogram[newHeads]++;
301+
state.totalTrials++;
302+
}
303+
304+
const progress = ((state.totalTrials - startTrials) / remaining) * 100;
305+
progressBar.style.width = `${progress}%`;
306+
updateHistogram();
307+
updateTrialCount();
308+
309+
if (state.totalTrials < state.maxTrials) {
310+
requestAnimationFrame(step);
311+
} else {
312+
progressContainer.style.display = 'none';
313+
disableButtons();
314+
}
315+
}
316+
317+
requestAnimationFrame(step);
318+
}
319+
320+
// Add button state management
321+
function disableButtons() {
322+
flipButton.disabled = true;
323+
playButton.disabled = true;
324+
completeButton.disabled = true;
223325
}
224326

225327
// Update functions
@@ -234,11 +336,24 @@
234336
}
235337
}
236338

339+
// Update the updateHistogram function
237340
function updateHistogram() {
341+
const maxY = Math.max(...state.histogram);
238342
const update = {
239343
y: [state.histogram]
240344
};
241-
Plotly.update('histogram', update);
345+
const layout = {
346+
'yaxis.dtick': maxY > 10 ? 10 : 1
347+
};
348+
Plotly.update('histogram', update, layout);
349+
}
350+
351+
function updateTrialCount() {
352+
trialCountSpan.textContent = `Trials: ${state.totalTrials}/${state.maxTrials}`;
353+
}
354+
355+
function updateProgress(progress) {
356+
progressBar.style.width = `${progress}%`;
242357
}
243358
</script>
244359
</body>

0 commit comments

Comments
 (0)