diff --git a/site/index.html b/site/index.html index b5f8c92..55afdd4 100644 --- a/site/index.html +++ b/site/index.html @@ -28,6 +28,7 @@
  • Generic Scorecard - 4 Players
  • Mahjong Scorecard
  • Finska Scorecard
  • +
  • Mental Arithmatic Game
  • Neon Snake: vibe-coded by Google Gemini
  • diff --git a/site/mental-arithmatic/index.html b/site/mental-arithmatic/index.html new file mode 100644 index 0000000..31e8a6c --- /dev/null +++ b/site/mental-arithmatic/index.html @@ -0,0 +1,42 @@ + + + + + + Mental Arithmatic - Tools + + + + + +
    +
    +
    +

    Mental Arithmatic

    +

    A simple mental arithmetic game

    +
    +
    + +
    +
    +
    + + + \ No newline at end of file diff --git a/site/mental-arithmatic/scripts/game.js b/site/mental-arithmatic/scripts/game.js new file mode 100644 index 0000000..e583a19 --- /dev/null +++ b/site/mental-arithmatic/scripts/game.js @@ -0,0 +1,104 @@ +import { Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"; + +export class GameController extends Controller { + static targets = [ + "clock", + "score", + "problem", + "indicators", + "answer", + ]; + + start() { + this.startGame(); + this.element.classList.remove('hidden'); + } + + submitAnswer(ev) { + ev.preventDefault(); + + this._checkAnswer(); + } + + startGame() { + this._clock = 120; + this._score = 0; + + this._generateProblem(); + this._startClock(); + + this._updateClock(); + } + + _startClock() { + this._clockInterval = window.setInterval(() => { + if (this._clock <= 0) { + this._gameOver(); + return; + } + + this._clock -= 1; + this._updateClock(); + }, 1000); + } + + _gameOver() { + window.clearInterval(this._clockInterval); + + this.element.classList.add('hidden'); + window.dispatchEvent(new CustomEvent("endGame")); + } + + _generateProblem() { + const num1 = Math.floor(Math.random() * 99) + 1; + const num2 = Math.floor(Math.random() * 99) + 1; + + this._problem = [num1, num2]; + this._answer = num1 + num2; + + this.indicatorsTarget.classList.remove('right', 'wrong'); + this.problemTarget.innerHTML = ''; + + for (let i = 0; i < this._problem.length; i++) { + const div = document.createElement('div'); + + if (i === this._problem.length - 1) { + div.textContent = '+ ' + this._problem[i]; + } else { + div.textContent = this._problem[i]; + } + this.problemTarget.appendChild(div); + } + + this.answerTarget.disabled = false; + this.answerTarget.value = ""; + this.answerTarget.focus(); + } + + _checkAnswer() { + let isRight = parseInt(this.answerTarget.value) === this._answer; + + this.answerTarget.disabled = true; + + let delay = 500; + + if (isRight) { + this._score += 1; + this.indicatorsTarget.classList.add('right'); + } else { + this.indicatorsTarget.classList.add('wrong'); + this.answerTarget.value = this._answer; + delay = 800; + } + + this.scoreTarget.textContent = this._score; + + window.setTimeout(() => { this._generateProblem(); }, delay); + } + + _updateClock() { + let m = Math.floor(this._clock / 60); + let s = this._clock % 60; + this.clockTarget.textContent = `${m}:${s < 10 ? '0' : ''}${s}`; + } +} \ No newline at end of file diff --git a/site/mental-arithmatic/scripts/main.js b/site/mental-arithmatic/scripts/main.js new file mode 100644 index 0000000..c15b905 --- /dev/null +++ b/site/mental-arithmatic/scripts/main.js @@ -0,0 +1,8 @@ +import { Application } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"; +import { WelcomeController } from "./welcome.js" +import { GameController } from "./game.js" + +window.Stimulus = Application.start(); + +window.Stimulus.register('welcome', WelcomeController); +window.Stimulus.register('game', GameController); \ No newline at end of file diff --git a/site/mental-arithmatic/scripts/welcome.js b/site/mental-arithmatic/scripts/welcome.js new file mode 100644 index 0000000..c4ce76f --- /dev/null +++ b/site/mental-arithmatic/scripts/welcome.js @@ -0,0 +1,14 @@ +import { Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js"; + +export class WelcomeController extends Controller { + startGame(ev) { + ev.preventDefault(); + + this.element.classList.add('hidden'); + window.dispatchEvent(new CustomEvent("startGame")); + } + + gameEnded(ev) { + this.element.classList.remove('hidden'); + } +} \ No newline at end of file diff --git a/site/mental-arithmatic/style.css b/site/mental-arithmatic/style.css new file mode 100644 index 0000000..58d4173 --- /dev/null +++ b/site/mental-arithmatic/style.css @@ -0,0 +1,99 @@ +.hidden { + display: none !important; +} + +body { + min-height: 100dvh; + --pico-form-element-disabled-opacity: 1.0; +} + +.game-card { + display: flex; + flex-direction: column; + min-height: 80dvh; +} + +.game-card header, +.game-card footer { + display: flex; + justify-content: space-between; + + font-size: 2em; + margin-block: 12px; + margin-inline: 20px; +} + +.game-card main { + flex-grow: 1; + flex-shrink: 1; + + display: flex; + align-items: center; +} + +.game-card .question { + margin: auto; + width: 60vw; + + text-align: right; + font-size: 3em; +} + +.game-card .answer { + border-block: 2px solid black; + padding-block: 2px; + position: relative; +} + +.game-card .answer.right { + border-color: green; + color: green; +} + +.game-card .answer.wrong { + border-color: red; + color: red; +} + +.game-card .answer .indicator { + display: none; + position: absolute; + top: 0; + bottom: 0; + left: 0; +} + +.game-card .answer.right .indicator.right { + display: block; +} + +.game-card .answer.wrong .indicator.wrong { + display: block; +} + +.game-card .question input { + border-inline: none; + border-radius: 0; + box-shadow: none; + + border: none; + + margin: 0; + padding: 0; + + text-align: right; + font-size: 1em; + height: 1em; +} + +.game-card .question input:focus { + border-none: none; +} + +.game-card .answer.right input { + color: green; +} + +.game-card .answer.wrong input { + color: red; +} \ No newline at end of file