diff --git a/content/contribution/RockPaperScissors/Script.js b/content/contribution/RockPaperScissors/Script.js new file mode 100644 index 0000000..790b181 --- /dev/null +++ b/content/contribution/RockPaperScissors/Script.js @@ -0,0 +1,80 @@ +const emojiFor = { rock: "🪨", paper: "📄", scissors: "✂️" }; + +const choiceButtons = document.querySelectorAll(".choice"); + +const playerChoiceEl = document.querySelector("#player-choice"); +const computerChoiceEl = document.querySelector("#computer-choice"); +const playerGlyphEl = document.querySelector("#player-glyph"); +const computerGlyphEl = document.querySelector("#computer-glyph"); +const resultMessageEl = document.querySelector("#result-message"); + +const playerScoreEl = document.querySelector("#player-score"); +const computerScoreEl = document.querySelector("#computer-score"); + +const resetButton = document.querySelector("#reset-button"); + +let playerScore = 0; +let computerScore = 0; + +const options = ["rock", "paper", "scissors"]; + +function getComputerChoice() { + const randomIndex = Math.floor(Math.random() * options.length); + return options[randomIndex]; +} + +function getWinner(playerChoice, computerChoice) { + if (playerChoice === computerChoice) return "draw"; + const playerWinsAgainst = { rock: "scissors", paper: "rock", scissors: "paper" }; + return playerWinsAgainst[playerChoice] === computerChoice ? "player" : "computer"; +} + +function playGame(playerChoice) { + choiceButtons.forEach((b) => b.classList.toggle("selected", b.dataset.choice === playerChoice)); + + const computerChoice = getComputerChoice(); + const winner = getWinner(playerChoice, computerChoice); + + playerGlyphEl.textContent = emojiFor[playerChoice]; + computerGlyphEl.textContent = emojiFor[computerChoice]; + playerChoiceEl.textContent = playerChoice; + computerChoiceEl.textContent = computerChoice; + + resultMessageEl.classList.remove("win", "lose", "draw"); + + if (winner === "player") { + playerScore++; + resultMessageEl.textContent = "You win!"; + resultMessageEl.classList.add("win"); + } else if (winner === "computer") { + computerScore++; + resultMessageEl.textContent = "Computer wins!"; + resultMessageEl.classList.add("lose"); + } else { + resultMessageEl.textContent = "It's a draw!"; + resultMessageEl.classList.add("draw"); + } + + playerScoreEl.textContent = playerScore; + computerScoreEl.textContent = computerScore; +} + +choiceButtons.forEach((choice) => { + choice.addEventListener("click", () => playGame(choice.dataset.choice)); +}); + +resetButton.addEventListener("click", () => { + playerScore = 0; + computerScore = 0; + + choiceButtons.forEach((b) => b.classList.remove("selected")); + playerGlyphEl.textContent = "–"; + computerGlyphEl.textContent = "–"; + playerChoiceEl.textContent = "—"; + computerChoiceEl.textContent = "—"; + resultMessageEl.textContent = "Make your choice!"; + resultMessageEl.classList.remove("win", "lose", "draw"); + + playerScoreEl.textContent = "0"; + computerScoreEl.textContent = "0"; +}); \ No newline at end of file diff --git a/content/contribution/RockPaperScissors/index.html b/content/contribution/RockPaperScissors/index.html new file mode 100644 index 0000000..434c139 --- /dev/null +++ b/content/contribution/RockPaperScissors/index.html @@ -0,0 +1,72 @@ + + + + + + Rock Paper Scissors + + + + + + + +
+
+
Game Night
+

Rock, Paper, Scissors

+

Pick your move — first to feel lucky wins + the round.

+
+ +
+ + + +
+ +
+
+
+
YOU
+
–
+
—
+
+
vs
+
+
COMPUTER
+
–
+
—
+
+
+

Make your choice!

