Skip to content

Commit 8c49888

Browse files
authored
Merge pull request #37 from ut-code/code_template
Code template: selector
2 parents 1c29782 + 2bafdae commit 8c49888

File tree

4 files changed

+712
-1
lines changed

4 files changed

+712
-1
lines changed
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
"use strict";
2+
3+
let timer = "stop";
4+
let generationFigure = 0;
5+
let isDragging = false;
6+
let dragMode = 0; // 1: 黒にする, 0: 白にする
7+
let isPlacingTemplate = false;
8+
let patternShape = [];
9+
let patternHeight = 0;
10+
let patternWidth = 0;
11+
let previewCells = [];
12+
13+
//変数設定
14+
let boardSize = 20; //盤面の大きさ(20x20)
15+
const cellSize = 600 / boardSize; //セルの大きさ(px)
16+
const maxLifespan = 2; // セルの最大寿命
17+
18+
// around: 周囲の生きたセル数, currentLifespan: 現在の寿命
19+
function getNextLifespan(around, currentLifespan) {
20+
// セルが生きている場合
21+
if (currentLifespan > 0) {
22+
// 周囲が 2 か 3 なら寿命を維持(減らさない)
23+
if (2 <= around && around <= 3) {
24+
return currentLifespan;
25+
}
26+
// 条件を満たさない場合は寿命を減らす
27+
return currentLifespan - 1;
28+
}
29+
30+
// セルが死んでいる場合
31+
// 周囲が 3 なら誕生
32+
if (around === 3) {
33+
return maxLifespan;
34+
}
35+
36+
return 0;
37+
}
38+
39+
// cellの状態に応じた色を返す関数
40+
function getStyle(cell, lifespan) {
41+
if (cell === 0) return "white";
42+
43+
// cellの値に応じて色を返す場合はここに追加
44+
if (lifespan === 2) return "black";
45+
if (lifespan === 1) return "gray";
46+
if (lifespan === 0) return "white";
47+
48+
return "black"; // デフォルトは黒
49+
}
50+
51+
//Boardの初期化
52+
let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => 0));
53+
const table = document.getElementById("game-board");
54+
55+
//盤面をBoardに従って変更する関数達(Boardを変更したら実行する)
56+
function renderBoard() {
57+
// 初回の盤面生成
58+
table.innerHTML = "";
59+
for (let i = 0; i < boardSize; i++) {
60+
const tr = document.createElement("tr");
61+
tr.style.padding = "0";
62+
for (let j = 0; j < boardSize; j++) {
63+
const td = document.createElement("td");
64+
td.style.padding = "0";
65+
const button = document.createElement("button");
66+
button.lifespan = board[i][j] ? maxLifespan : 0; // セルの寿命を設定
67+
button.style.backgroundColor = getStyle(board[i][j], button.lifespan); //Boardの対応する値によって色を変更
68+
69+
// ボードが大きいときは border をつけない
70+
if (boardSize >= 50) {
71+
button.style.border = "none";
72+
table.style.border = "1px solid black";
73+
} else {
74+
button.style.border = "0.5px solid black";
75+
}
76+
button.style.width = `${cellSize}px`;
77+
button.style.height = `${cellSize}px`;
78+
button.style.padding = "0"; //cellSizeが小さいとき、セルが横長になることを防ぐ
79+
button.style.display = "block"; //cellSizeが小さいとき、行間が空きすぎるのを防ぐ
80+
81+
button.onclick = () => {
82+
if (isPlacingTemplate) {
83+
clearPreview();
84+
isPlacingTemplate = false;
85+
if (i + patternHeight < boardSize + 1 && j + patternWidth < boardSize + 1) {
86+
for (let r = 0; r < patternHeight; r++) {
87+
for (let c = 0; c < patternWidth; c++) {
88+
const boardRow = i + r;
89+
const boardCol = j + c;
90+
board[boardRow][boardCol] = patternShape[r][c];
91+
// パターン配置時に寿命を設定
92+
if (patternShape[r][c]) {
93+
const btn = table.children[boardRow].children[boardCol].children[0];
94+
btn.lifespan = maxLifespan;
95+
}
96+
}
97+
}
98+
rerender();
99+
generationChange(0);
100+
stop();
101+
} else {
102+
window.parent.postMessage(
103+
{
104+
type: "Size shortage",
105+
data: {},
106+
},
107+
"*",
108+
);
109+
}
110+
}
111+
};
112+
button.onmousedown = (e) => {
113+
e.preventDefault();
114+
if (timer === "stop" && !isPlacingTemplate) {
115+
isDragging = true;
116+
board[i][j] = !board[i][j];
117+
dragMode = board[i][j];
118+
button.lifespan = board[i][j] ? maxLifespan : 0;
119+
button.style.backgroundColor = getStyle(board[i][j], button.lifespan);
120+
}
121+
};
122+
button.onmouseenter = () => {
123+
if (isDragging && timer === "stop" && board[i][j] !== dragMode && !isPlacingTemplate) {
124+
board[i][j] = dragMode;
125+
button.lifespan = board[i][j] ? maxLifespan : 0;
126+
button.style.backgroundColor = getStyle(board[i][j], button.lifespan);
127+
}
128+
if (isPlacingTemplate) {
129+
drawPreview(i, j);
130+
}
131+
};
132+
td.appendChild(button);
133+
tr.appendChild(td);
134+
}
135+
table.appendChild(tr);
136+
}
137+
}
138+
139+
table.onmouseleave = () => {
140+
if (isPlacingTemplate) {
141+
clearPreview();
142+
}
143+
};
144+
145+
function drawPreview(row, col) {
146+
clearPreview();
147+
for (let r = 0; r < patternHeight; r++) {
148+
for (let c = 0; c < patternWidth; c++) {
149+
if (patternShape[r][c] === 1) {
150+
const boardRow = row + r;
151+
const boardCol = col + c;
152+
if (boardRow < boardSize && boardCol < boardSize) {
153+
const cell = table.rows[boardRow].cells[boardCol].firstChild;
154+
cell.style.backgroundColor = "gray";
155+
previewCells.push({ row: boardRow, col: boardCol });
156+
}
157+
}
158+
}
159+
}
160+
}
161+
162+
function clearPreview() {
163+
previewCells.forEach((cellPos) => {
164+
const cell = table.rows[cellPos.row].cells[cellPos.col].firstChild;
165+
const lifespan = cell.lifespan || 0;
166+
cell.style.backgroundColor = getStyle(board[cellPos.row][cellPos.col], lifespan);
167+
});
168+
previewCells = [];
169+
}
170+
171+
function rerender() {
172+
// 2回目以降の盤面生成
173+
for (let i = 0; i < boardSize; i++) {
174+
for (let j = 0; j < boardSize; j++) {
175+
const button = table.children[i].children[j].children[0];
176+
177+
// 色の更新
178+
const currentCellColor = button.style.backgroundColor;
179+
const expectedCellColor = getStyle(board[i][j], button.lifespan);
180+
if (currentCellColor !== expectedCellColor) {
181+
button.style.backgroundColor = expectedCellColor;
182+
}
183+
184+
// セルサイズの更新
185+
const currentCellsize = button.style.width;
186+
const expectedCellsize = `${cellSize}px`;
187+
if (currentCellsize !== expectedCellsize) {
188+
button.style.width = expectedCellsize;
189+
button.style.height = expectedCellsize;
190+
}
191+
}
192+
}
193+
}
194+
195+
document.addEventListener("mouseup", () => {
196+
isDragging = false;
197+
});
198+
199+
renderBoard();
200+
progressBoard();
201+
202+
function generationChange(num) {
203+
//現在の世代を表すgenerationFigureを変更し、文章も変更
204+
generationFigure = num;
205+
window.parent.postMessage(
206+
{
207+
type: "generation_change",
208+
data: generationFigure,
209+
},
210+
"*",
211+
);
212+
}
213+
214+
function progressBoard() {
215+
const newLifespans = Array(boardSize)
216+
.fill(0)
217+
.map(() => Array(boardSize).fill(0));
218+
219+
for (let i = 0; i < boardSize; i++) {
220+
for (let j = 0; j < boardSize; j++) {
221+
//周囲のマスに黒マスが何個あるかを計算(aroundに格納)↓
222+
let around = 0;
223+
let tate, yoko;
224+
if (i === 0) {
225+
tate = [0, 1];
226+
} else if (i === boardSize - 1) {
227+
tate = [0, -1];
228+
} else {
229+
tate = [-1, 0, 1];
230+
}
231+
if (j === 0) {
232+
yoko = [0, 1];
233+
} else if (j === boardSize - 1) {
234+
yoko = [0, -1];
235+
} else {
236+
yoko = [-1, 0, 1];
237+
}
238+
for (let ii = 0; ii < tate.length; ii++) {
239+
for (let jj = 0; jj < yoko.length; jj++) {
240+
if (tate[ii] !== 0 || yoko[jj] !== 0) {
241+
const neighborButton = table.children[i + tate[ii]].children[j + yoko[jj]].children[0];
242+
around += neighborButton.lifespan > 0 ? 1 : 0;
243+
}
244+
}
245+
}
246+
//↑周囲のマスに黒マスが何個あるかを計算(aroundに格納)
247+
248+
// 前の世代の寿命をボタンから取得し、新しい寿命を計算して保存
249+
const button = table.children[i].children[j].children[0];
250+
const currentLifespan = button.lifespan;
251+
newLifespans[i][j] = getNextLifespan(around, currentLifespan);
252+
}
253+
}
254+
255+
// 新しい寿命を適用
256+
for (let i = 0; i < boardSize; i++) {
257+
for (let j = 0; j < boardSize; j++) {
258+
const button = table.children[i].children[j].children[0];
259+
button.lifespan = newLifespans[i][j];
260+
board[i][j] = button.lifespan > 0 ? 1 : 0;
261+
}
262+
}
263+
264+
generationChange(generationFigure + 1);
265+
rerender();
266+
}
267+
268+
//イベント
269+
270+
on.progress = () => {
271+
progressBoard();
272+
};
273+
274+
on.play = () => {
275+
timer = "start";
276+
};
277+
278+
on.pause = () => {
279+
timer = "stop";
280+
};
281+
282+
on.board_reset = () => {
283+
//すべて白にBoardを変更
284+
board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => 0));
285+
renderBoard();
286+
generationChange(0);
287+
};
288+
289+
on.board_randomize = () => {
290+
//白黒ランダムにBoardを変更
291+
board = Array.from({ length: boardSize }, () =>
292+
Array.from({ length: boardSize }, () => (Math.random() > 0.5 ? 1 : 0)),
293+
);
294+
renderBoard();
295+
generationChange(0);
296+
};
297+
298+
on.request_sync = () => {
299+
window.parent.postMessage(
300+
{
301+
type: "sync",
302+
data: {
303+
generationFigure: generationFigure,
304+
boardSize: boardSize,
305+
},
306+
},
307+
"*",
308+
);
309+
console.log("generationFigure:", generationFigure, "boardSize:", boardSize);
310+
};
311+
312+
on.place_template = (template) => {
313+
patternShape = template;
314+
patternHeight = patternShape.length;
315+
patternWidth = patternShape[0].length;
316+
isPlacingTemplate = true;
317+
table.style.cursor = "crosshair";
318+
stop();
319+
};
320+
321+
on.save_board = async () => {
322+
window.parent.postMessage({ type: "save_board", data: board }, "*");
323+
};
324+
325+
on.apply_board = (newBoard) => {
326+
board = newBoard;
327+
renderBoard();
328+
generationChange(0);
329+
stop();
330+
};

0 commit comments

Comments
 (0)