Skip to content

Commit 0bf5977

Browse files
authored
Merge pull request #46 from ut-code/colorful
Code Template: Colorful
2 parents a0a0864 + 34482f7 commit 0bf5977

File tree

16 files changed

+531
-21
lines changed

16 files changed

+531
-21
lines changed

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ model Board {
2222
name String
2323
preview Json
2424
code Json
25+
isColorful Boolean
2526
}
2627

2728
model Code {

src/iframe/life-game.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ let patternShape = [];
88
let patternHeight = 0;
99
let patternWidth = 0;
1010
let previewCells = [];
11+
let isColorful = false;
1112

1213
//盤面の大きさ
1314
const boardSize = 20;
@@ -262,11 +263,23 @@ on.place_template = (template) => {
262263
};
263264

264265
on.save_board = async () => {
265-
window.parent.postMessage({ type: "save_board", data: board }, "*");
266+
window.parent.postMessage(
267+
{
268+
type: "save_board",
269+
data: {
270+
board: board,
271+
isColorful: isColorful,
272+
},
273+
},
274+
"*",
275+
);
266276
};
267-
268277
on.apply_board = (newBoard) => {
269278
board = newBoard;
270279
renderBoard();
271280
generationChange(0);
272281
};
282+
283+
on.request_colorful_status = () => {
284+
window.parent.postMessage({ type: "colorful_status", data: isColorful }, "*");
285+
};

src/lib/api/board.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { toast } from "$lib/models/ToastStore.svelte";
22

33
export async function saveBoard(
4-
data: { board: number[][]; name: string; code: string },
4+
data: { board: number[][]; name: string; code: string; isColorful: boolean },
55
isJapanese: boolean,
66
) {
77
try {
@@ -67,6 +67,7 @@ export async function fetchBoardList(isJapanese: boolean): Promise<BoardListItem
6767
export type LoadedBoardData = {
6868
board: number[][];
6969
code: string;
70+
isColorful: boolean;
7071
};
7172

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

0 commit comments

Comments
 (0)