|
| 1 | +"use strict"; |
| 2 | + |
| 3 | +let generationFigure = 0; |
| 4 | +let currentRow = 0; |
| 5 | + |
| 6 | +//盤面の大きさ |
| 7 | +const boardSize = 201; //奇数に設定する |
| 8 | +const cellSize = 450 / boardSize; |
| 9 | +const centerline = (boardSize - 1) / 2; |
| 10 | + |
| 11 | +//セルの色 |
| 12 | +const aliveCellColor = "black"; |
| 13 | +const deadCellColor = "white"; |
| 14 | + |
| 15 | +//セルの生存条件(誕生条件) |
| 16 | +//今回は0から7までのパターンのうち 4, 3, 2, 1 が生存条件なので、2^4 + 2^3 + 2^2 + 2^1 = 30 から ルール30と呼ばれる。 |
| 17 | +//他にルール184(7,5,4,3)や、ルール110(6,5,3,2,1)、ルール90(6,4,3,1)などが有名なので試してみましょう |
| 18 | + |
| 19 | +// 生存パターン(2進法)を整数(10進法)で定義 |
| 20 | +const aliveRules = new Set([4, 3, 2, 1]); |
| 21 | +function isNextAlive(left, center, right) { |
| 22 | + // ビットシフトで3つの数値を1つの整数にする |
| 23 | + // 例: 1, 0, 0 => (1<<2) | (0<<1) | 0 => 4 |
| 24 | + const pattern = (left << 2) | (center << 1) | right; |
| 25 | + // セットに含まれているか確認(1 or 0 を返す) |
| 26 | + return aliveRules.has(pattern) ? 1 : 0; |
| 27 | +} |
| 28 | + |
| 29 | +function getStyle(cell) { |
| 30 | + if (cell === 0) return deadCellColor; |
| 31 | + return aliveCellColor; |
| 32 | +} |
| 33 | + |
| 34 | +let board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => 0)); |
| 35 | +board[0][centerline] = 1; //最上段中央のセルだけ生きたセルに変更 |
| 36 | +const table = document.getElementById("game-board"); |
| 37 | + |
| 38 | +function renderBoard() { |
| 39 | + document.body.style.display = "flex"; |
| 40 | + document.body.style.justifyContent = "center"; |
| 41 | + document.body.style.alignItems = "center"; |
| 42 | + document.body.style.minHeight = "100vh"; |
| 43 | + document.body.style.margin = "0"; |
| 44 | + document.body.style.padding = "0"; |
| 45 | + |
| 46 | + table.innerHTML = ""; |
| 47 | + for (let i = 0; i < boardSize; i++) { |
| 48 | + const tr = document.createElement("tr"); |
| 49 | + tr.style.padding = "0"; |
| 50 | + for (let j = 0; j < boardSize; j++) { |
| 51 | + const td = document.createElement("td"); |
| 52 | + td.style.padding = "0"; |
| 53 | + const button = document.createElement("button"); |
| 54 | + button.style.backgroundColor = board[i][j] ? aliveCellColor : deadCellColor; |
| 55 | + if (boardSize >= 50) { |
| 56 | + button.style.border = "none"; |
| 57 | + table.style.border = "1px solid black"; |
| 58 | + } else { |
| 59 | + button.style.border = "0.5px solid black"; |
| 60 | + } |
| 61 | + button.style.width = `${cellSize}px`; |
| 62 | + button.style.height = `${cellSize}px`; |
| 63 | + button.style.padding = "0"; |
| 64 | + button.style.display = "block"; |
| 65 | + td.appendChild(button); |
| 66 | + tr.appendChild(td); |
| 67 | + } |
| 68 | + table.appendChild(tr); |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +function rerender() { |
| 73 | + // 2回目以降の盤面生成は一行ずつ行う |
| 74 | + if (currentRow >= boardSize) return; |
| 75 | + for (let j = 0; j < boardSize; j++) { |
| 76 | + const button = table.children[currentRow].children[j].children[0]; |
| 77 | + const currentCellColor = button.style.backgroundColor; |
| 78 | + const expectedCellColor = getStyle(board[currentRow][j]); |
| 79 | + if (currentCellColor !== expectedCellColor) { |
| 80 | + button.style.backgroundColor = expectedCellColor; |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +renderBoard(); |
| 86 | + |
| 87 | +function generationChange(num) { |
| 88 | + generationFigure = num; |
| 89 | + window.parent.postMessage({ type: "generation_change", data: generationFigure }, "*"); |
| 90 | +} |
| 91 | + |
| 92 | +function progressBoard() { |
| 93 | + if (currentRow >= boardSize - 1) return; |
| 94 | + const newBoard = structuredClone(board); |
| 95 | + //セルの計算は一行ずつ行う |
| 96 | + for (let j = 1; j < boardSize - 1; j++) { |
| 97 | + const left = board[currentRow][j - 1]; |
| 98 | + const center = board[currentRow][j]; |
| 99 | + const right = board[currentRow][j + 1]; |
| 100 | + newBoard[currentRow + 1][j] = isNextAlive(left, center, right); |
| 101 | + } |
| 102 | + board = newBoard; |
| 103 | + currentRow += 1; |
| 104 | + generationChange(generationFigure + 1); |
| 105 | + rerender(); |
| 106 | +} |
| 107 | + |
| 108 | +on.progress = () => { |
| 109 | + progressBoard(); |
| 110 | +}; |
| 111 | + |
| 112 | +on.board_reset = () => { |
| 113 | + board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => 0)); |
| 114 | + board[0][centerline] = 1; //初期状態のドットを再配置 |
| 115 | + currentRow = 0; //行もリセット |
| 116 | + renderBoard(); |
| 117 | + generationChange(0); |
| 118 | +}; |
| 119 | + |
| 120 | +on.board_randomize = () => { |
| 121 | + board = Array.from({ length: boardSize }, () => Array.from({ length: boardSize }, () => 0)); |
| 122 | + for (let j = 0; j < boardSize; j++) { |
| 123 | + board[0][j] = Math.random() > 0.5 ? 1 : 0; //最初の行をランダムにする |
| 124 | + } |
| 125 | + currentRow = 0; |
| 126 | + renderBoard(); |
| 127 | + generationChange(0); |
| 128 | +}; |
| 129 | + |
| 130 | +on.save_board = async () => { |
| 131 | + window.parent.postMessage({ type: "save_board", data: board }, "*"); |
| 132 | +}; |
0 commit comments