commit be3ed507e0d484d187deda66126eaa9652e513db
Author: root <root@hub.scroll.pub> Date: 2025-01-03 02:25:35 +0000 Subject: Initial commit diff --git a/body.html b/body.html new file mode 100644 index 0000000..8ab332e --- /dev/null +++ b/body.html @@ -0,0 +1,30 @@ +<header> + <h1>Minesweeper Challenge</h1> + <div class="level-info"> + <span>Level: <span id="current-level">1</span>/30</span> + <span>Mines: <span id="mines-count">0</span></span> + <span>Time: <span id="timer">0</span>s</span> + </div> +</header> + +<main> + <div class="game-container"> + <div id="grid" class="grid"></div> + <div class="controls"> + <button id="restart" class="btn">Restart Level</button> + <button id="help" class="btn">How to Play</button> + </div> + </div> + + <div id="modal" class="modal"> + <div class="modal-content"> + <h2 id="modal-title"></h2> + <p id="modal-message"></p> + <button id="modal-button" class="btn"></button> + </div> + </div> +</main> + +<footer> + <p>Made with ❤️ for puzzle lovers</p> +</footer> diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..1753938 --- /dev/null +++ b/index.scroll @@ -0,0 +1,8 @@ +buildHtml +baseUrl https://minesweeper.scroll.pub +metaTags +editButton /edit.html +title Minesweeper Challenge - 30 Exciting Levels +style.css +body.html +script.js diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..495ca95 --- /dev/null +++ b/prompt.json @@ -0,0 +1,20 @@ +{ + "id": "msg_01Jd2QYxBwK7VGnh6K1Z8P2F", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet-20241022", + "content": [ + { + "type": "text", + "text": "Let me create a minesweeper challenge game website with 30 levels.\n\n---domain---\nminesweeper.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://minesweeper.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Minesweeper Challenge - 30 Exciting Levels\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n<header>\n <h1>Minesweeper Challenge</h1>\n <div class=\"level-info\">\n <span>Level: <span id=\"current-level\">1</span>/30</span>\n <span>Mines: <span id=\"mines-count\">0</span></span>\n <span>Time: <span id=\"timer\">0</span>s</span>\n </div>\n</header>\n\n<main>\n <div class=\"game-container\">\n <div id=\"grid\" class=\"grid\"></div>\n <div class=\"controls\">\n <button id=\"restart\" class=\"btn\">Restart Level</button>\n <button id=\"help\" class=\"btn\">How to Play</button>\n </div>\n </div>\n \n <div id=\"modal\" class=\"modal\">\n <div class=\"modal-content\">\n <h2 id=\"modal-title\"></h2>\n <p id=\"modal-message\"></p>\n <button id=\"modal-button\" class=\"btn\"></button>\n </div>\n </div>\n</main>\n\n<footer>\n <p>Made with ❤️ for puzzle lovers</p>\n</footer>\n\n---style.css---\n:root {\n --primary-color: #2c3e50;\n --secondary-color: #3498db;\n --grid-color: #ecf0f1;\n --cell-size: 40px;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', system-ui, sans-serif;\n background: linear-gradient(135deg, #1a2a3a 0%, #2c3e50 100%);\n color: white;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\nheader {\n text-align: center;\n padding: 2rem;\n background: rgba(0, 0, 0, 0.2);\n}\n\nh1 {\n font-size: 2.5rem;\n margin-bottom: 1rem;\n text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);\n}\n\n.level-info {\n display: flex;\n justify-content: center;\n gap: 2rem;\n font-size: 1.2rem;\n}\n\nmain {\n flex: 1;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 2rem;\n}\n\n.game-container {\n background: rgba(255, 255, 255, 0.1);\n padding: 2rem;\n border-radius: 1rem;\n box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);\n}\n\n.grid {\n display: grid;\n gap: 2px;\n background: var(--primary-color);\n padding: 2px;\n border-radius: 4px;\n margin-bottom: 1rem;\n}\n\n.cell {\n width: var(--cell-size);\n height: var(--cell-size);\n background: var(--grid-color);\n border: none;\n display: flex;\n justify-content: center;\n align-items: center;\n font-weight: bold;\n font-size: 1.2rem;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.cell:hover {\n background: #bdc3c7;\n}\n\n.cell.revealed {\n background: #95a5a6;\n}\n\n.cell.mine {\n background: #e74c3c;\n}\n\n.cell.flagged {\n background: #f1c40f;\n}\n\n.controls {\n display: flex;\n gap: 1rem;\n justify-content: center;\n}\n\n.btn {\n padding: 0.8rem 1.5rem;\n border: none;\n border-radius: 4px;\n background: var(--secondary-color);\n color: white;\n font-size: 1rem;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.btn:hover {\n background: #2980b9;\n transform: translateY(-2px);\n}\n\n.modal {\n display: none;\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background: rgba(0, 0, 0, 0.8);\n justify-content: center;\n align-items: center;\n}\n\n.modal-content {\n background: white;\n color: var(--primary-color);\n padding: 2rem;\n border-radius: 1rem;\n text-align: center;\n max-width: 90%;\n width: 400px;\n}\n\nfooter {\n text-align: center;\n padding: 1rem;\n background: rgba(0, 0, 0, 0.2);\n}\n\n@media (max-width: 768px) {\n :root {\n --cell-size: 30px;\n }\n \n .level-info {\n flex-direction: column;\n gap: 0.5rem;\n }\n \n .game-container {\n padding: 1rem;\n }\n}\n\n---script.js---\nclass MinesweeperGame {\n constructor() {\n this.level = 1;\n this.grid = [];\n this.mines = [];\n this.timer = 0;\n this.timerInterval = null;\n this.isGameOver = false;\n \n this.initializeDOM();\n this.setupEventListeners();\n this.startLevel(this.level);\n }\n\n initializeDOM() {\n this.gridElement = document.getElementById('grid');\n this.levelElement = document.getElementById('current-level');\n this.minesElement = document.getElementById('mines-count');\n this.timerElement = document.getElementById('timer');\n this.modal = document.getElementById('modal');\n this.modalTitle = document.getElementById('modal-title');\n this.modalMessage = document.getElementById('modal-message');\n this.modalButton = document.getElementById('modal-button');\n }\n\n setupEventListeners() {\n document.getElementById('restart').addEventListener('click', () => this.startLevel(this.level));\n document.getElementById('help').addEventListener('click', () => this.showHelp());\n this.modalButton.addEventListener('click', () => this.hideModal());\n }\n\n getLevelConfig(level) {\n return {\n rows: Math.min(8 + Math.floor(level / 5), 16),\n cols: Math.min(8 + Math.floor(level / 5), 16),\n mines: Math.min(10 + level * 2, 99)\n };\n }\n\n startLevel(level) {\n this.level = level;\n this.isGameOver = false;\n clearInterval(this.timerInterval);\n this.timer = 0;\n this.timerElement.textContent = '0';\n \n const config = this.getLevelConfig(level);\n this.createGrid(config);\n this.levelElement.textContent = level;\n this.minesElement.textContent = config.mines;\n \n this.timerInterval = setInterval(() => {\n this.timer++;\n this.timerElement.textContent = this.timer;\n }, 1000);\n }\n\n createGrid(config) {\n this.grid = [];\n this.mines = [];\n this.gridElement.innerHTML = '';\n this.gridElement.style.gridTemplateColumns = `repeat(${config.cols}, var(--cell-size))`;\n\n for (let i = 0; i < config.rows; i++) {\n this.grid[i] = [];\n for (let j = 0; j < config.cols; j++) {\n const cell = document.createElement('button');\n cell.className = 'cell';\n cell.dataset.row = i;\n cell.dataset.col = j;\n \n cell.addEventListener('click', (e) => this.handleClick(e));\n cell.addEventListener('contextmenu', (e) => this.handleRightClick(e));\n \n this.gridElement.appendChild(cell);\n this.grid[i][j] = {\n element: cell,\n isMine: false,\n isRevealed: false,\n isFlagged: false,\n neighborMines: 0\n };\n }\n }\n\n this.placeMines(config);\n this.calculateNeighborMines(config);\n }\n\n placeMines(config) {\n let minesToPlace = config.mines;\n while (minesToPlace > 0) {\n const row = Math.floor(Math.random() * config.rows);\n const col = Math.floor(Math.random() * config.cols);\n \n if (!this.grid[row][col].isMine) {\n this.grid[row][col].isMine = true;\n this.mines.push({row, col});\n minesToPlace--;\n }\n }\n }\n\n calculateNeighborMines(config) {\n const directions = [\n [-1,-1], [-1,0], [-1,1],\n [0,-1], [0,1],\n [1,-1], [1,0], [1,1]\n ];\n\n for (let i = 0; i < config.rows; i++) {\n for (let j = 0; j < config.cols; j++) {\n if (!this.grid[i][j].isMine) {\n let count = 0;\n for (const [dx, dy] of directions) {\n const newRow = i + dx;\n const newCol = j + dy;\n if (newRow >= 0 && newRow < config.rows &&\n newCol >= 0 && newCol < config.cols &&\n this.grid[newRow][newCol].isMine) {\n count++;\n }\n }\n this.grid[i][j].neighborMines = count;\n }\n }\n }\n }\n\n handleClick(e) {\n e.preventDefault();\n if (this.isGameOver) return;\n\n const row = parseInt(e.target.dataset.row);\n const col = parseInt(e.target.dataset.col);\n const cell = this.grid[row][col];\n\n if (cell.isFlagged) return;\n\n if (cell.isMine) {\n this.gameOver(false);\n return;\n }\n\n this.revealCell(row, col);\n this.checkWin();\n }\n\n handleRightClick(e) {\n e.preventDefault();\n if (this.isGameOver) return;\n\n const row = parseInt(e.target.dataset.row);\n const col = parseInt(e.target.dataset.col);\n const cell = this.grid[row][col];\n\n if (!cell.isRevealed) {\n cell.isFlagged = !cell.isFlagged;\n cell.element.classList.toggle('flagged');\n }\n }\n\n revealCell(row, col) {\n const cell = this.grid[row][col];\n if (cell.isRevealed || cell.isFlagged) return;\n\n cell.isRevealed = true;\n cell.element.classList.add('revealed');\n\n if (cell.neighborMines > 0) {\n cell.element.textContent = cell.neighborMines;\n } else {\n const directions = [\n [-1,-1], [-1,0], [-1,1],\n [0,-1], [0,1],\n [1,-1], [1,0], [1,1]\n ];\n\n for (const [dx, dy] of directions) {\n const newRow = row + dx;\n const newCol = col + dy;\n if (newRow >= 0 && newRow < this.grid.length &&\n newCol >= 0 && newCol < this.grid[0].length) {\n this.revealCell(newRow, newCol);\n }\n }\n }\n }\n\n checkWin() {\n const config = this.getLevelConfig(this.level);\n const totalCells = config.rows * config.cols;\n let revealedCells = 0;\n\n for (let i = 0; i < config.rows; i++) {\n for (let j = 0; j < config.cols; j++) {\n if (this.grid[i][j].isRevealed) revealedCells++;\n }\n }\n\n if (revealedCells === totalCells - config.mines) {\n this.gameOver(true);\n }\n }\n\n gameOver(won) {\n this.isGameOver = true;\n clearInterval(this.timerInterval);\n\n if (won) {\n if (this.level === 30) {\n this.showModal('Congratulations!', 'You\\'ve completed all levels!', 'Play Again');\n this.level = 1;\n } else {\n this.showModal('Level Complete!', `Ready for level ${this.level + 1}?`, 'Next Level');\n this.level++;\n }\n } else {\n this.showModal('Game Over', 'Try again?', 'Restart Level');\n this.revealMines();\n }\n }\n\n revealMines() {\n this.mines.forEach(({row, col}) => {\n const cell = this.grid[row][col];\n cell.element.classList.add('mine');\n });\n }\n\n showModal(title, message, buttonText) {\n this.modalTitle.textContent = title;\n this.modalMessage.textContent = message;\n this.modalButton.textContent = buttonText;\n this.modal.style.display = 'flex';\n }\n\n hideModal() {\n this.modal.style.display = 'none';\n this.startLevel(this.level);\n }\n\n showHelp() {\n this.showModal('How to Play', \n 'Left click to reveal cells. Right click to flag mines. Avoid all mines to complete the level!',\n 'Got it!'\n );\n }\n}\n\nwindow.addEventListener('load', () => {\n new MinesweeperGame();\n});\n\n---end---\n\nThis implementation creates a progressive minesweeper game with 30 levels of increasing difficulty. Key features include:\n\n1. Progressive difficulty scaling (larger grid and more mines as levels increase)\n2. Timer and mines counter\n3. Responsive design that works on mobile and desktop\n4. Smooth animations and modern UI\n5. Help system\n6. Right-click flag placement\n7. Level progression system\n8. Victory/defeat conditions\n9. Clean, accessible interface\n10. Modern gradient background with glass-morphism effects\n\nThe game follows standard minesweeper rules but adds the excitement of level progression. Each level becomes slightly more challenging than the last, with more mines and a larger grid (up to a maximum size)." + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 288, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 3931 + } +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..9110825 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,2 @@ +# minesweeper.scroll.pub +Website generated by Claude from prompt: 创建一个扫雷冲关小游戏,30关 \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..797ccaa --- /dev/null +++ b/script.js @@ -0,0 +1,253 @@ +class MinesweeperGame { + constructor() { + this.level = 1; + this.grid = []; + this.mines = []; + this.timer = 0; + this.timerInterval = null; + this.isGameOver = false; + + this.initializeDOM(); + this.setupEventListeners(); + this.startLevel(this.level); + } + + initializeDOM() { + this.gridElement = document.getElementById('grid'); + this.levelElement = document.getElementById('current-level'); + this.minesElement = document.getElementById('mines-count'); + this.timerElement = document.getElementById('timer'); + this.modal = document.getElementById('modal'); + this.modalTitle = document.getElementById('modal-title'); + this.modalMessage = document.getElementById('modal-message'); + this.modalButton = document.getElementById('modal-button'); + } + + setupEventListeners() { + document.getElementById('restart').addEventListener('click', () => this.startLevel(this.level)); + document.getElementById('help').addEventListener('click', () => this.showHelp()); + this.modalButton.addEventListener('click', () => this.hideModal()); + } + + getLevelConfig(level) { + return { + rows: Math.min(8 + Math.floor(level / 5), 16), + cols: Math.min(8 + Math.floor(level / 5), 16), + mines: Math.min(10 + level * 2, 99) + }; + } + + startLevel(level) { + this.level = level; + this.isGameOver = false; + clearInterval(this.timerInterval); + this.timer = 0; + this.timerElement.textContent = '0'; + + const config = this.getLevelConfig(level); + this.createGrid(config); + this.levelElement.textContent = level; + this.minesElement.textContent = config.mines; + + this.timerInterval = setInterval(() => { + this.timer++; + this.timerElement.textContent = this.timer; + }, 1000); + } + + createGrid(config) { + this.grid = []; + this.mines = []; + this.gridElement.innerHTML = ''; + this.gridElement.style.gridTemplateColumns = `repeat(${config.cols}, var(--cell-size))`; + + for (let i = 0; i < config.rows; i++) { + this.grid[i] = []; + for (let j = 0; j < config.cols; j++) { + const cell = document.createElement('button'); + cell.className = 'cell'; + cell.dataset.row = i; + cell.dataset.col = j; + + cell.addEventListener('click', (e) => this.handleClick(e)); + cell.addEventListener('contextmenu', (e) => this.handleRightClick(e)); + + this.gridElement.appendChild(cell); + this.grid[i][j] = { + element: cell, + isMine: false, + isRevealed: false, + isFlagged: false, + neighborMines: 0 + }; + } + } + + this.placeMines(config); + this.calculateNeighborMines(config); + } + + placeMines(config) { + let minesToPlace = config.mines; + while (minesToPlace > 0) { + const row = Math.floor(Math.random() * config.rows); + const col = Math.floor(Math.random() * config.cols); + + if (!this.grid[row][col].isMine) { + this.grid[row][col].isMine = true; + this.mines.push({row, col}); + minesToPlace--; + } + } + } + + calculateNeighborMines(config) { + const directions = [ + [-1,-1], [-1,0], [-1,1], + [0,-1], [0,1], + [1,-1], [1,0], [1,1] + ]; + + for (let i = 0; i < config.rows; i++) { + for (let j = 0; j < config.cols; j++) { + if (!this.grid[i][j].isMine) { + let count = 0; + for (const [dx, dy] of directions) { + const newRow = i + dx; + const newCol = j + dy; + if (newRow >= 0 && newRow < config.rows && + newCol >= 0 && newCol < config.cols && + this.grid[newRow][newCol].isMine) { + count++; + } + } + this.grid[i][j].neighborMines = count; + } + } + } + } + + handleClick(e) { + e.preventDefault(); + if (this.isGameOver) return; + + const row = parseInt(e.target.dataset.row); + const col = parseInt(e.target.dataset.col); + const cell = this.grid[row][col]; + + if (cell.isFlagged) return; + + if (cell.isMine) { + this.gameOver(false); + return; + } + + this.revealCell(row, col); + this.checkWin(); + } + + handleRightClick(e) { + e.preventDefault(); + if (this.isGameOver) return; + + const row = parseInt(e.target.dataset.row); + const col = parseInt(e.target.dataset.col); + const cell = this.grid[row][col]; + + if (!cell.isRevealed) { + cell.isFlagged = !cell.isFlagged; + cell.element.classList.toggle('flagged'); + } + } + + revealCell(row, col) { + const cell = this.grid[row][col]; + if (cell.isRevealed || cell.isFlagged) return; + + cell.isRevealed = true; + cell.element.classList.add('revealed'); + + if (cell.neighborMines > 0) { + cell.element.textContent = cell.neighborMines; + } else { + const directions = [ + [-1,-1], [-1,0], [-1,1], + [0,-1], [0,1], + [1,-1], [1,0], [1,1] + ]; + + for (const [dx, dy] of directions) { + const newRow = row + dx; + const newCol = col + dy; + if (newRow >= 0 && newRow < this.grid.length && + newCol >= 0 && newCol < this.grid[0].length) { + this.revealCell(newRow, newCol); + } + } + } + } + + checkWin() { + const config = this.getLevelConfig(this.level); + const totalCells = config.rows * config.cols; + let revealedCells = 0; + + for (let i = 0; i < config.rows; i++) { + for (let j = 0; j < config.cols; j++) { + if (this.grid[i][j].isRevealed) revealedCells++; + } + } + + if (revealedCells === totalCells - config.mines) { + this.gameOver(true); + } + } + + gameOver(won) { + this.isGameOver = true; + clearInterval(this.timerInterval); + + if (won) { + if (this.level === 30) { + this.showModal('Congratulations!', 'You\'ve completed all levels!', 'Play Again'); + this.level = 1; + } else { + this.showModal('Level Complete!', `Ready for level ${this.level + 1}?`, 'Next Level'); + this.level++; + } + } else { + this.showModal('Game Over', 'Try again?', 'Restart Level'); + this.revealMines(); + } + } + + revealMines() { + this.mines.forEach(({row, col}) => { + const cell = this.grid[row][col]; + cell.element.classList.add('mine'); + }); + } + + showModal(title, message, buttonText) { + this.modalTitle.textContent = title; + this.modalMessage.textContent = message; + this.modalButton.textContent = buttonText; + this.modal.style.display = 'flex'; + } + + hideModal() { + this.modal.style.display = 'none'; + this.startLevel(this.level); + } + + showHelp() { + this.showModal('How to Play', + 'Left click to reveal cells. Right click to flag mines. Avoid all mines to complete the level!', + 'Got it!' + ); + } +} + +window.addEventListener('load', () => { + new MinesweeperGame(); +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..9781f30 --- /dev/null +++ b/style.css @@ -0,0 +1,159 @@ +:root { + --primary-color: #2c3e50; + --secondary-color: #3498db; + --grid-color: #ecf0f1; + --cell-size: 40px; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', system-ui, sans-serif; + background: linear-gradient(135deg, #1a2a3a 0%, #2c3e50 100%); + color: white; + min-height: 100vh; + display: flex; + flex-direction: column; +} + +header { + text-align: center; + padding: 2rem; + background: rgba(0, 0, 0, 0.2); +} + +h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); +} + +.level-info { + display: flex; + justify-content: center; + gap: 2rem; + font-size: 1.2rem; +} + +main { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + padding: 2rem; +} + +.game-container { + background: rgba(255, 255, 255, 0.1); + padding: 2rem; + border-radius: 1rem; + box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); +} + +.grid { + display: grid; + gap: 2px; + background: var(--primary-color); + padding: 2px; + border-radius: 4px; + margin-bottom: 1rem; +} + +.cell { + width: var(--cell-size); + height: var(--cell-size); + background: var(--grid-color); + border: none; + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + font-size: 1.2rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.cell:hover { + background: #bdc3c7; +} + +.cell.revealed { + background: #95a5a6; +} + +.cell.mine { + background: #e74c3c; +} + +.cell.flagged { + background: #f1c40f; +} + +.controls { + display: flex; + gap: 1rem; + justify-content: center; +} + +.btn { + padding: 0.8rem 1.5rem; + border: none; + border-radius: 4px; + background: var(--secondary-color); + color: white; + font-size: 1rem; + cursor: pointer; + transition: all 0.2s ease; +} + +.btn:hover { + background: #2980b9; + transform: translateY(-2px); +} + +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + justify-content: center; + align-items: center; +} + +.modal-content { + background: white; + color: var(--primary-color); + padding: 2rem; + border-radius: 1rem; + text-align: center; + max-width: 90%; + width: 400px; +} + +footer { + text-align: center; + padding: 1rem; + background: rgba(0, 0, 0, 0.2); +} + +@media (max-width: 768px) { + :root { + --cell-size: 30px; + } + + .level-info { + flex-direction: column; + gap: 0.5rem; + } + + .game-container { + padding: 1rem; + } +}