Stack the Colors

Stack the Colors /* SortNStack Core Theme */ body { background: #111; color: white; font-family: 'Segoe UI', Roboto, -apple-system, sans-serif; display: flex; justify-content: center; align-items: center; min-height: 100vh; margin: 0; overflow-x: hidden; } .game-container { background: black; text-align: center; padding: 20px; width: 100%; } h1 { margin: 0 0 5px 0; font-weight: 300; letter-spacing: 4px; text-transform: uppercase; color: #eee; } #status { margin-bottom: 25px; font-size: 1rem; color: #bbb; min-height: 1.5rem; font-variant-numeric: tabular-nums; } /* The Grid */ .board { display: grid; gap: 1px; margin: 20px auto; background: transparent; border: none; padding: 10px; justify-content: center; transition: all 0.3s ease-in-out; } /* Cell Design */ .cell { width: 35px; height: 35px; cursor: pointer; transition: transform 0.15s, box-shadow 0.3s, filter 0.2s; border-radius: 4px; border: 1px solid rgba(255, 255, 255, 0.05); } /* Hover effect only for accessible cells */ .cell:not(.locked):hover { filter: brightness(1.2); transform: translateY(-2px); } .cell.locked { cursor: not-allowed; } .cell.selected { outline: 3px solid #FDD835; transform: scale(1.15) !important; z-index: 10; box-shadow: 0 0 20px rgba(253, 216, 53, 0.5); } /* Reset Button */ #reset-btn { background: transparent; color: #666; border: 1px solid #333; padding: 10px 20px; margin-top: 15px; cursor: pointer; font-size: 0.8rem; border-radius: 6px; transition: all 0.2s; text-transform: uppercase; letter-spacing: 1px; } #reset-btn:hover { color: #FDD835; border-color: #FDD835; } /* Results Card */ #next-level-container { animation: slideIn 0.4s ease-out; } @keyframes slideIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } /* Secret Menu (xpxp) */ #secret-menu { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.85); justify-content: center; align-items: center; z-index: 1000; backdrop-filter: blur(8px); } .menu-content { background: #1a1a1a; padding: 30px; border: 1px solid #FDD835; border-radius: 12px; text-align: left; min-width: 260px; box-shadow: 0 20px 50px rgba(0,0,0,0.5); } .menu-content h2 { color: #FDD835; margin-top: 0; } .menu-content label { display: block; margin: 15px 0 5px 0; font-size: 0.85rem; color: #888; } .menu-content input { background: #222; border: 1px solid #444; color: white; padding: 8px; width: 100%; box-sizing: border-box; border-radius: 4px; } .menu-buttons { margin-top: 25px; display: flex; gap: 10px; } .menu-buttons button { flex: 1; padding: 10px; cursor: pointer; border-radius: 4px; border: none; font-weight: bold; } #apply-settings { background: #FDD835; color: black; } #close-menu { background: #333; color: white; } Stack the Colors Initializing... Reset Grid Developer Settings Size: 2x3 Columns (Manual) Rows (Manual) Jump to Level Apply & Restart Close const boardElement = document.getElementById('board'); const statusDisplay = document.getElementById('status'); const resetBtn = document.getElementById('reset-btn'); const secretMenu = document.getElementById('secret-menu'); // State let currentLevel = 1; let COLS = 2; let PLAYABLE_ROWS = 3; let TOTAL_GRID_COLS = 3; let isBonusLevel = false; const WHITE = "#FFFFFF"; const ACCENT_YELLOW = "#FDD835"; const BLACK = "#000000"; const masterPalette = [ "#FF3B30", "#007AFF", "#4CD964", "#FFCC00", "#AF52DE", "#FF9500", "#FF2D55", "#5AC8FA", "#A2845E", "#000000", "#8EFA00", "#FF00FF", "#008080", "#E6BEFF", "#9A6324", "#F0A0FF", "#0075DC", "#993F00", "#4C005C", "#191919", "#005C31", "#2BCE48", "#FFCC99", "#808080", "#94FFB5" ]; // Extended Pixel Art (Height 5) const ALPHABET = { 'A': [[0,1,0],[1,0,1],[1,1,1],[1,0,1],[1,0,1]], // 3px 'D': [[1,1,0],[1,0,1],[1,0,1],[1,0,1],[1,1,0]], // 3px 'E': [[1,1],[1,0],[1,1],[1,0],[1,1]], // 2px 'H': [[1,0,1],[1,0,1],[1,1,1],[1,0,1],[1,0,1]], // 3px 'I': [[1],[1],[1],[1],[1]], // 1px (Sans Serif) 'K': [[1,0,1],[1,0,1],[1,1,0],[1,0,1],[1,0,1]], // 3px 'L': [[1,0],[1,0],[1,0],[1,0],[1,1]], // 2px 'M': [[1,0,1],[1,1,1],[1,0,1],[1,0,1],[1,0,1]], // 3px 'N': [[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,0,1]], // 3px 'O': [[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]], // 3px 'R': [[1,1,1],[1,0,1],[1,1,0],[1,0,1],[1,0,1]], // 3px 'T': [[1,1,1],[0,1,0],[0,1,0],[0,1,0],[0,1,0]], // 3px 'U': [[1,0,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]], // 3px 'V': [[1,0,1],[1,0,1],[1,0,1],[1,0,1],[0,1,0]], // 3px 'Y': [[1,0,1],[1,0,1],[1,0,1],[0,1,0],[0,1,0]] // 3px }; let gameState = []; let firstSelectedIndex = null; let gameActive = true; let keyBuffer = ""; let moveCount = 0; let secondsElapsed = 0; let timerInterval = null; let records = JSON.parse(localStorage.getItem('sortNStackRecords')) || {}; function saveRecord(level, moves, time) { if (!records[level]) { records[level] = { moves, time }; } else { records[level].moves = Math.min(records[level].moves, moves); records[level].time = Math.min(records[level].time, time); } localStorage.setItem('sortNStackRecords', JSON.stringify(records)); } function createBoard() { isBonusLevel = (currentLevel % 10 === 0); if (isBonusLevel) { // Bonus Level: 18x12 Total Grid (Cols 0 and 17 empty) TOTAL_GRID_COLS = 18; COLS = 16; PLAYABLE_ROWS = 12; } else { COLS = 2 Math.floor(currentLevel / 2); PLAYABLE_ROWS = 3 Math.floor((currentLevel - 1) / 2); TOTAL_GRID_COLS = COLS 1; } boardElement.innerHTML = ""; const oldResults = document.getElementById('next-level-container'); if (oldResults) oldResults.remove(); boardElement.style.gridTemplateColumns = `repeat(${TOTAL_GRID_COLS}, 35px)`; gameState = Array(PLAYABLE_ROWS * TOTAL_GRID_COLS).fill(null); gameActive = true; firstSelectedIndex = null; moveCount = 0; secondsElapsed = 0; clearInterval(timerInterval); updateStatusText(); if (currentLevel >= 3) startTimer(); // --- INITIALIZE GRID --- for (let i = 0; i < gameState.length; i ) gameState[i] = WHITE; if (isBonusLevel) { setupBonusLevel(); } else { setupNormalLevel(); } triggerGravity(); render(); } function setupBonusLevel() { // Fill background of Color Columns (1-16) with BLACK // Leave top row empty (row 0) for (let r = 1; r < PLAYABLE_ROWS; r ) { for (let c = 1; c < TOTAL_GRID_COLS - 1; c ) { gameState[r * TOTAL_GRID_COLS c] = BLACK; } } // Helper to paint char function paintChar(char, startCol, startRow) { const map = ALPHABET[char]; if (!map) return; // Detect width dynamically const charWidth = map[0].length; for (let r = 0; r < 5; r ) { for (let c = 0; c < charWidth; c ) { if (map[r][c] === 1) { const gridRow = startRow r; const gridCol = startCol c; if (gridRow < PLAYABLE_ROWS && gridCol < TOTAL_GRID_COLS - 1) { gameState[gridRow * TOTAL_GRID_COLS gridCol] = ACCENT_YELLOW; } } } } } if (currentLevel === 10) { // "THAN" (Row 1) - T(3) 1 H(3) 1 A(3) 1 N(3) = 15px width. // Fits in 16 cols. Start at 1. paintChar('T', 1, 1); paintChar('H', 5, 1); paintChar('A', 9, 1); paintChar('N', 13, 1); // "K YOU" (Row 7) // K(3) 2px Gap Y(3) 1px Gap O(3) 1px Gap U(3) = 16px width. // Fits perfectly in 16 cols (1-16). paintChar('K', 1, 7); paintChar('Y', 6, 7); // 1 (K) 3 2 (gap) = 6 paintChar('O', 10, 7); paintChar('U', 14, 7); } else if (currentLevel === 20) { // "I LOVE" -> I(1) L(2) O(3) V(3) E(2) 4 spaces = 15px paintChar('I', 1, 1); paintChar('L', 3, 1); paintChar('O', 6, 1); paintChar('V', 10, 1); paintChar('E', 14, 1); // "MELLE" -> M(3) E(2) L(2) L(2) E(2) 4 spaces = 15px paintChar('M', 1, 7); paintChar('E', 5, 7); paintChar('L', 8, 7); paintChar('L', 11, 7); paintChar('E', 14, 7); } else { // "YOUR AD" paintChar('Y', 2, 1); paintChar('O', 6, 1); paintChar('U', 10, 1); paintChar('R', 14, 1); paintChar('A', 6, 7); paintChar('D', 10, 7); } } function setupNormalLevel() { let colorsForGame = []; for (let i = 0; i < COLS; i ) { colorsForGame.push(masterPalette[i % masterPalette.length]); } let colorPool = []; colorsForGame.forEach(color => { for (let i = 0; i < PLAYABLE_ROWS - 1; i ) colorPool.push(color); }); while (colorPool.length < (COLS * PLAYABLE_ROWS)) colorPool.push(WHITE); colorPool.sort(() => Math.random() - 0.5); let poolIdx = 0; for (let r = 0; r < PLAYABLE_ROWS; r ) { for (let c = 1; c <= COLS; c ) { gameState[r * TOTAL_GRID_COLS c] = colorPool[poolIdx ]; } } } function updateStatusText() { let text = isBonusLevel ? `BONUS LEVEL ${currentLevel}` : `Lvl ${currentLevel}`; text = ` | Moves: ${moveCount}`; if (currentLevel >= 3) { const mins = Math.floor(secondsElapsed / 60); const secs = String(secondsElapsed % 60).padStart(2, '0'); text = ` | Time: ${mins}:${secs}`; } const record = records[currentLevel]; if (record) { text = ` (Best: ${record.moves})`; } statusDisplay.textContent = text; } function startTimer() { timerInterval = setInterval(() => { secondsElapsed ; updateStatusText(); }, 1000); } function triggerGravity() { let changed = false; for (let r = PLAYABLE_ROWS - 1; r > 0; r--) { for (let c = 0; c < TOTAL_GRID_COLS; c ) { const cur = r * TOTAL_GRID_COLS c; const up = (r - 1) * TOTAL_GRID_COLS c; if (gameState[cur] === WHITE && gameState[up] !== WHITE) { [gameState[cur], gameState[up]] = [gameState[up], gameState[cur]]; changed = true; } } } if (changed) triggerGravity(); } function isAccessible(index) { return index < TOTAL_GRID_COLS || gameState[index - TOTAL_GRID_COLS] === WHITE; } function isColumnComplete(colIndex) { let colColors = []; for (let r = 0; r < PLAYABLE_ROWS; r ) colColors.push(gameState[r * TOTAL_GRID_COLS colIndex]); const nonWhite = colColors.filter(c => c !== WHITE); if (nonWhite.length === 0) return true; const isMonochromatic = nonWhite.every(v => v === nonWhite[0]); if (isBonusLevel) { return isMonochromatic; } else { return isMonochromatic && nonWhite.length === (PLAYABLE_ROWS - 1); } } function render() { boardElement.innerHTML = ""; gameState.forEach((color, i) => { const cell = document.createElement('div'); cell.className = 'cell'; if (!isAccessible(i)) cell.classList.add('locked'); if (firstSelectedIndex === i) cell.classList.add('selected'); if (isColumnComplete(i % TOTAL_GRID_COLS)) { cell.style.boxShadow = `inset 0 0 10px ${ACCENT_YELLOW}`; cell.style.border = `1px solid ${ACCENT_YELLOW}`; } cell.style.backgroundColor = color; cell.onclick = () => handleCellClick(i); boardElement.appendChild(cell); }); } function handleCellClick(index) { if (!gameActive || !isAccessible(index)) return; let target = index; if (gameState[index] === WHITE) { while (target TOTAL_GRID_COLS < gameState.length && gameState[target TOTAL_GRID_COLS] === WHITE) target = TOTAL_GRID_COLS; } if (firstSelectedIndex === null) { if (gameState[target] !== WHITE) { firstSelectedIndex = target; } } else { if (firstSelectedIndex === target) { firstSelectedIndex = null; } else { [gameState[firstSelectedIndex], gameState[target]] = [gameState[target], gameState[firstSelectedIndex]]; moveCount ; updateStatusText(); triggerGravity(); let completeCount = 0; for (let c = 0; c < TOTAL_GRID_COLS; c ) { if (isColumnComplete(c)) completeCount ; } if (completeCount === TOTAL_GRID_COLS) showWinState(); firstSelectedIndex = null; } } render(); } function showWinState() { gameActive = false; clearInterval(timerInterval); saveRecord(currentLevel, moveCount, secondsElapsed); statusDisplay.textContent = isBonusLevel ? "BONUS CLEARED!" : "LEVEL COMPLETE!"; const resultsBox = document.createElement('div'); resultsBox.id = "next-level-container"; resultsBox.style.cssText = `background: #222; border: 2px solid ${ACCENT_YELLOW}; padding: 15px; margin: 20px auto; border-radius: 10px; max-width: 300px; color: white; text-align: center;`; const timeHTML = currentLevel >= 3 ? `<p>Time: <strong>${Math.floor(secondsElapsed / 60)}:${String(secondsElapsed % 60).padStart(2, '0')}</strong></p>` : ""; resultsBox.innerHTML = ` <h3 style="color: ${ACCENT_YELLOW}; margin: 0 0 10px 0;">Results</h3> ${timeHTML} <p>Moves: <strong>${moveCount}</strong></p> <button id="next-level-btn" style="width: 100%; padding: 10px; margin-top: 10px; cursor: pointer; background: ${ACCENT_YELLOW}; border: none; font-weight: bold; border-radius: 5px;">Next Level</button> `; boardElement.after(resultsBox); document.getElementById('next-level-btn').onclick = () => { currentLevel ; createBoard(); }; } // Menu logic function updateMenuPreview() { const jump = parseInt(document.getElementById('input-lvl').value); const preview = document.getElementById('size-preview'); if (!isNaN(jump) && jump > 0) { if (jump % 10 === 0) { preview.textContent = `Level ${jump} (BONUS): 18x12`; } else { const w = 2 Math.floor(jump / 2); const h = 3 Math.floor((jump - 1) / 2); preview.textContent = `Level ${jump} Preview: ${w}x${h}`; } } else { preview.textContent = `Custom: ${document.getElementById('input-cols').value}x${document.getElementById('input-rows').value}`; } } ['input-cols','input-rows','input-lvl'].forEach(id => document.getElementById(id).oninput = updateMenuPreview); window.onkeydown = (e) => { keyBuffer = e.key.toLowerCase(); if (keyBuffer.endsWith("xpxp")) { secretMenu.style.display = "flex"; keyBuffer = ""; updateMenuPreview(); } }; document.getElementById('apply-settings').onclick = () => { const jump = parseInt(document.getElementById('input-lvl').value); if (!isNaN(jump) && jump > 0) { currentLevel = jump; } else { COLS = parseInt(document.getElementById('input-cols').value) || 2; PLAYABLE_ROWS = parseInt(document.getElementById('input-rows').value) || 3; currentLevel = 1; } secretMenu.style.display = "none"; createBoard(); }; document.getElementById('close-menu').onclick = () => secretMenu.style.display = "none"; resetBtn.onclick = createBoard; createBoard();
 •  0 comments  •  flag
Share on Twitter
Published on December 30, 2025 08:48
No comments have been added yet.