game of life 9 of 9

ライフ・ゲームの初代行列をフリーハンドで設定する。


local n = 1 -- 世代番号の初期値
local stringPen = "PEN UP"
local stringEraser = "ERASER UP"
local posX, posY = 0, 0
local posC, posR = 1, 1 
local penDown = false 
local eraserDown = false
local rdim, cdim = 25,25 -- 行列のサイズを決める。
local sideLen = 8 -- セルの一辺の長さを決める。
local screenW, screenH = sideLen * cdim, sideLen * rdim -- 画面サイズを決める。 

-- 空行列を作る
local mat_now, mat_void = {}, {}
for r = 1, rdim do
   mat_now[r], mat_void[r] = {}, {} 
   for c = 1, cdim do
      mat_now[r][c], mat_void[r][c] = 0, 0
   end
end

-- 現世代行列を描画する。
function on.paint(gc)    
   gc:drawString(n, 250, 80, "top") -- 世代数を表示する。
   if     penDown    then mat_now[posR][posC] = 1 -- 行列に 1 を代入     
   elseif eraserDown then mat_now[posR][posC] = 0 -- 行列に 0 を代入
   end
   --[[ -- あとで再現できるよう、できた行列を nspire 側に保存しておく
   var.store("mat", mat_now)
   --]]
      
   -- 行列を描画する。
   for r = 1, rdim do
      for c = 1, cdim do
         if mat_now[r][c] == 1 then
            gc:setColorRGB(0,0,0)
            gc:fillRect((c - 1) * sideLen, (r - 1) * sideLen, sideLen, sideLen)
         end
         gc:setColorRGB(198,113,113)
         gc:setFont("serif", "r", 7)
        --[[ -- 確認のため行列の要素を表示する
        gc:drawString(mat_now[r][c], (c - 1) * sideLen, (r - 1) * sideLen, "top")
        --]]
      end
   end
   gc:drawRect((posC -1) * sideLen, (posR - 1) * sideLen, sideLen, sideLen) -- カーソルの位置に目印の枠を出す
   gc:setFont("serif", "r", 7)
   gc:setColorRGB(0,0,0)
   gc:drawString(stringPen, 230, 0) -- ペンの状態を表示する
   gc:drawString(stringEraser, 230, 15) -- 消しゴムの状態を表示する
   gc:drawString("x = "..posX, 230, 30) -- x 座標を表示する
   gc:drawString("y = "..posY, 275, 30) -- y 座標を表示する
   gc:drawString("Col = "..posC, 230, 45) -- 列番号を表示する
   gc:drawString("Row = "..posR, 275, 45) -- 行番号を表示する
end

function on.timer() -- タイマーが動いたら
   n = n + 1 -- 世代番号を 1 進める。
   mat_now = game_of_life(mat_now) -- 現世代行列を game_of_life 函数に読み込んで次世代行列を計算する。
   platform.window:invalidate()
end
function on.enterKey() -- enter キーでタイマーを開始する。 
   timer.start(0.01)
end
function on.escapeKey() -- esc キーでタイマーを停止する。 
   timer.stop()
   platform.window:invalidate()
end
function on.mouseDown() -- 左クリックでペンを下ろす
   stringPen = "PEN DOWN"
   penDown = true
   platform.window:invalidate()
end
function on.mouseUp()
   stringPen = "PEN UP"
   penDown = false
   platform.window:invalidate()
end
function on.rightMouseDown() --右クリックで消しゴムを下ろす
   eraserDown = true
   stringEraser = "ERASER DOWN"
   platform.window:invalidate()
end
function on.rightMouseUp()
   eraserDown = false
   stringEraser = "ERASER UP"
   platform.window:invalidate()
end
function on.mouseMove(x, y)
   posX, posY = x, y
   posC = math.floor(posX/(screenW/cdim)) + 1 -- x 座標を列番号にマッピングする。 
   posC = math.max(1, math.min(posC, cdim)) -- 列数をはみ出さないようにする。
   posR = math.floor(posY/(screenH/rdim)) + 1 -- y 座標を行番号にマッピングする。
   posR = math.max(1, math.min(posR, rdim)) -- 行数をはみ出さないようにする。
   platform.window:invalidate()
end

-- 現世代行列から次世代行列を求める部分プログラム
function game_of_life(mat_now)
   local mat_moore = mat_void

   -- ルール表を定義する。
   local mat_rule = {{0,0,0,1,0,0,0,0,0},  
                     {0,0,1,1,0,0,0,0,0}}
   
   -- ムーア近傍の和の行列を作る。
   for r = 1, rdim do 
      for c = 1, cdim do

         -- トーラス接続にする。
         local above, below, left, right 
         if r == 1    then above = rdim else above = r - 1 end
         if r == rdim then below = 1    else below = r + 1 end
         if c == 1    then left  = cdim else left  = c - 1 end
         if c == cdim then right = 1    else right = c + 1 end

         -- ムーア近傍の和を計算する。
         mat_moore[r][c] = mat_now[above][left] + mat_now[above][c] + mat_now[above][right] 
                         + mat_now[r][left]               +               mat_now[r][right]
                         + mat_now[below][left] + mat_now[below][c] + mat_now[below][right]
      end
   end
   local mat_next = mat_now
   
   -- 次世代行列を作る。
   for r = 1, rdim do 
      for c = 1, cdim do
         mat_next[r][c] = mat_rule[mat_now[r][c] + 1][mat_moore[r][c] + 1] 
      end
   end
   
   -- 次世代行列を返す。
   return mat_next 
end

つぎはぎ、つぎはぎしているので無駄が多い。