`ζ§y~ioZavBRj>G`3S]']>S}Wup8d%qy)6o;]AsVl>S 02TvͫgE#2Tv2Tv >?wqqDw~D~Gtt~GNqNNwzD~~DNDAz~Azw~DNqG~t~tGtGA~wztG~~NA~A~D~NqDA~GtNw~~wqwDDwAt~GNA~tGz~tG箧ztGtGNtGtGqJ~NqJ~qNqAAqNAN~J~~Az~A箧zNNJGtJNwtz~GwqwtGw~~Nq~ꮧzqqGG~ttqqGG~ttqꮧzqq~~~NqwtGwNwtz~GwNNJGtJAz~A箧zN~J~~AAqNAqNq~JqN~JtGqtGtGNtGz~tG箧z~tGAt~GNAqwDDwNw~~wA~GtD~NqDA~A~~N~tGA~wztGtG~G~t~DNqAzwAz~~DND~DwzNNqt~GN~GtDw~Dqqw)-- title: Cellular automaton game -- author: led3761 -- desc: Implementation of algorithms of cellular automata of different types -- site: led3761@live.com -- license: MIT License (change this to your license of choice) -- version: 0.1 -- script: lua bufMemoryAdr = 0x8000 tilesMemoryAdr4 = 0x04000*2 keys = {KEY_C = 3, KEY_G = 7, KEY_H = 8, KEY_P = 16, KEY_R = 18, KEY_S = 19, KEY_X = 24, KEY_Z = 26, KEY_SPACE = 48 } sprites = { {num = 0, widthN = 1, heightN = 1}, {num = 1, widthN = 1, heightN = 1}, {num = 2, widthN = 1, heightN = 1}, {num = 3, widthN = 1, heightN = 1}, {num = 4, widthN = 1, heightN = 1}, {num = 5, widthN = 1, heightN = 1}, {num = 6, widthN = 1, heightN = 1}, {num = 7, widthN = 1, heightN = 1}, {num = 8, widthN = 1, heightN = 1}, {num = 9, widthN = 1, heightN = 1}, {num = 10, widthN = 1, heightN = 1}, {num = 11, widthN = 1, heightN = 1}, {num = 12, widthN = 3, heightN = 3}, {num = 16, widthN = 2, heightN = 2}, {num = 18, widthN = 2, heightN = 2}, {num = 20, widthN = 5, heightN = 2} } rules = { {name = "B3/S23 (Conway's Game of Life)", b = {3}, s = {2,3}, g = 2}, {name = "B3/S12345 (Maze)", b = {3}, s = {1,2,3,4,5}, g = 2}, {name = "B3/S1234 (Mazectric)", b = {3}, s = {1,2,3,4}, g = 2}, {name = "B37/S12345 (Maze with mice)", b = {3,7}, s = {1,2,3,4,5}, g = 2}, {name = "B37/S1234 (Mazectric with mice)", b = {3,7}, s = {1,2,3,4}, g = 2}, {name = "B1/S012345678 (H-tree)", b = {1}, s = {0,1,2,3,4,5,6,7,8}, g = 2}, {name = "B35678/S5678 (Diameba)", b = {3,5,6,7,8}, s = {5,6,7,8}, g = 2}, {name = "B345/S4567 (Assimilation)", b = {3,4,5}, s = {4,5,6,7}, g = 2}, {name = "B2/S0 (Live free or die)", b = {2}, s = {0}, g = 2}, {name = "B2/S (N. seeds)", b = {2}, s = {}, g = 2}, {name = "B234/S (Persian Carpet)", b = {2,3,4}, s = {}, g = 2}, {name = "B3/S45678 (Corals)", b = {3}, s = {4,5,6,7,8}, g = 2}, {name = "B378/S235678 (Coagulation)", b = {3,7,8}, s = {2,3,5,6,7,8}, g = 2}, {name = "B45678/S5678 (Majority)", b = {4,5,6,7,8}, s = {5,6,7,8}, g = 2}, {name = "B4678/S35678 (Annealing)", b = {4,6,7,8}, s = {3,5,6,7,8}, g = 2}, {name = "B2/S0345 (Free Star)", b = {2}, s = {0,3,4,5}, g = 2}, {name = "B2/S0345/10 (Free Star Gen)", b = {2}, s = {0,3,4,5}, g = 10}, {name = "B2/S345/4 (Star Wars)", b = {2}, s = {3,4,5}, g = 4}, {name = "B25/S3467/6 (Worms)", b = {2,5}, s = {3,4,6,7}, g = 6}, {name = "B2/S3456/6 (Sticks)", b = {2}, s = {3,4,5,6}, g = 6}, {name = "B26/S345/5 (Transers I)", b = {2,6}, s = {3,4,5}, g = 5}, {name = "B26/S0345/6 (Transers II)", b = {2,6}, s = {0,3,4,5}, g = 6}, {name = "B37/S345/3 (Line at infinity)", b = {3,7}, s = {3,4,5}, g = 3}, {name = "B35/S3457/5 (Replicator)", b = {3,5}, s = {3,4,5,7}, g = 5}, {name = "B234/S2/5 (Spirals)", b = {2,3,4}, s = {2}, g = 5}, {name = "B23/S145678/8 (BelZhab Sediment)", b = {2,3}, s = {1,4,5,6,7,8}, g = 8}, {name = "B458/S012345/3 (Lines)", b = {4,5,8}, s = {0,1,2,3,4,5}, g = 3} } startPhrase = { "HELLO GAME LIFE", "DESIGNED BY LED3761", "REGULAR GRID OF CELLS", "CONWAY'S GAME OF LIFE", "YOU ARE AMAZING" } sim = {width = 240, height = 136, run = false, ruleIndx = 1, maxAge = 15, cellAgingRatio = maxAge, speedList = {10, 5, 1}, speedIndx = 3} sprite = {x = 64, y = 32, rotation = 0, num = 1, totalNum = #sprites} colors = {cell = 1, bg = 14, overlay = 15} game = {t = 0, compactMenu = true} function BOOT() cls(colors.bg) local phrase = math.random(#startPhrase) print(startPhrase[phrase] , 110, 64, colors.cell) end function TIC() game.t = game.t+1 local needDrawFrame = (game.t%sim.speedList[sim.speedIndx] == 0) local needDrawSprite = keyProcess(needDrawFrame) if needDrawFrame or not sim.run then if needDrawSprite and sim.run then vbank(1) cls() vbank(0) drawSprite(sprites[sprite.num].num, sprite.x, sprite.y, colors.cell, sprite.rotation, sprites[sprite.num].widthN, sprites[sprite.num].heightN) else vbank(1) cls() drawSprite(sprites[sprite.num].num, sprite.x, sprite.y, colors.overlay, sprite.rotation, sprites[sprite.num].widthN, sprites[sprite.num].heightN) vbank(0) end if not sim.run then drawOverlay() else lifeTic() end end end function keyProcess(needDrawFrame) local needDrawSprite = false -- Navigation button process if btn(0) and (needDrawFrame or not sim.run) then sprite.y=sprite.y-1 if sprite.y < 0 then sprite.y = sim.height-1 end needDrawSprite = true end if btn(1) and (needDrawFrame or not sim.run) then sprite.y=sprite.y+1 if sprite.y > sim.height-1 then sprite.y = 0 end needDrawSprite = true end if btn(2) and (needDrawFrame or not sim.run) then sprite.x=sprite.x-1 if sprite.x < 0 then sprite.x = sim.width-1 end needDrawSprite = true end if btn(3) and (needDrawFrame or not sim.run) then sprite.x=sprite.x+1 if sprite.x > sim.width-1 then sprite.x = 0 end needDrawSprite = true end -- Control button process if keyp(keys.KEY_SPACE) then sim.run = not sim.run elseif keyp(keys.KEY_Z) then sprite.num = sprite.num+1 sprite.rotation = 0 if sprite.num > sprite.totalNum then sprite.num = 1 end elseif keyp(keys.KEY_X) then sprite.rotation = sprite.rotation+90 if sprite.rotation > 270 then sprite.rotation = 0 end elseif keyp(keys.KEY_S) then sim.speedIndx = sim.speedIndx+1 if sim.speedIndx > 3 then sim.speedIndx = 1 end elseif keyp(keys.KEY_G) then sim.ruleIndx = sim.ruleIndx+1 if sim.ruleIndx > #rules then sim.ruleIndx = 1 end sim.cellAgingRatio = sim.maxAge//(rules[sim.ruleIndx].g - 1) elseif keyp(keys.KEY_C) then vbank(0) cls(colors.bg) elseif keyp(keys.KEY_R) then randomSimulation() elseif keyp(keys.KEY_H) then game.compactMenu = not game.compactMenu elseif keyp(keys.KEY_P) then vbank(0) drawSprite(sprites[sprite.num].num, sprite.x, sprite.y, colors.cell, sprite.rotation, sprites[sprite.num].widthN, sprites[sprite.num].heightN) end return needDrawSprite end function randomSimulation() vbank(0) cls(colors.bg) for i = 0, 5000 do local x = math.random(0,sim.width-1) local y = math.random(0,sim.height-1) pix(x, y, colors.cell) end end function sprCustom(spriteNum, x, y, color, rotation, widthN, heightN) for i = 0, (8*heightN-1) do for j = 0, (8*widthN-1) do local px = peek4((tilesMemoryAdr4 + (64*(spriteNum + (i//8)*16 + j//8))) + (i%8)*8 + j%8) if px == colors.cell then if (rotation == 0) then pix(x + j, y + i, color) elseif (rotation == 90) then pix(x + (8*widthN-1) - i, y + j, color) elseif (rotation == 180) then pix(x + (8*widthN-1) - j, y + (8*heightN-1) - i, color) elseif (rotation == 270) then pix(x + i, y + (8*heightN-1) - j, color) end end end end end function drawSprite(spriteNum, x, y, color, rotation, widthN, heightN) local xS = x local yS = y local xS2 = nil local yS2 = nil if xS + 8*widthN > (sim.width - 1) then xS2 = xS - (sim.width) yS2 = yS end if yS + 8*heightN > (sim.height - 1) then if not xS2 then xS2 = xS end yS2 = yS - (sim.height) end sprCustom(spriteNum, xS, yS, color, rotation, widthN, heightN) if xS2 or yS2 then sprCustom(spriteNum, xS2, yS2, color, rotation, widthN, heightN) end end function drawOverlay() vbank(1) if (not game.compactMenu) then print("SPACE - start/stop simulation", 8, 52, colors.overlay, false, 1, true) print("H - hide extended help", 8, 60, colors.overlay, false, 1, true) print("Arrows - move sprite", 8, 68, colors.overlay, false, 1, true) print("G - change rule", 8, 76, colors.overlay, false, 1, true) print("Z - change sprite", 8, 84, colors.overlay, false, 1, true) print("X - rotate sprite", 8, 92, colors.overlay, false, 1, true) print("P - print sprite", 8, 100, colors.overlay, false, 1, true) print("C - clear simulation", 8, 108, colors.overlay, false, 1, true) print("R - random simulation", 8, 116, colors.overlay, false, 1, true) print("S - change simulation speed", 8, 124, colors.overlay, false, 1, true) else print("SPACE - start/stop simulation", 8, 116, colors.overlay, false, 1, true) print("H - show extended help", 8, 124, colors.overlay, false, 1, true) end if sim.speedIndx == 1 then print("low", 208, 4, colors.overlay, false, 1, true) elseif sim.speedIndx == 2 then print("medium", 208, 4, colors.overlay, false, 1, true) elseif sim.speedIndx == 3 then print("hight", 208, 4, colors.overlay, false, 1, true) end print(rules[sim.ruleIndx].name, 8, 4, colors.overlay, false, 1, true) vbank(0) end function lifeTic() memcpy(bufMemoryAdr, 0x0000, 16320) for i = 0, sim.width-1 do for j = 0, sim.height-1 do local cellAlive = (pix(i,j) == colors.cell) local cellAging = not cellAlive and (pix(i,j) ~= colors.bg) local cellNeighbors = getNeighborsCount(i,j,1) -- Born if (not cellAlive and (not cellAging) and #rules[sim.ruleIndx].b ~= 0) then for k = 1, #rules[sim.ruleIndx].b do if ( cellNeighbors == rules[sim.ruleIndx].b[k]) then pix(i,j,colors.cell) break end end -- Life, aging or die elseif (cellAlive and #rules[sim.ruleIndx].s ~= 0) then local aging = true for k = 1, #rules[sim.ruleIndx].s do if ( cellNeighbors == rules[sim.ruleIndx].s[k]) then aging = false break end end if aging then if rules[sim.ruleIndx].g > 2 then pix(i,j,colors.cell + sim.cellAgingRatio) else pix(i,j,colors.bg) end end -- Aging or die elseif (cellAging) then local age = pix(i,j) - colors.cell if (age // sim.cellAgingRatio) >= (rules[sim.ruleIndx].g - 2) then pix(i,j,colors.bg) else pix(i,j,colors.cell + age + sim.cellAgingRatio) end else pix(i,j,colors.bg) end end end end function fromPxToAddr4(x, y, vbankN) local shift = 0 if (vbankN == 1) then shift = bufMemoryAdr*2 end return shift + y*240 + x end function getNeighborsCount(x, y, vbankN) n = 0 for i = x-1, x+1 do for j = y-1, y+1 do local xCh = i local yCh = j if xCh < 0 then xCh = sim.width+xCh end if xCh >= sim.width then xCh = xCh - sim.width end if yCh < 0 then yCh = sim.height+yCh end if yCh >= sim.height then yCh = yCh - sim.height end if (not (xCh==x and yCh==y) and peek4(fromPxToAddr4(xCh,yCh,vbankN)) == colors.cell) then n = n+1 end end end return n end