Advertisement
nonogamer9

CC-Conway : A Port Of Conway's Game Of Life For ComputerCraft

Sep 27th, 2024 (edited)
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.77 KB | Software | 0 0
  1. local version = "1.0"
  2.  
  3. local config = {
  4.     gridWidth = 150,
  5.     gridHeight = 100,
  6.     updateInterval = 0.1,
  7.     maxZoom = 4,
  8.     historySize = 20,
  9.     patterns = {
  10.         glider = {{0,1,0},{0,0,1},{1,1,1}},
  11.         blinker = {{1},{1},{1}},
  12.         pulsar = {
  13.             {0,0,1,1,1,0,0,0,1,1,1,0,0},
  14.             {0,0,0,0,0,0,0,0,0,0,0,0,0},
  15.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  16.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  17.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  18.             {0,0,1,1,1,0,0,0,1,1,1,0,0},
  19.             {0,0,0,0,0,0,0,0,0,0,0,0,0},
  20.             {0,0,1,1,1,0,0,0,1,1,1,0,0},
  21.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  22.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  23.             {1,0,0,0,0,1,0,1,0,0,0,0,1},
  24.             {0,0,0,0,0,0,0,0,0,0,0,0,0},
  25.             {0,0,1,1,1,0,0,0,1,1,1,0,0}
  26.         },
  27.         spaceship = {{1,0,0,1,0},{0,0,0,0,1},{1,0,0,0,1},{0,1,1,1,1}}
  28.     }
  29. }
  30.  
  31. config.screenWidth, config.screenHeight = term.getSize()
  32.  
  33. local state = {
  34.     grid = {},
  35.     scrollX = 0,
  36.     scrollY = 0,
  37.     paused = true,
  38.     automatic = false,
  39.     generation = 0,
  40.     population = 0,
  41.     mode = "conway",
  42.     zoomLevel = 1,
  43.     history = {},
  44.     selectedPattern = "glider"
  45. }
  46.  
  47. for y = 1, config.gridHeight do
  48.     state.grid[y] = {}
  49.     for x = 1, config.gridWidth do
  50.         state.grid[y][x] = 0
  51.     end
  52. end
  53.  
  54. local function deepCopy(orig)
  55.     local orig_type = type(orig)
  56.     local copy
  57.     if orig_type == 'table' then
  58.         copy = {}
  59.         for orig_key, orig_value in next, orig, nil do
  60.             copy[deepCopy(orig_key)] = deepCopy(orig_value)
  61.         end
  62.         setmetatable(copy, deepCopy(getmetatable(orig)))
  63.     else
  64.         copy = orig
  65.     end
  66.     return copy
  67. end
  68.  
  69. local function countNeighbors(x, y)
  70.     local count = 0
  71.     for dy = -1, 1 do
  72.         for dx = -1, 1 do
  73.             if dx ~= 0 or dy ~= 0 then
  74.                 local nx, ny = (x + dx - 1) % config.gridWidth + 1, (y + dy - 1) % config.gridHeight + 1
  75.                 count = count + state.grid[ny][nx]
  76.             end
  77.         end
  78.     end
  79.     return count
  80. end
  81.  
  82. local function updateGrid()
  83.     local newGrid, newPopulation = {}, 0
  84.     for y = 1, config.gridHeight do
  85.         newGrid[y] = {}
  86.         for x = 1, config.gridWidth do
  87.             local neighbors = countNeighbors(x, y)
  88.             local cell = state.grid[y][x]
  89.             local newCell = 0
  90.             if state.mode == "conway" then
  91.                 newCell = ((cell == 1 and (neighbors == 2 or neighbors == 3)) or (cell == 0 and neighbors == 3)) and 1 or 0
  92.             elseif state.mode == "highlife" then
  93.                 newCell = ((cell == 1 and (neighbors == 2 or neighbors == 3)) or (cell == 0 and (neighbors == 3 or neighbors == 6))) and 1 or 0
  94.             elseif state.mode == "daynight" then
  95.                 newCell = ((cell == 1 and (neighbors == 3 or neighbors == 4 or neighbors == 6 or neighbors == 7 or neighbors == 8)) or
  96.                            (cell == 0 and (neighbors == 3 or neighbors == 6 or neighbors == 7 or neighbors == 8))) and 1 or 0
  97.             end
  98.             newGrid[y][x] = newCell
  99.             newPopulation = newPopulation + newCell
  100.         end
  101.     end
  102.     table.insert(state.history, 1, deepCopy(state.grid))
  103.     if #state.history > config.historySize then table.remove(state.history) end
  104.     state.grid = newGrid
  105.     state.generation = state.generation + 1
  106.     state.population = newPopulation
  107. end
  108.  
  109. local function drawCell(x, y, alive)
  110.     local char = alive and "\127" or " "
  111.     local fg = alive and colors.lime or colors.black
  112.     local bg = alive and colors.green or colors.gray
  113.     return char, fg, bg
  114. end
  115.  
  116. local function drawGrid()
  117.     local buffer = {}
  118.     for y = 1, config.screenHeight - 3 do
  119.         buffer[y] = {text = "", fg = {}, bg = {}}
  120.         for x = 1, config.screenWidth do
  121.             local gridX = math.floor((x - 1 + state.scrollX) / state.zoomLevel) + 1
  122.             local gridY = math.floor((y - 1 + state.scrollY) / state.zoomLevel) + 1
  123.             if gridX >= 1 and gridX <= config.gridWidth and gridY >= 1 and gridY <= config.gridHeight then
  124.                 local char, fg, bg = drawCell(gridX, gridY, state.grid[gridY][gridX] == 1)
  125.                 buffer[y].text = buffer[y].text .. char
  126.                 buffer[y].fg[x] = fg
  127.                 buffer[y].bg[x] = bg
  128.             else
  129.                 buffer[y].text = buffer[y].text .. " "
  130.                 buffer[y].fg[x] = colors.black
  131.                 buffer[y].bg[x] = colors.gray
  132.             end
  133.         end
  134.     end
  135.    
  136.     for y = 1, config.screenHeight - 3 do
  137.         for x = 1, config.screenWidth do
  138.             term.setCursorPos(x, y)
  139.             term.setTextColor(buffer[y].fg[x])
  140.             term.setBackgroundColor(buffer[y].bg[x])
  141.             term.write(buffer[y].text:sub(x,x))
  142.         end
  143.     end
  144.    
  145.     local status = state.paused and "Paused" or (state.automatic and "Auto" or "Running")
  146.     term.setBackgroundColor(colors.black)
  147.     term.setTextColor(colors.yellow)
  148.     term.setCursorPos(1, config.screenHeight - 2)
  149.     term.write(string.format("CC-Conway v%s | Mode: %s | Gen: %d | Pop: %d | Zoom: %d",
  150.                 version, state.mode, state.generation, state.population, state.zoomLevel))
  151.     term.setCursorPos(1, config.screenHeight - 1)
  152.     term.setTextColor(colors.cyan)
  153.     term.write(string.format("Status: %s | Pattern: %s", status, state.selectedPattern))
  154.     term.setCursorPos(1, config.screenHeight)
  155.     term.setTextColor(colors.white)
  156.     term.write("Press H for Help")
  157. end
  158.  
  159. local function placePattern(patternName, x, y)
  160.     local pattern = config.patterns[patternName]
  161.     if not pattern then return end
  162.    
  163.     for dy = 1, #pattern do
  164.         if type(pattern[dy]) ~= "table" then return end
  165.         for dx = 1, #pattern[dy] do
  166.             local gx, gy = x + dx - 1, y + dy - 1
  167.             if gx >= 1 and gx <= config.gridWidth and gy >= 1 and gy <= config.gridHeight then
  168.                 state.grid[gy][gx] = pattern[dy][dx]
  169.             end
  170.         end
  171.     end
  172. end
  173.  
  174. local function handleMouse(button, x, y)
  175.     if y <= config.screenHeight - 3 then
  176.         local gridX = math.floor((x - 1 + state.scrollX) / state.zoomLevel) + 1
  177.         local gridY = math.floor((y - 1 + state.scrollY) / state.zoomLevel) + 1
  178.         if gridX >= 1 and gridX <= config.gridWidth and gridY >= 1 and gridY <= config.gridHeight then
  179.             if button == 1 then
  180.                 state.grid[gridY][gridX] = 1 - state.grid[gridY][gridX]
  181.             elseif button == 2 then
  182.                 placePattern(state.selectedPattern, gridX, gridY)
  183.             end
  184.         end
  185.     end
  186. end
  187.  
  188. local function showPatternMenu()
  189.     local patterns = {}
  190.     for name, _ in pairs(config.patterns) do
  191.         table.insert(patterns, name)
  192.     end
  193.     table.sort(patterns)
  194.  
  195.     local function drawMenu()
  196.         term.setBackgroundColor(colors.black)
  197.         term.clear()
  198.         term.setCursorPos(1, 1)
  199.         term.setTextColor(colors.yellow)
  200.         print("Select a pattern:")
  201.         for i, name in ipairs(patterns) do
  202.             term.setTextColor(colors.white)
  203.             term.setCursorPos(2, i + 1)
  204.             print(i .. ". " .. name)
  205.             term.setBackgroundColor(colors.gray)
  206.             term.setCursorPos(1, i + 1)
  207.             term.write(" ")
  208.             term.setBackgroundColor(colors.black)
  209.         end
  210.         term.setTextColor(colors.red)
  211.         term.setCursorPos(2, #patterns + 3)
  212.         print("Cancel")
  213.         term.setBackgroundColor(colors.gray)
  214.         term.setCursorPos(1, #patterns + 3)
  215.         term.write(" ")
  216.         term.setBackgroundColor(colors.black)
  217.     end
  218.  
  219.     drawMenu()
  220.  
  221.     while true do
  222.         local event, button, x, y = os.pullEvent("mouse_click")
  223.         if button == 1 then
  224.             if y >= 2 and y <= #patterns + 1 and x <= 20 then
  225.                 state.selectedPattern = patterns[y - 1]
  226.                 return
  227.             elseif y == #patterns + 3 and x <= 8 then
  228.                 return
  229.             end
  230.         end
  231.     end
  232. end
  233.  
  234. local function showGuide()
  235.     local guide = {
  236.         {"Q", "Quit the program"},
  237.         {"S", "Step (advance one generation)"},
  238.         {"R", "Reset with random cells"},
  239.         {"M", "Change mode (Conway, HighLife, Day & Night)"},
  240.         {"P", "Select pattern"},
  241.         {"C", "Clear the grid"},
  242.         {"A", "Toggle automatic mode"},
  243.         {"Z", "Zoom in"},
  244.         {"X", "Zoom out"},
  245.         {"U", "Undo last change"},
  246.         {"Left Shift", "Pause/Resume"},
  247.         {"Space", "Pause/Resume"},
  248.         {"Arrow Keys", "Scroll the grid"},
  249.         {"Left Click", "Toggle cell state"},
  250.         {"Right Click", "Place selected pattern"},
  251.         {"H", "Show this help guide"}
  252.     }
  253.  
  254.     term.setBackgroundColor(colors.black)
  255.     term.clear()
  256.     term.setCursorPos(1, 1)
  257.     term.setTextColor(colors.yellow)
  258.     print("CC-Conway Controls:")
  259.     for _, control in ipairs(guide) do
  260.         term.setTextColor(colors.white)
  261.         print(control[1] .. ":")
  262.         term.setTextColor(colors.cyan)
  263.         print("  " .. control[2])
  264.         print()
  265.     end
  266.     term.setTextColor(colors.yellow)
  267.     print("Press any key to return...")
  268.     os.pullEvent("key")
  269. end
  270.  
  271. local function handleKey(key)
  272.     if key == keys.left then
  273.         state.scrollX = math.max(0, state.scrollX - state.zoomLevel)
  274.     elseif key == keys.right then
  275.         state.scrollX = math.min(config.gridWidth * state.zoomLevel - config.screenWidth, state.scrollX + state.zoomLevel)
  276.     elseif key == keys.up then
  277.         state.scrollY = math.max(0, state.scrollY - state.zoomLevel)
  278.     elseif key == keys.down then
  279.         state.scrollY = math.min(config.gridHeight * state.zoomLevel - config.screenHeight + 3, state.scrollY + state.zoomLevel)
  280.     elseif key == keys.space then
  281.         state.paused = not state.paused
  282.         state.automatic = false
  283.     elseif key == keys.s then
  284.         updateGrid()
  285.     elseif key == keys.r then
  286.         state.generation, state.population = 0, 0
  287.         for y = 1, config.gridHeight do
  288.             for x = 1, config.gridWidth do
  289.                 state.grid[y][x] = math.random(0, 1)
  290.                 state.population = state.population + state.grid[y][x]
  291.             end
  292.         end
  293.     elseif key == keys.m then
  294.         if state.mode == "conway" then state.mode = "highlife"
  295.         elseif state.mode == "highlife" then state.mode = "daynight"
  296.         else state.mode = "conway" end
  297.     elseif key == keys.p then
  298.         state.paused = true
  299.         showPatternMenu()
  300.         os.sleep(0.5)
  301.     elseif key == keys.c then
  302.         for y = 1, config.gridHeight do
  303.             for x = 1, config.gridWidth do
  304.                 state.grid[y][x] = 0
  305.             end
  306.         end
  307.         state.generation, state.population = 0, 0
  308.     elseif key == keys.a then
  309.         state.automatic = not state.automatic
  310.         state.paused = false
  311.     elseif key == keys.z then
  312.         if state.zoomLevel < config.maxZoom then
  313.             state.zoomLevel = state.zoomLevel + 1
  314.         end
  315.     elseif key == keys.x then
  316.         if state.zoomLevel > 1 then
  317.             state.zoomLevel = state.zoomLevel - 1
  318.         end
  319.     elseif key == keys.u and #state.history > 0 then
  320.         state.grid = table.remove(state.history, 1)
  321.         state.generation = math.max(0, state.generation - 1)
  322.     elseif key == keys.leftShift then
  323.         state.paused = not state.paused
  324.         state.automatic = false
  325.     elseif key == keys.h then
  326.         showGuide()
  327.     end
  328. end
  329.  
  330. local function mainLoop()
  331.     local lastUpdate = os.clock()
  332.     while true do
  333.         drawGrid()
  334.        
  335.         local timer = os.startTimer(config.updateInterval)
  336.         while true do
  337.             local event, p1, p2, p3 = os.pullEvent()
  338.             if event == "mouse_click" then
  339.                 handleMouse(p1, p2, p3)
  340.                 break
  341.             elseif event == "key" then
  342.                 if p1 == keys.q then
  343.                     term.setBackgroundColor(colors.black)
  344.                     term.clear()
  345.                     term.setCursorPos(1, 1)
  346.                     term.setTextColor(colors.yellow)
  347.                     print("Thank you for playing CC-Conway!")
  348.                     return
  349.                 end
  350.                 handleKey(p1)
  351.                 break
  352.             elseif event == "timer" and p1 == timer then
  353.                 if not state.paused or state.automatic then
  354.                     updateGrid()
  355.                     lastUpdate = os.clock()
  356.                 end
  357.                 break
  358.             end
  359.         end
  360.     end
  361. end
  362.  
  363. term.setBackgroundColor(colors.black)
  364. term.clear()
  365. term.setCursorPos(1, 1)
  366. term.setTextColor(colors.yellow)
  367. print("Welcome to CC-Conway v" .. version)
  368. term.setTextColor(colors.lime)
  369. print("Coded by nonogamer9")
  370. term.setTextColor(colors.cyan)
  371. print("Press any key to start...")
  372. os.pullEvent("key")
  373. mainLoop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement