diff --git a/src/iframe/life-game.js b/src/iframe/life-game.js index 8ce2604..ef47d94 100644 --- a/src/iframe/life-game.js +++ b/src/iframe/life-game.js @@ -6,13 +6,15 @@ let generationFigure = 0; let timerTime = 1000; let isDragging = false; let dragMode = false; // true: 黒にする, false: 白にする - -const DEFAULT_BOARD_SIZE = 20; -const DEFAULT_CELL_SIZE = 30; +let isPlacingTemplate = false; +let patternShape = []; +let patternHeight = 0; +let patternWidth = 0; +let previewCells = []; //変数設定 -let boardSize = 20; -let cellSize = 30; +let boardSize = 20; //盤面の大きさ(20x20) +const cellSize = 600 / boardSize; //セルの大きさ(px) // around: 周囲の生きたセル数 self: 自身が生きているかどうか function isNextAlive(around, self) { @@ -27,11 +29,19 @@ function isNextAlive(around, self) { return false; } +// cellの状態に応じた色を返す関数 +function getStyle(cell) { + // cellがtrueなら黒、falseなら白を返す + return cell ? "black" : "white"; +} + //Boardの初期化 let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false)); const table = document.getElementById("game-board"); + +//盤面をBoardに従って変更する関数達(Boardを変更したら実行する) function renderBoard() { - //盤面をBoardに従って変更する関数(Boardを変更したら必ず実行する) + // 初回の盤面生成 table.innerHTML = ""; for (let i = 0; i < boardSize; i++) { const tr = document.createElement("tr"); @@ -46,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]; @@ -56,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); @@ -68,6 +108,61 @@ 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++) { + for (let j = 0; j < boardSize; j++) { + const button = table.children[i].children[j].children[0]; + + // 色の更新 + const currentCellColor = button.style.backgroundColor; + const expectedCellColor = getStyle(board[i][j]); + if (currentCellColor !== expectedCellColor) { + button.style.backgroundColor = expectedCellColor; + } + + // セルサイズの更新 + const currentCellsize = button.style.width; + const expectedCellsize = `${cellSize}px`; + if (currentCellsize !== expectedCellsize) { + button.style.width = expectedCellsize; + button.style.height = expectedCellsize; + } + } + } +} + document.addEventListener("mouseup", () => { isDragging = false; }); @@ -126,7 +221,7 @@ function progressBoard() { } board = newBoard; generationChange(generationFigure + 1); - renderBoard(); + rerender(); } function resetTimer() { @@ -147,15 +242,6 @@ on.pause = () => { resetTimer(); }; -on.board_resize = (newSize) => { - boardSize = newSize; - cellSize = Math.floor(DEFAULT_CELL_SIZE * (DEFAULT_BOARD_SIZE / newSize)); - board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false)); - renderBoard(); - generationChange(0); - resetTimer(); -}; - on.board_reset = () => { //すべて白にBoardを変更 board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false)); @@ -196,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(); }; @@ -209,6 +296,7 @@ on.save_board = async () => { }; on.apply_board = (newBoard) => { + boardSize = newBoard.length; board = newBoard; renderBoard(); generationChange(0); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index f2ec61f..0cd28f2 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -46,7 +46,7 @@ // unused events | "board_resize"; - 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) { @@ -60,6 +60,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": { saveState = { saving: true, boardData: event.data.data as boolean[][], boardName: "" }; break; @@ -164,13 +172,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) { @@ -185,19 +188,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); }} >