├── .gitignore ├── images └── life_icon.png ├── button.lua ├── cell.lua ├── conf.lua ├── copying.txt ├── README.md └── main.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .classpath 2 | .project 3 | .metadata 4 | .settings 5 | .buildpath -------------------------------------------------------------------------------- /images/life_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hleb-kastseika/conways-game-of-life/HEAD/images/life_icon.png -------------------------------------------------------------------------------- /button.lua: -------------------------------------------------------------------------------- 1 | Button = {} 2 | 3 | function Button.new(name, action) 4 | local o = {} 5 | o.name = name 6 | o.action = action 7 | setmetatable(o, { __index = Button }) 8 | return o 9 | end 10 | -------------------------------------------------------------------------------- /cell.lua: -------------------------------------------------------------------------------- 1 | Cell = {} 2 | 3 | function Cell.new(x, y, isAlive) 4 | local o = {} 5 | o.x = x 6 | o.y = y 7 | o.isAlive = isAlive 8 | setmetatable(o, { __index = Cell }) 9 | return o 10 | end 11 | -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | -- configuration file for LOVE framework 2 | _CONFIGS = {} 3 | 4 | function love.conf(t) 5 | t.window.width = 904 6 | t.window.height = 768 7 | t.title = "Conway's Game Of Life" 8 | t.window.resizable = false 9 | t.window.fullscreentype = "desktop" 10 | t.window.vsync = true 11 | t.window.icon = "images/life_icon.png" 12 | t.console = true 13 | 14 | _CONFIGS = t --save configuration to global variable 15 | end 16 | -------------------------------------------------------------------------------- /copying.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2017 Gleb Kosteiko 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Conway's Game of Life 2 | 3 | [![License](https://img.shields.io/badge/License-WTFPL-brightgreen.svg)](https://raw.githubusercontent.com/gleb-kosteiko/conways-game-of-life/master/copying.txt) 4 | 5 | Conway's Game of Life created with Lua with the LÖVE framework. 6 | 7 | ![Screenshot](https://pbs.twimg.com/media/CUgPnwGUcAAzIUg.png:large) 8 | 9 | *Feel free to make pull requests!* 10 | 11 | 12 | --- 13 | 14 | **Copyright © Gleb Kosteiko** 15 | 16 | This work is free. You can redistribute it and/or modify it under the 17 | terms of the Do What The Fuck You Want To Public License, Version 2, 18 | as published by Sam Hocevar. See the [COPYING](https://github.com/gleb-kosteiko/conways-game-of-life/blob/master/copying.txt) file for more details. 19 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | require "cell" 2 | require "button" 3 | 4 | function love.load() 5 | --rgb colors 6 | _GREEN_COLOR = {64, 243, 19, 250} 7 | _BLACK_COLOR = {0, 0, 0, 250} 8 | _GREY_COLOR = {130, 130, 130, 250} 9 | _WHITE_COLOR = {255, 255, 255, 250} 10 | _BROWN_COLOR = {175,115,63,100} 11 | 12 | _IS_STARTED = false 13 | _IS_PAUSED = true 14 | _MARGIN_WIDTH = 10 15 | _BUTTON_WIDTH = 120 16 | _BUTTON_HEIGHT = 50 17 | _BUTTON_MARGIN_WIDTH = 40 18 | _BUTTON_MARGIN_HEIGHT = 20 19 | _UNIVERSE_SIZE =_CONFIGS.window.height - _MARGIN_WIDTH * 2 20 | _GRID_BORDER_SIZE = 1 21 | _CELL_SIZE = 4 22 | _CELL_GRID_SIZE = math.floor(_UNIVERSE_SIZE/(_CELL_SIZE+_GRID_BORDER_SIZE)) 23 | 24 | --populate initial cell array 25 | _CELL_GRID = {} 26 | generateInitialCells() 27 | _OLD_CELL_GRID = clone(_CELL_GRID) 28 | 29 | _BUTTONS = { 30 | Button.new("Start", 31 | function() 32 | _IS_STARTED = true 33 | _IS_PAUSED = false 34 | generateInitialCells() 35 | end), 36 | Button.new("Pause", 37 | function() 38 | if _IS_PAUSED then 39 | _IS_PAUSED = false 40 | else 41 | _IS_PAUSED = true 42 | end 43 | end), 44 | Button.new("Clear", 45 | function() 46 | _IS_STARTED = false 47 | _IS_PAUSED = true 48 | end) 49 | } 50 | end 51 | 52 | function getColor(rgbColor) 53 | return rgbColor[1], rgbColor[2], rgbColor[3], rgbColor[4] 54 | end 55 | 56 | function love.draw() 57 | drawUniverseBorder() 58 | drawButtons() 59 | if _IS_STARTED then 60 | if not _IS_PAUSED then 61 | calculateNewGeneration() 62 | drawCells(_CELL_GRID) 63 | else 64 | drawCells(_CELL_GRID) 65 | end 66 | else 67 | drawEmptyCells() 68 | end 69 | end 70 | 71 | function drawUniverseBorder() 72 | love.graphics.setColor(getColor(_GREEN_COLOR)) 73 | love.graphics.rectangle("line", _MARGIN_WIDTH, _MARGIN_WIDTH, _UNIVERSE_SIZE + 5, _UNIVERSE_SIZE + 5) 74 | love.graphics.setColor(getColor(_BLACK_COLOR)) 75 | love.graphics.rectangle("line", _MARGIN_WIDTH + 1, _MARGIN_WIDTH + 1, _UNIVERSE_SIZE + 3, _UNIVERSE_SIZE + 3) 76 | end 77 | 78 | function drawButtons() 79 | local xCoorditate = _CONFIGS.window.width - _BUTTON_WIDTH - _MARGIN_WIDTH 80 | local xLabelCoorditate = _CONFIGS.window.width - _BUTTON_WIDTH - _MARGIN_WIDTH + _BUTTON_MARGIN_WIDTH 81 | for i=1,#_BUTTONS do 82 | _BUTTONS[i].xCoorditate = xCoorditate 83 | _BUTTONS[i].xLabelCoorditate = xLabelCoorditate 84 | love.graphics.setColor(getColor(_GREY_COLOR)) --buttons color 85 | local yCoorditate = _MARGIN_WIDTH * i + _BUTTON_HEIGHT * (i-1) 86 | local yLabelCoorditate = _MARGIN_WIDTH * i + _BUTTON_HEIGHT * (i-1) + _BUTTON_MARGIN_HEIGHT 87 | _BUTTONS[i].yCoorditate = yCoorditate 88 | _BUTTONS[i].yLabelCoorditate = yLabelCoorditate 89 | love.graphics.rectangle("fill", xCoorditate, yCoorditate, _BUTTON_WIDTH, _BUTTON_HEIGHT) 90 | love.graphics.setColor(getColor(_WHITE_COLOR)) --button labels color 91 | love.graphics.print(_BUTTONS[i].name, xLabelCoorditate, yLabelCoorditate) 92 | end 93 | end 94 | 95 | function drawEmptyCells() 96 | for i=0,_UNIVERSE_SIZE,_CELL_SIZE+_GRID_BORDER_SIZE do 97 | for j=0,_UNIVERSE_SIZE,_CELL_SIZE+_GRID_BORDER_SIZE do 98 | love.graphics.setColor(getColor(_BROWN_COLOR)) 99 | love.graphics.rectangle("fill", _MARGIN_WIDTH + 2 + i, _MARGIN_WIDTH + 2 + j, _CELL_SIZE, _CELL_SIZE) 100 | end 101 | end 102 | end 103 | 104 | function drawCells(cellGrid) 105 | for i=0,#cellGrid do 106 | for j=0,#cellGrid do 107 | if cellGrid[i][j].isAlive then 108 | love.graphics.setColor(getColor(_GREEN_COLOR)) 109 | else 110 | love.graphics.setColor(getColor(_BROWN_COLOR)) 111 | end 112 | love.graphics.rectangle("fill", cellGrid[i][j].x, cellGrid[i][j].y, _CELL_SIZE, _CELL_SIZE) 113 | end 114 | end 115 | end 116 | 117 | function calculateNewGeneration() 118 | _OLD_CELL_GRID = clone(_CELL_GRID) 119 | _CELL_GRID = {} 120 | for i=0,_CELL_GRID_SIZE do 121 | _CELL_GRID[i] = {} 122 | for j=0,_CELL_GRID_SIZE do 123 | local oldCell = _OLD_CELL_GRID[i][j] 124 | 125 | local neighbors = {} 126 | local tempCell = nil 127 | 128 | tempCell = getUpperLeft(i,j) 129 | if tempCell ~= nil then 130 | table.insert(neighbors, tempCell) 131 | end 132 | 133 | tempCell = getUpper(i,j) 134 | if tempCell ~= nil then 135 | table.insert(neighbors, tempCell) 136 | end 137 | 138 | tempCell = getUpperRight(i,j) 139 | if tempCell ~= nil then 140 | table.insert(neighbors, tempCell) 141 | end 142 | 143 | tempCell = getLeft(i,j) 144 | if tempCell ~= nil then 145 | table.insert(neighbors, tempCell) 146 | end 147 | 148 | tempCell = getRight(i,j) 149 | if tempCell ~= nil then 150 | table.insert(neighbors, tempCell) 151 | end 152 | 153 | tempCell = getBottomLeft(i,j) 154 | if tempCell ~= nil then 155 | table.insert(neighbors, tempCell) 156 | end 157 | 158 | tempCell = getBottom(i,j) 159 | if tempCell ~= nil then 160 | table.insert(neighbors, tempCell) 161 | end 162 | 163 | tempCell = getBottomRight(i,j) 164 | if tempCell ~= nil then 165 | table.insert(neighbors, tempCell) 166 | end 167 | 168 | local liveNeighborsCount = 0 169 | for i=1,#neighbors do 170 | if neighbors[i].isAlive then 171 | liveNeighborsCount = liveNeighborsCount + 1 172 | end 173 | end 174 | 175 | local newCell = clone(oldCell) 176 | if oldCell.isAlive and (liveNeighborsCount < 2 or liveNeighborsCount > 3) then 177 | newCell.isAlive = false 178 | end 179 | 180 | if not oldCell.isAlive and liveNeighborsCount == 3 then 181 | newCell.isAlive = true 182 | end 183 | 184 | _CELL_GRID[i][j] = newCell 185 | end 186 | end 187 | end 188 | 189 | function love.keypressed(key) 190 | if key == "escape" then 191 | love.event.push("quit") 192 | end 193 | end 194 | 195 | function love.update(dt) 196 | if _IS_STARTED then 197 | love.timer.sleep(1) 198 | end 199 | end 200 | 201 | --catch buttons clicks 202 | function love.mousepressed(x, y) 203 | for i=1,#_BUTTONS do 204 | if x > _BUTTONS[i].xCoorditate and x < _BUTTONS[i].xCoorditate + _BUTTON_WIDTH 205 | and y > _BUTTONS[i].yCoorditate and y < _BUTTONS[i].yCoorditate + _BUTTON_HEIGHT then 206 | _BUTTONS[i].action() 207 | end 208 | end 209 | end 210 | 211 | function getUpperLeft(i,j) 212 | if i>0 and j>0 then 213 | return _OLD_CELL_GRID[i-1][j-1] 214 | else 215 | return nil 216 | end 217 | end 218 | 219 | function getUpper(i,j) 220 | if j>0 then 221 | return _OLD_CELL_GRID[i][j-1] 222 | else 223 | return nil 224 | end 225 | end 226 | 227 | function getUpperRight(i,j) 228 | if i>0 and j<#_OLD_CELL_GRID then 229 | return _OLD_CELL_GRID[i-1][j+1] 230 | else 231 | return nil 232 | end 233 | end 234 | 235 | function getLeft(i,j) 236 | if i>0 then 237 | return _OLD_CELL_GRID[i-1][j] 238 | else 239 | return nil 240 | end 241 | end 242 | 243 | function getRight(i,j) 244 | if i<#_OLD_CELL_GRID then 245 | return _OLD_CELL_GRID[i+1][j] 246 | else 247 | return nil 248 | end 249 | end 250 | 251 | function getBottomLeft(i,j) 252 | if i>0 and j<#_OLD_CELL_GRID then 253 | return _OLD_CELL_GRID[i-1][j+1] 254 | else 255 | return nil 256 | end 257 | end 258 | 259 | function getBottom(i,j) 260 | if j<#_OLD_CELL_GRID then 261 | return _OLD_CELL_GRID[i][j+1] 262 | else 263 | return nil 264 | end 265 | end 266 | 267 | function getBottomRight(i,j) 268 | if i<#_OLD_CELL_GRID and j<#_OLD_CELL_GRID then 269 | return _OLD_CELL_GRID[i+1][j+1] 270 | else 271 | return nil 272 | end 273 | end 274 | 275 | function generateInitialCells() 276 | for i=0,_CELL_GRID_SIZE do 277 | _CELL_GRID[i] = {} 278 | for j=0,_CELL_GRID_SIZE do 279 | local cell = Cell.new( 280 | _MARGIN_WIDTH + 2 + i*(_CELL_SIZE+_GRID_BORDER_SIZE), 281 | _MARGIN_WIDTH + 2 + j*(_CELL_SIZE+_GRID_BORDER_SIZE), 282 | math.random(0, 10) == 1 and true or false) 283 | _CELL_GRID[i][j] = cell 284 | end 285 | end 286 | end 287 | 288 | function clone(t) -- deep-copy a table 289 | if type(t) ~= "table" then return t end 290 | local meta = getmetatable(t) 291 | local target = {} 292 | for k, v in pairs(t) do 293 | if type(v) == "table" then 294 | target[k] = clone(v) 295 | else 296 | target[k] = v 297 | end 298 | end 299 | setmetatable(target, meta) 300 | return target 301 | end 302 | --------------------------------------------------------------------------------