+
+ +
+
+
PLAYER
+
0
+
+
+
COMPUTER
+
0
+
+
+ +
+ +
+
+ + + + \ No newline at end of file diff --git a/content/contribution/RockPaperScissors/style.css b/content/contribution/RockPaperScissors/style.css new file mode 100644 index 0000000..001b09c --- /dev/null +++ b/content/contribution/RockPaperScissors/style.css @@ -0,0 +1,278 @@ +:root{ + --felt-900:#0f2e23; + --felt-800:#153d2e; + --felt-700:#1c4d3a; + --cream:#f4ecd8; + --gold:#d4a24e; + --gold-bright:#e6bd6f; + --brick:#c1483e; + --brick-dark:#8f3129; + --ink:#0c1f18; + --line: rgba(244,236,216,0.14); + --font-display: "Fraunces", Georgia, serif; + --font-ui: "Space Grotesk", -apple-system, sans-serif; + } + :root{ + --bg-outer: radial-gradient(120% 140% at 50% -10%, var(--felt-700) 0%, var(--felt-900) 60%, #081a13 100%); + --panel: rgba(244,236,216,0.05); + --panel-border: rgba(244,236,216,0.12); + --text-main: var(--cream); + --text-dim: rgba(244,236,216,0.6); + } + + *{ margin:0; padding:0; box-sizing:border-box; } + + body{ + min-height:100vh; + background: var(--bg-outer); + font-family: var(--font-ui); + color: var(--text-main); + display:flex; + align-items:center; + justify-content:center; + padding: 32px 18px; + } + + .table{ + width:100%; + max-width: 620px; + position:relative; + } + + .table::before{ + content:""; + position:absolute; + inset:-40px; + background-image: radial-gradient(rgba(244,236,216,0.035) 1px, transparent 1px); + background-size: 14px 14px; + pointer-events:none; + z-index:0; + border-radius: 40px; + } + + header{ + position:relative; + z-index:1; + text-align:center; + margin-bottom: 34px; + } + + .kicker{ + font-family: var(--font-ui); + font-size: 13px; + letter-spacing: 0.14em; + color: var(--gold-bright); + font-weight: 600; + margin-bottom: 10px; + } + + h1{ + font-family: var(--font-display); + font-weight: 600; + font-optical-sizing: auto; + font-size: clamp(2.4rem, 7vw, 3.4rem); + line-height: 1.05; + letter-spacing: -0.01em; + } + + .instruction{ + margin-top: 12px; + color: var(--text-dim); + font-size: 16px; + } + + .choices{ + position:relative; + z-index:1; + display:flex; + justify-content:center; + gap: 16px; + margin-bottom: 30px; + flex-wrap: wrap; + } + + .choice{ + --chip: var(--cream); + width: 108px; + height: 108px; + border-radius: 50%; + border: none; + cursor:pointer; + background: + radial-gradient(circle at 35% 30%, #fffdf6 0%, var(--chip) 55%, #e7dcc0 100%); + box-shadow: + 0 6px 0 rgba(0,0,0,0.28), + 0 10px 18px rgba(0,0,0,0.35), + inset 0 0 0 3px rgba(12,31,24,0.08); + display:flex; + flex-direction:column; + align-items:center; + justify-content:center; + gap: 4px; + font-family: var(--font-ui); + font-weight: 600; + font-size: 13px; + color: var(--ink); + transition: transform 120ms ease, box-shadow 120ms ease; + } + .choice .emoji{ font-size: 34px; line-height:1; } + + .choice:hover{ + transform: translateY(-3px); + box-shadow: + 0 9px 0 rgba(0,0,0,0.28), + 0 14px 22px rgba(0,0,0,0.4), + inset 0 0 0 3px rgba(12,31,24,0.08); + } + .choice:active{ + transform: translateY(2px); + box-shadow: + 0 3px 0 rgba(0,0,0,0.28), + 0 5px 10px rgba(0,0,0,0.35), + inset 0 0 0 3px rgba(12,31,24,0.08); + } + .choice:focus-visible{ + outline: 3px solid var(--gold-bright); + outline-offset: 4px; + } + + .choice.selected{ + --chip: var(--gold-bright); + } + + .stage{ + position:relative; + z-index:1; + background: var(--panel); + border: 1px solid var(--panel-border); + border-radius: 18px; + padding: 26px 24px; + margin-bottom: 26px; + } + + .versus{ + display:flex; + align-items:center; + justify-content:center; + gap: 18px; + margin-bottom: 16px; + } + + .side{ + flex:1; + text-align:center; + } + .side .label{ + font-size:12px; + letter-spacing:0.1em; + color: var(--text-dim); + margin-bottom: 8px; + } + .side .glyph{ + font-size: 44px; + line-height:1; + height: 52px; + display:flex; + align-items:center; + justify-content:center; + } + .side .name{ + margin-top:6px; + font-size: 14px; + text-transform: capitalize; + color: var(--text-main); + min-height: 18px; + } + + .vs-mark{ + font-family: var(--font-display); + font-style: italic; + font-size: 18px; + color: var(--gold-bright); + opacity:0.85; + } + + #result-message{ + text-align:center; + font-family: var(--font-display); + font-weight: 600; + font-size: clamp(1.3rem, 4vw, 1.7rem); + padding-top: 14px; + border-top: 1px solid var(--line); + } + #result-message.win{ color: var(--gold-bright); } + #result-message.lose{ color: #e0897f; } + #result-message.draw{ color: var(--text-dim); } + + .scoreboard{ + position:relative; + z-index:1; + display:flex; + align-items:center; + justify-content:center; + gap: 0; + margin-bottom: 28px; + border-radius: 14px; + overflow:hidden; + border: 1px solid var(--panel-border); + } + .score-block{ + flex:1; + padding: 16px 10px; + text-align:center; + background: var(--panel); + } + .score-block + .score-block{ + border-left: 1px solid var(--panel-border); + } + .score-block .tally-label{ + font-size: 11px; + letter-spacing: 0.12em; + color: var(--text-dim); + margin-bottom: 6px; + } + .score-block .tally-num{ + font-family: var(--font-display); + font-size: 30px; + font-weight: 700; + color: var(--gold-bright); + } + + .actions{ + position:relative; + z-index:1; + display:flex; + justify-content:center; + } + + #reset-button{ + padding: 12px 28px; + border-radius: 999px; + border: 1px solid var(--panel-border); + background: transparent; + color: var(--text-main); + font-family: var(--font-ui); + font-weight: 600; + font-size: 14px; + cursor:pointer; + transition: background 120ms ease, border-color 120ms ease, transform 120ms ease; + } + #reset-button:hover{ + background: rgba(244,236,216,0.08); + border-color: var(--gold-bright); + } + #reset-button:active{ transform: translateY(1px); } + #reset-button:focus-visible{ + outline: 3px solid var(--gold-bright); + outline-offset: 3px; + } + + @media (max-width: 420px){ + .choice{ width: 90px; height: 90px; } + .choice .emoji{ font-size: 28px; } + .side .glyph{ font-size: 36px; } + } + + @media (prefers-reduced-motion: reduce){ + .choice, #reset-button{ transition: none; } + } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5c0a278 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "javascript-games", + "lockfileVersion": 3, + "requires": true, + "packages": {} +}