Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 78 additions & 29 deletions src/iframe/life-game.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
"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;
let patternShape = [];
let patternHeight = 0;
let patternWidth = 0;
let previewCells = [];

//変数設定
let boardSize = 20; //盤面の大きさ(20x20)
Expand Down Expand Up @@ -51,20 +54,49 @@ 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);
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];
button.style.backgroundColor = board[i][j] ? "black" : "white";
}
};
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);
Expand All @@ -73,6 +105,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++) {
Expand Down Expand Up @@ -158,30 +221,25 @@ 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 = () => {
//すべて白にBoardを変更
board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => false));
renderBoard();
generationChange(0);
resetTimer();
};

on.board_randomize = () => {
Expand All @@ -191,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 = () => {
Expand All @@ -216,11 +265,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();
};

Expand All @@ -233,6 +283,5 @@ on.apply_board = (newBoard) => {
board = newBoard;
renderBoard();
generationChange(0);
resetTimer();
stop();
};
63 changes: 37 additions & 26 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@
let resetModalOpen = $state(false);
let bottomDrawerOpen = $state(false);

let intervalMs = $state(1000);
let generationFigure = $state(0);
let sizeValue = $state(20);

let timer: "running" | "stopped" = $state("stopped");
let intervalMs = $state(1000);
$effect(() => {
if (timer === "stopped") return;
const timerId = setInterval(() => {
sendEvent("progress");
}, intervalMs);
return () => clearInterval(timerId);
});

type SaveState =
| { saving: false }
| { saving: true; boardData: boolean[][]; boardName: string; boardPreview: boolean[][] };
Expand All @@ -39,15 +48,15 @@
| "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" | "save_board";
type IncomingEvent = "generation_change" | "sync" | "Size shortage" | "save_board";

function handleMessage(event: MessageEvent<{ type: IncomingEvent; data: unknown }>) {
switch (event.data.type) {
Expand All @@ -61,6 +70,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);
Expand Down Expand Up @@ -168,13 +185,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) {
Expand All @@ -189,19 +201,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);
}}
>
<img
Expand Down Expand Up @@ -415,7 +416,6 @@
class="btn btn-ghost btn-circle hover:bg-[rgb(220,220,220)]"
onclick={() => {
intervalMs = intervalMs * 2;
sendEvent("timer_change", intervalMs);
}}
>
<img class="size-6" src={icons.decelerate} alt="decelerate" />
Expand All @@ -425,7 +425,6 @@
class="btn btn-ghost btn-circle text-black hover:bg-[rgb(220,220,220)]"
onclick={() => {
intervalMs = 1000;
sendEvent("timer_change", intervalMs);
}}
>
x1
Expand All @@ -435,7 +434,6 @@
class="btn btn-ghost btn-circle hover:bg-[rgb(220,220,220)]"
onclick={() => {
intervalMs = intervalMs / 2;
sendEvent("timer_change", intervalMs);
}}
>
<img class="size-6" src={icons.accelerate} alt="accelerate" />
Expand All @@ -455,8 +453,13 @@
<button
class="btn btn-ghost btn-circle hover:bg-[rgb(220,220,220)] swap"
onclick={() => {
const eventName = isProgress ? "pause" : "play";
sendEvent(eventName);
if (isProgress) {
timer = "stopped";
sendEvent("pause");
} else {
timer = "running";
sendEvent("play");
}
isProgress = !isProgress;
}}
>
Expand All @@ -477,6 +480,7 @@
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
onclick={() => {
isProgress = false;
timer = "stopped";
sendEvent("pause");
sendEvent("save_board");
}}
Expand All @@ -488,6 +492,7 @@
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
onclick={() => {
isProgress = false;
timer = "stopped";
sendEvent("pause");
handleLoad();
}}
Expand All @@ -499,6 +504,8 @@
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
onclick={() => {
isProgress = false;
timer = "stopped";
sendEvent("pause");
sendEvent("board_reset");
}}
>
Expand All @@ -509,6 +516,8 @@
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
onclick={() => {
isProgress = false;
timer = "stopped";
sendEvent("pause");
sendEvent("board_randomize");
}}
>
Expand All @@ -522,6 +531,8 @@
class="btn btn-ghost hover:bg-[rgb(220,220,220)] text-black"
onclick={() => {
appliedCode = editingCode;
isProgress = false;
timer = "stopped";
}}
>
{isJapanese ? "コードを適用" : "Apply Code"}
Expand Down