658 lines
20 KiB
HTML
658 lines
20 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
<title>Wheel of Photons</title>
|
|
<style>
|
|
:root {
|
|
color-scheme: dark;
|
|
--panel: rgba(10, 16, 25, 0.92);
|
|
--line: rgba(255, 255, 255, 0.16);
|
|
--text: #f8f3ea;
|
|
--muted: #aeb8c8;
|
|
--cyan: #65d8e8;
|
|
--gold: #f2c96b;
|
|
--green: #88d76f;
|
|
--red: #ef735f;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html,
|
|
body {
|
|
min-height: 100%;
|
|
margin: 0;
|
|
}
|
|
|
|
body {
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
-webkit-tap-highlight-color: transparent;
|
|
background:
|
|
radial-gradient(circle at 16% 12%, rgba(101, 216, 232, 0.22), transparent 28%),
|
|
radial-gradient(circle at 86% 80%, rgba(242, 201, 107, 0.12), transparent 32%),
|
|
linear-gradient(140deg, #101926 0%, #1b242d 48%, #080b10 100%);
|
|
color: var(--text);
|
|
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
}
|
|
|
|
.shell {
|
|
min-height: 100vh;
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto;
|
|
gap: 14px;
|
|
padding: 16px;
|
|
}
|
|
|
|
header,
|
|
footer {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.brand {
|
|
display: grid;
|
|
grid-template-columns: auto 1fr;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.brand img {
|
|
width: clamp(48px, 8vw, 72px);
|
|
opacity: 0.58;
|
|
}
|
|
|
|
.brand small {
|
|
color: var(--cyan);
|
|
font-size: 0.78rem;
|
|
font-weight: 900;
|
|
letter-spacing: 0.08em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
h1 {
|
|
margin: 0;
|
|
font-size: clamp(1.45rem, 3.5vw, 2.5rem);
|
|
line-height: 1;
|
|
}
|
|
|
|
.stats {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
}
|
|
|
|
.stat,
|
|
.tip,
|
|
button,
|
|
.panel {
|
|
border: 1px solid var(--line);
|
|
border-radius: 8px;
|
|
background: var(--panel);
|
|
box-shadow: 0 16px 36px rgba(0, 0, 0, 0.28);
|
|
backdrop-filter: blur(12px);
|
|
}
|
|
|
|
.stat {
|
|
min-width: 108px;
|
|
padding: 8px 10px;
|
|
}
|
|
|
|
.stat span {
|
|
display: block;
|
|
color: var(--muted);
|
|
font-size: 0.7rem;
|
|
font-weight: 900;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.stat strong {
|
|
display: block;
|
|
margin-top: 2px;
|
|
font-size: 1.1rem;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
main {
|
|
min-height: 0;
|
|
display: grid;
|
|
grid-template-columns: minmax(260px, 0.82fr) minmax(340px, 1.18fr);
|
|
gap: 14px;
|
|
}
|
|
|
|
.wheel-panel {
|
|
position: relative;
|
|
display: grid;
|
|
place-items: center;
|
|
min-height: 420px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.wheel-panel::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 12%;
|
|
background: url("assets/txproducts-logo-watermark.png") center / contain no-repeat;
|
|
opacity: 0.1;
|
|
transform: rotate(-18deg);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.wheel {
|
|
position: relative;
|
|
width: min(72vmin, 420px);
|
|
aspect-ratio: 1;
|
|
border: 10px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 50%;
|
|
background:
|
|
conic-gradient(
|
|
from 0deg,
|
|
#f2c96b 0 30deg,
|
|
#65d8e8 30deg 60deg,
|
|
#88d76f 60deg 90deg,
|
|
#293544 90deg 120deg,
|
|
#f2c96b 120deg 150deg,
|
|
#ef735f 150deg 180deg,
|
|
#65d8e8 180deg 210deg,
|
|
#88d76f 210deg 240deg,
|
|
#293544 240deg 270deg,
|
|
#f2c96b 270deg 300deg,
|
|
#65d8e8 300deg 330deg,
|
|
#ef735f 330deg 360deg
|
|
);
|
|
box-shadow: 0 26px 80px rgba(0, 0, 0, 0.45), inset 0 0 60px rgba(0, 0, 0, 0.42);
|
|
transition: transform 1100ms cubic-bezier(0.16, 0.82, 0.28, 1);
|
|
}
|
|
|
|
.wheel::after {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 28%;
|
|
border: 1px solid rgba(255, 255, 255, 0.24);
|
|
border-radius: 50%;
|
|
background: rgba(6, 10, 15, 0.78);
|
|
}
|
|
|
|
.pointer {
|
|
position: absolute;
|
|
top: calc(50% - min(36vmin, 210px) - 16px);
|
|
left: 50%;
|
|
width: 0;
|
|
height: 0;
|
|
z-index: 3;
|
|
transform: translateX(-50%);
|
|
border-left: 16px solid transparent;
|
|
border-right: 16px solid transparent;
|
|
border-top: 26px solid var(--text);
|
|
filter: drop-shadow(0 5px 8px rgba(0, 0, 0, 0.35));
|
|
}
|
|
|
|
.spin-readout {
|
|
position: absolute;
|
|
z-index: 4;
|
|
display: grid;
|
|
place-items: center;
|
|
width: 45%;
|
|
aspect-ratio: 1;
|
|
border: 1px solid var(--line);
|
|
border-radius: 50%;
|
|
background: rgba(8, 13, 20, 0.86);
|
|
text-align: center;
|
|
}
|
|
|
|
.spin-readout span {
|
|
color: var(--muted);
|
|
font-size: 0.75rem;
|
|
font-weight: 900;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.spin-readout strong {
|
|
display: block;
|
|
margin-top: 4px;
|
|
color: var(--gold);
|
|
font-size: clamp(1.7rem, 5vw, 3rem);
|
|
line-height: 1;
|
|
}
|
|
|
|
.game-panel {
|
|
display: grid;
|
|
gap: 14px;
|
|
padding: 16px;
|
|
}
|
|
|
|
.category {
|
|
display: inline-flex;
|
|
width: fit-content;
|
|
border: 1px solid rgba(101, 216, 232, 0.34);
|
|
border-radius: 999px;
|
|
padding: 6px 10px;
|
|
color: var(--cyan);
|
|
font-size: 0.78rem;
|
|
font-weight: 900;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.phrase {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
align-content: center;
|
|
min-height: 180px;
|
|
}
|
|
|
|
.word {
|
|
display: inline-flex;
|
|
gap: 5px;
|
|
margin-right: 8px;
|
|
}
|
|
|
|
.tile {
|
|
display: grid;
|
|
place-items: center;
|
|
width: clamp(30px, 5vw, 52px);
|
|
height: clamp(38px, 6vw, 64px);
|
|
border: 1px solid rgba(255, 255, 255, 0.24);
|
|
border-radius: 8px;
|
|
background: rgba(255, 255, 255, 0.08);
|
|
color: var(--text);
|
|
font-size: clamp(1.15rem, 3vw, 2rem);
|
|
font-weight: 1000;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.tile.space {
|
|
width: 18px;
|
|
border: 0;
|
|
background: transparent;
|
|
}
|
|
|
|
.keyboard {
|
|
display: grid;
|
|
grid-template-columns: repeat(13, minmax(0, 1fr));
|
|
gap: 6px;
|
|
}
|
|
|
|
.difficulty {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(72px, 100px));
|
|
gap: 5px;
|
|
justify-content: start;
|
|
}
|
|
|
|
button {
|
|
min-height: 40px;
|
|
padding: 0 8px;
|
|
color: var(--text);
|
|
font: inherit;
|
|
font-weight: 900;
|
|
cursor: pointer;
|
|
touch-action: manipulation;
|
|
}
|
|
|
|
button.primary {
|
|
border-color: rgba(242, 201, 107, 0.62);
|
|
background: linear-gradient(180deg, #f3d16e, #d89a37);
|
|
color: #211609;
|
|
}
|
|
|
|
button.active-level {
|
|
color: #071014;
|
|
background: var(--green);
|
|
border-color: rgba(136, 215, 111, 0.85);
|
|
}
|
|
|
|
.letter.used {
|
|
opacity: 0.32;
|
|
}
|
|
|
|
.letter.good {
|
|
border-color: rgba(136, 215, 111, 0.62);
|
|
color: var(--green);
|
|
}
|
|
|
|
.letter.bad {
|
|
border-color: rgba(239, 115, 95, 0.62);
|
|
color: var(--red);
|
|
}
|
|
|
|
.actions,
|
|
.footer-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
|
|
.difficulty button {
|
|
min-height: 28px;
|
|
padding: 0 6px;
|
|
font-size: 0.76rem;
|
|
}
|
|
|
|
.actions button {
|
|
min-height: 38px;
|
|
font-size: 0.88rem;
|
|
}
|
|
|
|
.keyboard .letter {
|
|
min-height: 48px;
|
|
font-size: clamp(1rem, 1.8vw, 1.24rem);
|
|
}
|
|
|
|
.explain {
|
|
min-height: 54px;
|
|
color: var(--muted);
|
|
font-size: clamp(1rem, 1.7vw, 1.15rem);
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.tip {
|
|
padding: 8px 10px;
|
|
color: var(--muted);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
@media (max-width: 920px) {
|
|
body {
|
|
overflow: auto;
|
|
}
|
|
|
|
.shell {
|
|
min-height: 100vh;
|
|
height: auto;
|
|
padding: 10px;
|
|
}
|
|
|
|
header,
|
|
footer {
|
|
align-items: stretch;
|
|
flex-direction: column;
|
|
}
|
|
|
|
main {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.wheel-panel {
|
|
min-height: 330px;
|
|
}
|
|
|
|
.stats,
|
|
.footer-actions {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.keyboard {
|
|
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="shell">
|
|
<header>
|
|
<div class="brand">
|
|
<img src="assets/txproducts-logo-watermark.png" alt="">
|
|
<div>
|
|
<small>txproducts.de photon puzzle</small>
|
|
<h1>Wheel of Photons</h1>
|
|
</div>
|
|
</div>
|
|
<div class="stats">
|
|
<div class="stat"><span>Score</span><strong id="score">0</strong></div>
|
|
<div class="stat"><span>Beamtime</span><strong id="beamtime">6</strong></div>
|
|
<div class="stat"><span>Puzzle</span><strong id="round">1 / 6</strong></div>
|
|
</div>
|
|
</header>
|
|
|
|
<main>
|
|
<section class="panel wheel-panel">
|
|
<div class="pointer"></div>
|
|
<div class="wheel" id="wheel"></div>
|
|
<div class="spin-readout">
|
|
<div><span>Spin value</span><strong id="spinValue">Spin</strong></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel game-panel">
|
|
<div class="category" id="category">Category</div>
|
|
<div class="difficulty" aria-label="Difficulty">
|
|
<button class="active-level" data-level="Easy" type="button">Easy</button>
|
|
<button data-level="Medium" type="button">Medium</button>
|
|
<button data-level="Hard" type="button">Hard</button>
|
|
</div>
|
|
<div class="phrase" id="phrase" aria-label="Hidden phrase"></div>
|
|
<div class="keyboard" id="keyboard" aria-label="Letter keyboard"></div>
|
|
<div class="actions">
|
|
<button class="primary" id="spinButton" type="button">Spin</button>
|
|
<button id="solveButton" type="button">Solve / Reveal</button>
|
|
<button id="nextButton" type="button">Next phrase</button>
|
|
</div>
|
|
<div class="explain" id="explain">Spin, pick letters, then use Next phrase for another puzzle.</div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer>
|
|
<div class="tip">Passerby mode: one spin, one letter, quick reveal. Good for drawing people into the booth.</div>
|
|
<div class="footer-actions">
|
|
<button id="resetButton" type="button">Reset</button>
|
|
<button type="button" onclick="window.location.href='index.html'">Back to menu</button>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
const puzzleSets = {
|
|
Easy: [
|
|
{ category: "Easy Product", phrase: "WAVEGATE", explain: "WaveGate tailors synchrotron pulse timing." },
|
|
{ category: "Easy Product", phrase: "PICOSWITCH", explain: "PicoSwitch shortens x-ray pulses." },
|
|
{ category: "Easy Data", phrase: "TXDATA", explain: "TXData turns measurements into insight." },
|
|
{ category: "Easy Service", phrase: "TXSERVICES", explain: "TXServices supports better experiments." },
|
|
{ category: "Easy Physics", phrase: "XRAY PULSE", explain: "X-ray pulses are the timing target." },
|
|
{ category: "Easy Business", phrase: "BETTER BEAMTIME", explain: "Good support helps teams get more from beamtime." }
|
|
],
|
|
Medium: [
|
|
{ category: "Product", phrase: "WAVEGATE", explain: "WaveGate tailors synchrotron pulse structure from nanoseconds to seconds." },
|
|
{ category: "Product", phrase: "PICOSWITCH", explain: "PicoSwitch shortens x-ray pulses into the 5-10 ps range." },
|
|
{ category: "Physics", phrase: "BRAGG DIFFRACTION", explain: "Bragg diffraction links x-ray scattering to ordered crystal structure." },
|
|
{ category: "Timing", phrase: "TEMPORAL PULSE STRUCTURE", explain: "Timing structure describes when pulses arrive and which pulses are transmitted." },
|
|
{ category: "Beamline", phrase: "SYNCHROTRON RADIATION", explain: "Synchrotrons produce bright, tunable radiation for advanced experiments." },
|
|
{ category: "Experiment", phrase: "COHERENT LATTICE DYNAMICS", explain: "Short x-ray pulses help observe fast collective motion in crystals." },
|
|
{ category: "Business", phrase: "ACTIONABLE INSIGHT", explain: "TXData and TXServices can help convert complex measurements into useful decisions." },
|
|
{ category: "Service", phrase: "BETTER BEAMTIME", explain: "Good products, data, and support help teams get more value from scarce beamtime." }
|
|
],
|
|
Hard: [
|
|
{ category: "Hard Product", phrase: "PHOTO ACOUSTIC BRAGG SWITCH", explain: "PicoSwitch uses photo-acoustic Bragg switching to shorten x-ray pulses." },
|
|
{ category: "Hard Timing", phrase: "NANOSECONDS TO SECONDS", explain: "WaveGate covers broad temporal pulse tailoring." },
|
|
{ category: "Hard Experiment", phrase: "COHERENT LATTICE DYNAMICS", explain: "Short pulses can reveal ultrafast motion inside materials." },
|
|
{ category: "Hard Business", phrase: "MEASUREMENTS INTO INSIGHT", explain: "TXData and TXServices help convert complex measurements into decisions." },
|
|
{ category: "Hard Physics", phrase: "DIFFRACTION EFFICIENCY", explain: "Diffraction efficiency matters when choosing repetition rates and switching conditions." },
|
|
{ category: "Hard Beamline", phrase: "TEMPORAL SYNCHROTRON PULSE STRUCTURE", explain: "Temporal structure controls which pulses reach the experiment." }
|
|
]
|
|
};
|
|
let puzzles = puzzleSets.Easy;
|
|
|
|
const wheelValues = [100, 150, 200, 250, 300, 350, 400, 500, 600, 750, "BANKRUPT", "PASS"];
|
|
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
|
|
const state = {
|
|
index: 0,
|
|
level: "Easy",
|
|
score: 0,
|
|
beamtime: 6,
|
|
currentValue: 0,
|
|
rotation: 0,
|
|
guessed: new Map(),
|
|
solved: false
|
|
};
|
|
|
|
const scoreEl = document.querySelector("#score");
|
|
const beamtimeEl = document.querySelector("#beamtime");
|
|
const roundEl = document.querySelector("#round");
|
|
const spinValueEl = document.querySelector("#spinValue");
|
|
const wheel = document.querySelector("#wheel");
|
|
const categoryEl = document.querySelector("#category");
|
|
const phraseEl = document.querySelector("#phrase");
|
|
const keyboardEl = document.querySelector("#keyboard");
|
|
const explainEl = document.querySelector("#explain");
|
|
const spinButton = document.querySelector("#spinButton");
|
|
const solveButton = document.querySelector("#solveButton");
|
|
const nextButton = document.querySelector("#nextButton");
|
|
const resetButton = document.querySelector("#resetButton");
|
|
const levelButtons = document.querySelectorAll("[data-level]");
|
|
|
|
function render() {
|
|
const puzzle = puzzles[state.index];
|
|
categoryEl.textContent = `${state.level} / ${puzzle.category}`;
|
|
scoreEl.textContent = state.score;
|
|
beamtimeEl.textContent = state.beamtime;
|
|
roundEl.textContent = `${state.index + 1} / ${puzzles.length}`;
|
|
spinValueEl.textContent = state.currentValue || "Spin";
|
|
renderPhrase(puzzle.phrase);
|
|
renderKeyboard(puzzle.phrase);
|
|
}
|
|
|
|
function renderPhrase(phrase) {
|
|
phraseEl.innerHTML = "";
|
|
for (const word of phrase.split(" ")) {
|
|
const wordEl = document.createElement("span");
|
|
wordEl.className = "word";
|
|
for (const char of word) {
|
|
const tile = document.createElement("span");
|
|
tile.className = "tile";
|
|
tile.textContent = state.solved || state.guessed.get(char) ? char : "";
|
|
wordEl.appendChild(tile);
|
|
}
|
|
phraseEl.appendChild(wordEl);
|
|
}
|
|
}
|
|
|
|
function renderKeyboard(phrase) {
|
|
keyboardEl.innerHTML = "";
|
|
for (const letter of letters) {
|
|
const button = document.createElement("button");
|
|
const used = state.guessed.has(letter);
|
|
const good = used && phrase.includes(letter);
|
|
const bad = used && !phrase.includes(letter);
|
|
button.type = "button";
|
|
button.className = `letter${used ? " used" : ""}${good ? " good" : ""}${bad ? " bad" : ""}`;
|
|
button.textContent = letter;
|
|
button.disabled = used || state.solved || !state.currentValue || state.currentValue === "BANKRUPT" || state.currentValue === "PASS";
|
|
button.addEventListener("click", () => guessLetter(letter));
|
|
button.addEventListener("touchend", (event) => {
|
|
event.preventDefault();
|
|
guessLetter(letter);
|
|
}, { passive: false });
|
|
keyboardEl.appendChild(button);
|
|
}
|
|
}
|
|
|
|
function spin() {
|
|
if (state.solved) return;
|
|
const value = wheelValues[Math.floor(Math.random() * wheelValues.length)];
|
|
state.currentValue = value;
|
|
state.rotation += 720 + Math.floor(Math.random() * 720);
|
|
wheel.style.transform = `rotate(${state.rotation}deg)`;
|
|
if (value === "BANKRUPT") {
|
|
state.score = Math.max(0, Math.floor(state.score * 0.5));
|
|
state.beamtime = Math.max(0, state.beamtime - 1);
|
|
explainEl.textContent = "Beamtime penalty: half the score is lost. Spin again.";
|
|
} else if (value === "PASS") {
|
|
state.beamtime = Math.max(0, state.beamtime - 1);
|
|
explainEl.textContent = "No useful pulse this turn. Spin again.";
|
|
} else {
|
|
explainEl.textContent = `Spin value ${value}. Pick a consonant or vowel from the board.`;
|
|
}
|
|
render();
|
|
checkBeamtime();
|
|
}
|
|
|
|
function guessLetter(letter) {
|
|
const puzzle = puzzles[state.index];
|
|
if (state.guessed.has(letter) || state.solved || typeof state.currentValue !== "number") return;
|
|
const count = puzzle.phrase.split("").filter((char) => char === letter).length;
|
|
state.guessed.set(letter, true);
|
|
if (count > 0) {
|
|
state.score += count * state.currentValue;
|
|
explainEl.textContent = `${letter} appears ${count} time${count === 1 ? "" : "s"}.`;
|
|
} else {
|
|
state.beamtime = Math.max(0, state.beamtime - 1);
|
|
explainEl.textContent = `${letter} is not in the phrase. Beamtime drops by one.`;
|
|
}
|
|
state.currentValue = 0;
|
|
if (isSolved(puzzle.phrase)) solve(true);
|
|
render();
|
|
checkBeamtime();
|
|
}
|
|
|
|
function isSolved(phrase) {
|
|
return phrase.split("").every((char) => char === " " || state.guessed.has(char));
|
|
}
|
|
|
|
function solve(auto = false) {
|
|
const puzzle = puzzles[state.index];
|
|
state.solved = true;
|
|
explainEl.textContent = auto ? `${puzzle.phrase}. ${puzzle.explain}` : `${puzzle.phrase}: ${puzzle.explain}`;
|
|
render();
|
|
}
|
|
|
|
function nextPuzzle() {
|
|
state.index = (state.index + 1) % puzzles.length;
|
|
state.beamtime = 6;
|
|
state.currentValue = 0;
|
|
state.guessed = new Map();
|
|
state.solved = false;
|
|
explainEl.textContent = "Spin, pick letters, then use Next phrase for another puzzle.";
|
|
render();
|
|
}
|
|
|
|
function resetGame() {
|
|
state.index = 0;
|
|
state.score = 0;
|
|
state.beamtime = 6;
|
|
state.currentValue = 0;
|
|
state.rotation = 0;
|
|
state.guessed = new Map();
|
|
state.solved = false;
|
|
explainEl.textContent = "Spin, pick letters, then use Next phrase for another puzzle.";
|
|
wheel.style.transform = "rotate(0deg)";
|
|
render();
|
|
}
|
|
|
|
function setLevel(level) {
|
|
state.level = level;
|
|
puzzles = puzzleSets[level];
|
|
levelButtons.forEach((button) => button.classList.toggle("active-level", button.dataset.level === level));
|
|
resetGame();
|
|
}
|
|
|
|
function checkBeamtime() {
|
|
if (state.beamtime <= 0 && !state.solved) {
|
|
solve(false);
|
|
explainEl.textContent = `Beamtime expired. ${puzzles[state.index].phrase}: ${puzzles[state.index].explain}`;
|
|
}
|
|
}
|
|
|
|
spinButton.addEventListener("click", spin);
|
|
solveButton.addEventListener("click", () => solve(false));
|
|
nextButton.addEventListener("click", nextPuzzle);
|
|
resetButton.addEventListener("click", resetGame);
|
|
levelButtons.forEach((button) => button.addEventListener("click", () => setLevel(button.dataset.level)));
|
|
window.addEventListener("keydown", (event) => {
|
|
const letter = event.key.toUpperCase();
|
|
if (letters.includes(letter)) guessLetter(letter);
|
|
if (event.key === " ") {
|
|
event.preventDefault();
|
|
spin();
|
|
}
|
|
if (event.key.toLowerCase() === "r") resetGame();
|
|
});
|
|
|
|
render();
|
|
</script>
|
|
</body>
|
|
</html>
|