Skip to content

Instantly share code, notes, and snippets.

@fedeisas
Created December 12, 2016 00:11
Show Gist options
  • Save fedeisas/f5b1534fa24e0e21275436c9271200b5 to your computer and use it in GitHub Desktop.
Save fedeisas/f5b1534fa24e0e21275436c9271200b5 to your computer and use it in GitHub Desktop.
Simple Conway's Game of Life
<?php
/**
* Conway's Game of Life
* @see https://en.wikipedia.org/wiki/Conway's_Game_of_Life
*/
define('SIZE', 36);
define('DISPLAY_ALIVE', '☼');
define('DISPLAY_DEAD', ' ');
define('DISPLAY_NEWLINE', PHP_EOL);
define('SLEEP', 100000);
define('START_TIME', microtime(true));
/**
* Creates a random scenario of a given size.
* @param int $size
* @return array
*/
function createScenario(int $size) : array {
$scenario = [];
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$scenario[$i][$j] = random_int(0, 1);
}
}
return $scenario;
}
/**
* Converts an scenario to a string for display
* @param array $scenario
* @return string
*/
function render(array $scenario) : string {
$output = '';
foreach ($scenario as $row) {
$output .= join('', array_map(function ($value) {
return $value ? DISPLAY_ALIVE : DISPLAY_DEAD;
}, $row));
$output .= DISPLAY_NEWLINE;
}
return $output;
}
/**
* @param array $scenario
* @return array
*/
function updateScenario(array $scenario) : array {
$size = sizeof($scenario);
$newScenario = $scenario; // Clone scenario.
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$aliveNeighbors = countAliveNeighbors($i, $j, $scenario);
$initialValue = $scenario[$i][$j];
// Apply rules...
if ($initialValue === 0) {
if ($aliveNeighbors === 3) {
$newScenario[$i][$j] = 1;
} else {
$newScenario[$i][$j] = 0;
}
}
if ($initialValue === 1) {
switch ($aliveNeighbors) {
case 0:
case 1:
// Any live cell with fewer than two live neighbours dies, as if caused by underpopulation.
$newScenario[$i][$j] = 0;
break;
case 2:
case 3:
// Any live cell with two or three live neighbours lives on to the next generation.
$newScenario[$i][$j] = 1;
break;
case 4:
case 5:
case 6:
case 7:
case 8:
// Any live cell with more than three live neighbours dies, as if by overpopulation.
$newScenario[$i][$j] = 0;
break;
default:
throw new LogicException('The number of alive neighbors is wrong: ' . $aliveNeighbors);
}
}
}
}
return $newScenario;
}
/**
* Counts the number of alive neighbors on an scenario for a given cell.
* @param int $x
* @param int $y
* @param array $scenario
* @return int
*/
function countAliveNeighbors(int $x, int $y, array $scenario) : int {
return array_sum([
(!empty($scenario[$x - 1][$y - 1])) ? $scenario[$x - 1][$y - 1] : 0, // top left
(!empty($scenario[$x - 1][$y])) ? $scenario[$x - 1][$y] : 0, // top center
(!empty($scenario[$x - 1][$y + 1])) ? $scenario[$x - 1][$y + 1] : 0, // top right
(!empty($scenario[$x][$y - 1])) ? $scenario[$x][$y - 1] : 0, // middle left
(!empty($scenario[$x][$y + 1])) ? $scenario[$x][$y + 1] : 0, // middle right
(!empty($scenario[$x + 1][$y - 1])) ? $scenario[$x + 1][$y - 1] : 0, // bottom left
(!empty($scenario[$x + 1][$y])) ? $scenario[$x + 1][$y] : 0, // bottom center
(!empty($scenario[$x + 1][$y + 1])) ? $scenario[$x + 1][$y + 1] : 0, // bottom right
]);
}
/**
* Returns some useful info (memory usage, elapsed time, population, generation)
* @param array $scenario
* @param int $generation
* @return string
*/
function getScenarioInfo(array $scenario, int $generation) : string {
$memory = round(memory_get_usage() / 1048576, 2);
$time = secondsToTimeString(microtime(true) - START_TIME);
$population = array_reduce($scenario, function(&$res, $item) {
return $res + array_sum($item);
}, 0);
return join(PHP_EOL, [
sprintf('%.2F MB, %s', $memory, $time),
sprintf('Size: %d, Population: %d, Generation: %d', sizeof($scenario), $population, $generation),
]);
}
/**
* Formats the elapsed time as a string.
*
* @see https://github.com/sebastianbergmann/php-timer/blob/master/src/Timer.php#L55-L74
* @param float $time
* @return string
*/
function secondsToTimeString(float $time) : string
{
$ms = round($time * 1000);
foreach ([
'hour' => 3600000,
'minute' => 60000,
'second' => 1000
] as $unit => $value) {
if ($ms >= $value) {
$time = floor($ms / $value * 100.0) / 100.0;
return $time . ' ' . ($time == 1 ? $unit : $unit . 's');
}
}
return $ms . ' ms';
}
/**
* Begin program
*/
system('clear');
$scenario = createScenario(SIZE);
$generation = 1;
do {
echo join(PHP_EOL, [
render($scenario),
getScenarioInfo($scenario, $generation)
]);
$scenario = updateScenario($scenario);
$generation++;
usleep(SLEEP);
system('clear');
} while (true);
@fedeisas
Copy link
Author

fedeisas commented Dec 12, 2016

asciicast

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment