森林火災のシミュレーション 5, forestFire()
これまでに作った函数を全部まとめて forestFire() 函数をつくった。
上段が nspire 側で用意した初代行列。
左下が、初代行列に色づけしたもの。
右下が、lua から返された次世代行列に色づけしたもの(緑が樹木、赤が発火、灰色が灰、黄色が発火準備状態)。
require "color" -- nspire 側で用意した行列 "mat" を lua 側の変数 mat に読み込む。 local mat = var.recall("mat") ----------------- ルール行列と発火レベル範囲とを定義する ------------------- local matRule = {{0,0,0,0,0,0,0,0,0,color.brown},--レベル 0、空き地、茶 {1,2,2,2,2,3,3,3,3,color.green},--レベル 1、樹木、緑 {2,3,3,4,4,5,5,5,5,color.yellow},--レベル 2、発火準備、黄 {3,4,4,5,5,5,5,5,5,color.yellow},--レベル 3、発火準備、黄 {4,5,5,5,5,5,5,5,5,color.yellow},--レベル 4、発火準備、黄 {6,6,6,6,6,6,6,6,6,6,color.red},--レベル 5、発火、赤 {7,7,7,7,7,7,7,7,7,7,color.red},--レベル 6、発火、赤 {8,8,8,8,8,8,8,8,8,8,color.red},--レベル 7、発火、赤 {9,9,9,9,9,9,9,9,9,9,color.red},--レベル 8、発火、赤 {9,9,9,9,9,9,9,9,9,color.gray}}--レベル 9、木炭、灰 local fireRange = {lower = 5, upper = 8} ---------------------------------------------------------------------------- -- 現世代行列と次世代行列とを確認する。 function on.paint(gc) local temp = forestFire(mat, matRule) -- 次世代行列を計算する drawCell(mat, 5, 5, 17, matRule, true, gc) -- 現世代行列を表示する drawCell(temp, 130, 5, 17, matRule, true, gc) -- 次世代行列を表示する end -- 現世代行列を読み込んで次世代行列を出力する(現世代行列, ルール行列) function forestFire(matCurrent, matRule) local temp = countFire(matCurrent, fireRange.lower, fireRange.upper) local matNew = matNext(matCurrent, temp, matRule) return matNew end -- セルを塗り分ける部分函数(行列, 原点座標, 1 辺の長さ, ルール行列, 数字を表示する/しない, gc) function drawCell(mat, x0, y0, sideLen, matRule, stringOn, gc) for r = 1, #mat do for c = 1, #mat[r] do gc:setColorRGB(matRule[mat[r][c] + 1][#matRule[mat[r][c] + 1]]) --[[ルール行列の各行の末尾に指定した色を読み取る。--]] gc:fillRect(x0 + (c - 1) * sideLen, y0 + (r - 1) * sideLen, sideLen, sideLen) if stringOn then --[[行列の要素を表示する。--]] gc:setFont("serif", "r", 7) gc:setColorRGB(color.black) gc:drawString(mat[r][c], x0 + (c - 1) * sideLen, y0 + (r - 1) * sideLen, "top") end end end end -- ルール行列とカウント行列とに従って現世代行列を書き換える部分函数(現世代行列, カウント行列, ルール行列) function matNext(matNow, matCount, matRule) local matNext = newMat(#matNow, #matNow[1]) for r = 1, #matNow do for c = 1, #matNow[1] do matNext[r][c] = matRule[matNow[r][c] + 1][matCount[r][c] + 1] end end return matNext end -- 注目セルとそのムーア近傍と合計 9 セルに含まれている発火セルの数を数える部分函数(行列, 下限, 上限) function countFire(mat, boundLo, boundHi) local matReturn = newMat(#mat, #mat[1]) local matTemp = newMat(#mat + 2, #mat[1] + 2) for r = 2, #matTemp - 1 do for c = 2, #matTemp[1] - 1 do matTemp[r][c] = mat[r-1][c-1] end end for r = 2, #matTemp - 1 do for c = 2, #matTemp[1] - 1 do local matSub = subMat(matTemp, r-1,c-1,r+1,c+1) matSub = mat2list(matSub) local n = countIf(matSub, boundLo, true, boundHi, true) matReturn[r-1][c-1] = n end end return matReturn end -- 空行列函数(行数, 列数) function newMat(rowDim, colDim) local mat = {} for r = 1, rowDim do mat[r] = {} for c = 1, colDim do mat[r][c] = 0 end end return mat end -- 行列表示函数、(穴なし行列, 原点座標, 1 辺の長さ, フォントサイズ, gc) function drawMat(mat, x0, y0, sideLen, fontSize, gc) for r = 1, table.maxn(mat) do for c = 1, table.maxn(mat[r]) do gc:setFont("sansserif", "r", fontSize) gc:setColorRGB(0x000000) gc:drawString(mat[r][c], x0 + (c - 1) * sideLen, y0 + (r - 1) * sideLen, "top") end end end -- 範囲内の数字の個数を数える(穴なしリスト, 下限, 含む, 上限, 含む) function countIf(list, lowBound, lowIncl, upBound, upIncl) local n = 0 for i = 1, table.maxn(list) do if lowIncl == false and upIncl == false and lowBound < list[i] and list[i] < upBound then n = n + 1 --[[ 下限 < ? < 上限 --]] elseif lowIncl == false and upIncl == true and lowBound < list[i] and list[i] <= upBound then n = n + 1 --[[ 下限 < ? <= 上限 --]] elseif lowIncl == true and upIncl == false and lowBound <= list[i] and list[i] < upBound then n = n + 1 --[[ 下限 <= ? < 上限 --]] elseif lowIncl == true and upIncl == true and lowBound <= list[i] and list[i] <= upBound then n = n + 1 --[[ 下限 <= ? <= 上限 --]] end end return n end -- 行列の一部を行列として抜き出す(穴なし行列, 左上のどこから右下のどこまでを抜き出す) function subMat(mat, rowTop, colLeft, rowBottom, colRight) local matReturn = {} for r = 1, (rowBottom - rowTop + 1) do matReturn[r] = {} for c = 1, (colRight - colLeft + 1) do matReturn[r][c] = mat[rowTop + (r - 1)][colLeft + (c - 1)] end end return matReturn end -- 行列をリスト化する(穴なし行列) function mat2list(mat) local n = 1 local listReturn = {} for r = 1, table.maxn(mat) do for c = 1, table.maxn(mat[r]) do listReturn[n] = mat[r][c] n = n + 1 end end return listReturn end