Squarsh.
Tic Tac Toe with a Twist:
Each player has only 4 pieces, when you place a 4th, your oldest piece vanishes.
Player X's turn
(function () { var cells = Array.from(document.querySelectorAll("#tic-tac-toe-game .ttt-cell")); var statusEl = document.getElementById("ttt-status"); var resetBtn = document.getElementById("ttt-reset"); var currentPlayer = "X"; var board = ["", "", "", "", "", "", "", "", ""]; var gameOver = false; var moveHistory = { X: [], O: [] }; var winningCombos = [ [0,1,2],[3,4,5],[6,7,8], [0,3,6],[1,4,7],[2,5,8], [0,4,8],[2,4,6] ]; function setStatus(msg) { statusEl.textContent = msg; } function handleCellClick(e) { var cell = e.currentTarget; var index = parseInt(cell.getAttribute("data-index")); if (gameOver || board[index] !== "") return; board[index] = currentPlayer; cell.textContent = currentPlayer; moveHistory[currentPlayer].push(index); if (moveHistory[currentPlayer].length > 3) { var oldest = moveHistory[currentPlayer].shift(); board[oldest] = ""; cells[oldest].textContent = ""; cells[oldest].classList.remove("win"); } cells.forEach(c => c.classList.remove("win")); if (checkWin(currentPlayer)) { gameOver = true; setStatus("Player " + currentPlayer + " wins!"); highlightWinningCells(currentPlayer); disableBoard(); return; } currentPlayer = currentPlayer === "X" ? "O" : "X"; setStatus("Player " + currentPlayer + "'s turn"); } function checkWin(player) { return winningCombos.some(combo => combo.every(i => board[i] === player) ); } function highlightWinningCells(player) { winningCombos.forEach(combo => { if (combo.every(i => board[i] === player)) { combo.forEach(i => cells[i].classList.add("win")); } }); } function disableBoard() { cells.forEach(c => c.classList.add("disabled")); } function resetGame() { board.fill(""); moveHistory.X = []; moveHistory.O = []; currentPlayer = "X"; gameOver = false; cells.forEach(c => { c.textContent = ""; c.classList.remove("disabled", "win"); }); setStatus("Player X's turn"); } cells.forEach(c => c.addEventListener("click", handleCellClick)); resetBtn.addEventListener("click", resetGame); })();

Thanks. We'll update you soon!

5 Dice Roller

DiceSquarsh

Tap dice to freeze. Roll rerolls only unfrozen dice. NEW Turn rerolls all five and clears freezes.

Turn: 1Rolls: 0Total: —
Frozen: 0/5
Tip: freeze the dice you want to keep, then roll again.Tap to Freeze
const DICE_COUNT = 5; const state = { turn: 1, rollsThisTurn: 0, dice: Array.from({ length: DICE_COUNT }, () => ({ value: 1, frozen: false })) }; // 3x3 pip indices: // 0 1 2 // 3 4 5 // 6 7 8 const faces = { 1: [4], 2: [0, 8], 3: [0, 4, 8], 4: [0, 2, 6, 8], 5: [0, 2, 4, 6, 8], 6: [0, 2, 3, 5, 6, 8] }; const diceRow = document.getElementById('diceRow'); const rollBtn = document.getElementById('rollBtn'); const newTurnBtn = document.getElementById('newTurnBtn'); const clearFreezeBtn = document.getElementById('clearFreezeBtn'); const turnCountEl = document.getElementById('turnCount'); const rollCountEl = document.getElementById('rollCount'); const sumEl = document.getElementById('sum'); const frozenCountEl = document.getElementById('frozenCount'); function randDie(){ return Math.floor(Math.random() * 6) + 1; } function computeSum(){ return state.dice.reduce((a,d)=>a+d.value,0); } function frozenCount(){ return state.dice.filter(d=>d.frozen).length; } function render(){ diceRow.innerHTML = ''; state.dice.forEach((die, idx) => { const dieEl = document.createElement('div'); dieEl.className = 'die' + (die.frozen ? ' frozen' : ''); dieEl.setAttribute('role', 'button'); dieEl.setAttribute('tabindex', '0'); dieEl.setAttribute('aria-label', `Die ${idx+1}: ${die.value}${die.frozen ? ', frozen' : ''}`); const badge = document.createElement('div'); badge.className = 'badge'; badge.textContent = 'FROZEN'; dieEl.appendChild(badge); const face = document.createElement('div'); face.className = 'face'; for(let i=0;i<9;i++){ const pip = document.createElement('div'); pip.className = 'pip' + (faces[die.value].includes(i) ? ' on' : ''); face.appendChild(pip); } dieEl.appendChild(face); const toggleFreeze = () => { state.dice[idx].frozen = !state.dice[idx].frozen; render(); }; dieEl.addEventListener('click', toggleFreeze); dieEl.addEventListener('keydown', (e) => { if(e.key === 'Enter' || e.key === ' '){ e.preventDefault(); toggleFreeze(); } }); diceRow.appendChild(dieEl); }); turnCountEl.textContent = String(state.turn); rollCountEl.textContent = String(state.rollsThisTurn); sumEl.textContent = String(computeSum()); frozenCountEl.textContent = String(frozenCount()); } function animateDice(indices){ const els = Array.from(diceRow.children); indices.forEach(i => { const el = els[i]; if(!el) return; el.classList.remove('shake'); void el.offsetWidth; // reflow el.classList.add('shake'); }); } function roll({ includeFrozen }){ const changed = []; state.dice.forEach((d,i)=>{ if(includeFrozen || !d.frozen){ d.value = randDie(); changed.push(i); } }); state.rollsThisTurn += 1; render(); animateDice(changed); } function newTurn(){ state.turn += 1; state.rollsThisTurn = 0; state.dice.forEach(d => d.frozen = false); roll({ includeFrozen: true }); // counts as first roll of the turn } function clearFreezes(){ state.dice.forEach(d => d.frozen = false); render(); } rollBtn.addEventListener('click', () => roll({ includeFrozen: false })); newTurnBtn.addEventListener('click', newTurn); clearFreezeBtn.addEventListener('click', clearFreezes); // initial roll shown on load, but don't count it as a "user roll" roll({ includeFrozen: true }); state.rollsThisTurn = 0; render();