270 lines
7.3 KiB
Plaintext
270 lines
7.3 KiB
Plaintext
// 🧬 Conway's Game of Life - Canvas版視覚化
|
||
// セルラーオートマトンの美しい可視化
|
||
|
||
print("🧬 === Conway's Game of Life - Canvas Edition ===")
|
||
|
||
DEBUG = new DebugBox()
|
||
DEBUG.startTracking()
|
||
|
||
// 🔬 セルBox - 各セルが独立した生命体
|
||
box CellBox {
|
||
init { alive, nextState, x, y }
|
||
|
||
CellBox(posX, posY, initialState) {
|
||
me.alive = initialState
|
||
me.nextState = false
|
||
me.x = posX
|
||
me.y = posY
|
||
}
|
||
|
||
setNextState(state) {
|
||
me.nextState = state
|
||
}
|
||
|
||
update() {
|
||
me.alive = me.nextState
|
||
}
|
||
|
||
isAlive() {
|
||
return me.alive
|
||
}
|
||
|
||
draw(canvas, cellSize) {
|
||
color = "black"
|
||
if me.alive {
|
||
color = "lime"
|
||
}
|
||
|
||
canvas.setFillStyle(color)
|
||
canvas.fillRect(me.x * cellSize, me.y * cellSize, cellSize, cellSize)
|
||
|
||
// グリッド線
|
||
canvas.setStrokeStyle("gray")
|
||
canvas.strokeRect(me.x * cellSize, me.y * cellSize, cellSize, cellSize)
|
||
}
|
||
}
|
||
|
||
// 🌍 Game of Life世界Box
|
||
box GameOfLifeBox {
|
||
init { grid, width, height, canvas, cellSize, generation }
|
||
|
||
GameOfLifeBox(canvasId, gridWidth, gridHeight, pixelCellSize) {
|
||
me.width = gridWidth
|
||
me.height = gridHeight
|
||
me.cellSize = pixelCellSize
|
||
me.generation = 0
|
||
|
||
// Canvas初期化
|
||
canvasWidth = gridWidth * pixelCellSize
|
||
canvasHeight = gridHeight * pixelCellSize
|
||
me.canvas = new WebCanvasBox(canvasId, canvasWidth, canvasHeight)
|
||
|
||
// グリッド初期化
|
||
me.grid = new ArrayBox()
|
||
y = 0
|
||
loop (y < me.height) {
|
||
row = new ArrayBox()
|
||
x = 0
|
||
loop (x < me.width) {
|
||
cell = new CellBox(x, y, false)
|
||
row.add(cell)
|
||
x = x + 1
|
||
}
|
||
me.grid.add(row)
|
||
y = y + 1
|
||
}
|
||
|
||
DEBUG.trackBox(me, "GameOfLifeWorld")
|
||
}
|
||
|
||
// 指定位置のセルを取得
|
||
getCell(x, y) {
|
||
if x >= 0 and x < me.width and y >= 0 and y < me.height {
|
||
row = me.grid.get(y)
|
||
return row.get(x)
|
||
}
|
||
return new CellBox(-1, -1, false) // 境界外は死んだセル
|
||
}
|
||
|
||
// 近傍の生きているセル数をカウント
|
||
countLiveNeighbors(x, y) {
|
||
count = 0
|
||
|
||
// 8近傍をチェック
|
||
dy = -1
|
||
loop (dy <= 1) {
|
||
dx = -1
|
||
loop (dx <= 1) {
|
||
if not (dx == 0 and dy == 0) { // 自分自身は除外
|
||
neighbor = me.getCell(x + dx, y + dy)
|
||
if neighbor.isAlive() {
|
||
count = count + 1
|
||
}
|
||
}
|
||
dx = dx + 1
|
||
}
|
||
dy = dy + 1
|
||
}
|
||
|
||
return count
|
||
}
|
||
|
||
// Conway's Game of Lifeルールの適用
|
||
applyRules() {
|
||
// 次世代の状態を計算
|
||
y = 0
|
||
loop (y < me.height) {
|
||
x = 0
|
||
loop (x < me.width) {
|
||
cell = me.getCell(x, y)
|
||
liveNeighbors = me.countLiveNeighbors(x, y)
|
||
|
||
newState = false
|
||
|
||
if cell.isAlive() {
|
||
// 生きているセルのルール
|
||
if liveNeighbors == 2 or liveNeighbors == 3 {
|
||
newState = true // 生存
|
||
}
|
||
// それ以外は死滅(過疎・過密)
|
||
} else {
|
||
// 死んでいるセルのルール
|
||
if liveNeighbors == 3 {
|
||
newState = true // 誕生
|
||
}
|
||
}
|
||
|
||
cell.setNextState(newState)
|
||
x = x + 1
|
||
}
|
||
y = y + 1
|
||
}
|
||
|
||
// 状態を一斉更新
|
||
y = 0
|
||
loop (y < me.height) {
|
||
x = 0
|
||
loop (x < me.width) {
|
||
cell = me.getCell(x, y)
|
||
cell.update()
|
||
x = x + 1
|
||
}
|
||
y = y + 1
|
||
}
|
||
|
||
me.generation = me.generation + 1
|
||
}
|
||
|
||
// パターン設定
|
||
setPattern(pattern) {
|
||
if pattern == "glider" {
|
||
// グライダーパターン
|
||
me.getCell(1, 0).alive = true
|
||
me.getCell(2, 1).alive = true
|
||
me.getCell(0, 2).alive = true
|
||
me.getCell(1, 2).alive = true
|
||
me.getCell(2, 2).alive = true
|
||
}
|
||
|
||
if pattern == "blinker" {
|
||
// 点滅パターン
|
||
centerX = me.width / 2
|
||
centerY = me.height / 2
|
||
me.getCell(centerX - 1, centerY).alive = true
|
||
me.getCell(centerX, centerY).alive = true
|
||
me.getCell(centerX + 1, centerY).alive = true
|
||
}
|
||
|
||
if pattern == "random" {
|
||
// ランダムパターン
|
||
random = new RandomBox()
|
||
y = 0
|
||
loop (y < me.height) {
|
||
x = 0
|
||
loop (x < me.width) {
|
||
if random.float() < 0.3 { // 30%の確率で生存
|
||
me.getCell(x, y).alive = true
|
||
}
|
||
x = x + 1
|
||
}
|
||
y = y + 1
|
||
}
|
||
}
|
||
}
|
||
|
||
// 描画
|
||
render() {
|
||
// 背景クリア
|
||
me.canvas.setFillStyle("white")
|
||
me.canvas.fillRect(0, 0, me.canvas.width, me.canvas.height)
|
||
|
||
// 全セル描画
|
||
y = 0
|
||
loop (y < me.height) {
|
||
x = 0
|
||
loop (x < me.width) {
|
||
cell = me.getCell(x, y)
|
||
cell.draw(me.canvas, me.cellSize)
|
||
x = x + 1
|
||
}
|
||
y = y + 1
|
||
}
|
||
|
||
// 情報表示
|
||
me.canvas.setFillStyle("blue")
|
||
me.canvas.fillText("Generation: " + me.generation, 10, 20)
|
||
me.canvas.fillText("Conway's Game of Life", 10, 40)
|
||
}
|
||
|
||
// 生きているセルの総数
|
||
countAliveCells() {
|
||
count = 0
|
||
y = 0
|
||
loop (y < me.height) {
|
||
x = 0
|
||
loop (x < me.width) {
|
||
if me.getCell(x, y).isAlive() {
|
||
count = count + 1
|
||
}
|
||
x = x + 1
|
||
}
|
||
y = y + 1
|
||
}
|
||
return count
|
||
}
|
||
}
|
||
|
||
// 🚀 Game of Life開始!
|
||
print("Creating Conway's Game of Life world...")
|
||
game = new GameOfLifeBox("life-canvas", 40, 30, 10)
|
||
|
||
// パターン設定
|
||
print("🧬 Setting up initial patterns...")
|
||
game.setPattern("random")
|
||
|
||
// シミュレーション実行
|
||
print("🌱 Running life simulation...")
|
||
generation = 0
|
||
loop (generation < 50) {
|
||
game.render()
|
||
aliveCount = game.countAliveCells()
|
||
|
||
if generation % 10 == 0 {
|
||
print("Generation " + generation + ": " + aliveCount + " cells alive")
|
||
}
|
||
|
||
game.applyRules()
|
||
generation = generation + 1
|
||
}
|
||
|
||
// 最終統計
|
||
finalAlive = game.countAliveCells()
|
||
print("🏁 Simulation complete!")
|
||
print("Final generation: " + game.generation)
|
||
print("Final alive cells: " + finalAlive)
|
||
print(DEBUG.memoryReport())
|
||
|
||
print("✨ Conway's Game of Life - Everything is Box!")
|
||
print("🔬 Each cell is an independent CellBox")
|
||
print("🌍 The world itself is a GameOfLifeBox")
|
||
print("🎨 Beautiful visualization through WebCanvasBox") |