From cc513ff1d612f697eed731f088fb3d8a016bb9f0 Mon Sep 17 00:00:00 2001 From: TKHR-Shiu Date: Thu, 13 Nov 2025 22:04:18 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=83=95=E3=83=AA?= =?UTF-8?q?=E3=82=AF=E3=83=88=E3=81=AE=E8=A7=A3=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iframe/life-game.js | 81 +++++++++++++++++++++++++++++++++++++---- src/routes/+page.svelte | 27 +++++--------- 2 files changed, 84 insertions(+), 24 deletions(-) diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js index 23e942b..ef47d94 100644 --- a/src/iframe/life-game.js +++ b/src/iframe/life-game.js @@ -6,6 +6,11 @@ let generationFigure = 0; let timerTime = 1000; let isDragging = false; let dragMode = false; // true: 黒にする, false: 白にする +let isPlacingTemplate = false; +let patternShape = []; +let patternHeight = 0; +let patternWidth = 0; +let previewCells = []; //変数設定 let boardSize = 20; //盤面の大きさ(20x20) @@ -51,9 +56,36 @@ function renderBoard() { button.style.height = `${cellSize}px`; button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ + button.onclick = () => { + if (isPlacingTemplate) { + clearPreview(); + isPlacingTemplate = false; + if (i + patternHeight < boardSize + 1 && j + patternWidth < boardSize + 1) { + for (let r = 0; r < patternHeight; r++) { + for (let c = 0; c < patternWidth; c++) { + const boardRow = i + r; + const boardCol = j + c; + board[boardRow][boardCol] = patternShape[r][c] === 1; + } + } + rerender(); + generationChange(0); + resetTimer(); + stop(); + } else { + window.parent.postMessage( + { + type: "Size shortage", + data: {}, + }, + "*", + ); + } + } + }; button.onmousedown = (e) => { e.preventDefault(); - if (timer === "stop") { + if (timer === "stop" && !isPlacingTemplate) { isDragging = true; board[i][j] = !board[i][j]; dragMode = board[i][j]; @@ -61,10 +93,13 @@ function renderBoard() { } }; button.onmouseenter = () => { - if (isDragging && timer === "stop" && board[i][j] !== dragMode) { + if (isDragging && timer === "stop" && board[i][j] !== dragMode && !isPlacingTemplate) { board[i][j] = dragMode; button.style.backgroundColor = board[i][j] ? "black" : "white"; } + if (isPlacingTemplate) { + drawPreview(i, j); + } }; td.appendChild(button); tr.appendChild(td); @@ -73,6 +108,37 @@ function renderBoard() { } } +table.onmouseleave = () => { + if (isPlacingTemplate) { + clearPreview(); + } +}; + +function drawPreview(row, col) { + clearPreview(); + for (let r = 0; r < patternHeight; r++) { + for (let c = 0; c < patternWidth; c++) { + if (patternShape[r][c] === 1) { + const boardRow = row + r; + const boardCol = col + c; + if (boardRow < boardSize && boardCol < boardSize) { + const cell = table.rows[boardRow].cells[boardCol].firstChild; + cell.style.backgroundColor = "gray"; + previewCells.push({ row: boardRow, col: boardCol }); + } + } + } + } +} + +function clearPreview() { + previewCells.forEach((cellPos) => { + const cell = table.rows[cellPos.row].cells[cellPos.col].firstChild; + cell.style.backgroundColor = board[cellPos.row][cellPos.col] ? "black" : "white"; + }); + previewCells = []; +} + function rerender() { // 2回目以降の盤面生成 for (let i = 0; i < boardSize; i++) { @@ -216,11 +282,12 @@ on.request_sync = () => { console.log("generationFigure:", generationFigure, "boardSize:", boardSize); }; -on.place_template = (newBoard) => { - board = newBoard; - renderBoard(); - generationChange(0); - resetTimer(); +on.place_template = (template) => { + patternShape = template; + patternHeight = patternShape.length; + patternWidth = patternShape[0].length; + isPlacingTemplate = true; + table.style.cursor = "crosshair"; stop(); }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index e8b90ee..23ce65e 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -47,7 +47,7 @@ | "apply_board" | "request_sync"; - type IncomingEvent = "generation_change" | "sync" | "save_board"; + type IncomingEvent = "generation_change" | "sync" | "Size shortage" | "save_board"; function handleMessage(event: MessageEvent<{ type: IncomingEvent; data: unknown }>) { switch (event.data.type) { @@ -61,6 +61,14 @@ sizeValue = data.boardSize; break; } + case "Size shortage": { + alert( + isJapanese + ? "盤面からはみ出してしまうため、キャンセルしました" + : "This action was canceled because it would have extended beyond the board.", + ); + break; + } case "save_board": { const board = event.data.data as boolean[][]; const preview = createPreview(board); @@ -168,13 +176,8 @@ onclick={() => { sendEvent("request_sync"); - const newBoard = Array.from({ length: sizeValue }, () => - Array.from({ length: sizeValue }, () => false), - ); const patternData = patterns[patternName]; const patternShape = patternData.shape; - const patternHeight = patternShape.length; - const patternWidth = patternShape[0].length; if (sizeValue < (patternData.minBoardSize || 0)) { if (isJapanese) { @@ -190,18 +193,8 @@ return; } // パターンがボードの中央に来るよう、パターンの左上のセルの位置(startRow, startCol)を調整 - const startRow = Math.floor((sizeValue - patternHeight) / 2); - const startCol = Math.floor((sizeValue - patternWidth) / 2); - - for (let r = 0; r < patternHeight; r++) { - for (let c = 0; c < patternWidth; c++) { - const boardRow = startRow + r; - const boardCol = startCol + c; - newBoard[boardRow][boardCol] = patternShape[r][c] === 1; - } - } bottomDrawerOpen = false; - sendEvent("place_template", newBoard); + sendEvent("place_template", patternShape); }} > Date: Thu, 13 Nov 2025 22:52:27 +0900 Subject: [PATCH 2/5] =?UTF-8?q?Timer=E3=80=80=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/iframe/life-game.js | 28 +++++--------------------- src/routes/+page.svelte | 44 +++++++++++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js index ef47d94..a1cadfd 100644 --- a/src/iframe/life-game.js +++ b/src/iframe/life-game.js @@ -1,9 +1,7 @@ "use strict"; let timer = "stop"; -let timerId = 0; let generationFigure = 0; -let timerTime = 1000; let isDragging = false; let dragMode = false; // true: 黒にする, false: 白にする let isPlacingTemplate = false; @@ -70,7 +68,6 @@ function renderBoard() { } rerender(); generationChange(0); - resetTimer(); stop(); } else { window.parent.postMessage( @@ -224,22 +221,18 @@ function progressBoard() { rerender(); } -function resetTimer() { - if (timer !== "stop") { - timer = "stop"; - clearInterval(timerId); - } -} - //イベント +on.progress = () => { + progressBoard(); +}; + on.play = () => { timer = "start"; - timerId = setInterval(progressBoard, timerTime); }; on.pause = () => { - resetTimer(); + timer = "stop"; }; on.board_reset = () => { @@ -247,7 +240,6 @@ on.board_reset = () => { board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false)); renderBoard(); generationChange(0); - resetTimer(); }; on.board_randomize = () => { @@ -257,15 +249,6 @@ on.board_randomize = () => { ); renderBoard(); generationChange(0); - resetTimer(); -}; - -on.timer_change = (ms) => { - timerTime = ms; - if (timer === "start") { - clearInterval(timerId); - timerId = setInterval(progressBoard, timerTime); - } }; on.request_sync = () => { @@ -300,6 +283,5 @@ on.apply_board = (newBoard) => { board = newBoard; renderBoard(); generationChange(0); - resetTimer(); stop(); }; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 23ce65e..ecec587 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -19,6 +19,7 @@ let isJapanese = $state(true); let resetModalOpen = $state(false); let bottomDrawerOpen = $state(false); + let timerId: number | NodeJS.Timeout = 0; let intervalMs = $state(1000); let generationFigure = $state(0); @@ -39,13 +40,13 @@ | "play" | "pause" | "state_update" - | "timer_change" | "board_reset" | "board_randomize" | "place_template" | "save_board" | "apply_board" - | "request_sync"; + | "request_sync" + | "progress"; type IncomingEvent = "generation_change" | "sync" | "Size shortage" | "save_board"; @@ -192,7 +193,6 @@ return; } - // パターンがボードの中央に来るよう、パターンの左上のセルの位置(startRow, startCol)を調整 bottomDrawerOpen = false; sendEvent("place_template", patternShape); }} @@ -408,7 +408,12 @@ class="btn btn-ghost btn-circle hover:bg-[rgb(220,220,220)]" onclick={() => { intervalMs = intervalMs * 2; - sendEvent("timer_change", intervalMs); + if (isProgress) { + clearInterval(timerId); + timerId = setInterval(() => { + sendEvent("progress"); + }, intervalMs); + } }} > decelerate @@ -418,7 +423,12 @@ class="btn btn-ghost btn-circle text-black hover:bg-[rgb(220,220,220)]" onclick={() => { intervalMs = 1000; - sendEvent("timer_change", intervalMs); + if (isProgress) { + clearInterval(timerId); + timerId = setInterval(() => { + sendEvent("progress"); + }, intervalMs); + } }} > x1 @@ -428,7 +438,12 @@ class="btn btn-ghost btn-circle hover:bg-[rgb(220,220,220)]" onclick={() => { intervalMs = intervalMs / 2; - sendEvent("timer_change", intervalMs); + if (isProgress) { + clearInterval(timerId); + timerId = setInterval(() => { + sendEvent("progress"); + }, intervalMs); + } }} > accelerate @@ -448,8 +463,15 @@