Skip to content

Commit

Permalink
Add more game of life implementations and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
lefticus committed Jan 11, 2023
1 parent e9938c8 commit 6364de6
Show file tree
Hide file tree
Showing 11 changed files with 962 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*~
callgrind.*
48 changes: 48 additions & 0 deletions python/conway_game_of_life/larger_tests/average_times.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/bash

# This script will time each executable in a directory 5 times, average the times, and report the average timed run for each executable


# Loop through all executables in the directory
for executable in *; do

# Skip any files that have an extension
if [[ "$executable" == *.* ]]; then
continue
fi

# Skip any files that do not have executable bit set
if [[ ! -x "$executable" ]]; then
continue
fi

# Get the file name
filename=$(basename "$executable")

# Print the executable name
echo "Executable: $filename"

# Create a variable to hold the total time
total_time=0
total_ram=0

# Loop five times
for i in {1..5}; do
# Time the executable and save the output to a variable
run_time=$(/usr/bin/time -f "%e %M" "./$executable" 2>&1 1>/dev/null )
# Extract the time from the output
time=$(echo "$run_time" | awk '{print $1}')
ram=$(echo "$run_time" | awk '{print $2}')
# Add the time to the total
total_time=$(echo "$total_time + $time" | bc)
total_ram=$(echo "$total_ram + $ram" | bc)
done

# Calculate the average time
average_time=$(echo "$total_time / 5" | bc -l)
average_ram=$(echo "$total_ram / 5" | bc -l)

# Print the average time
echo "Average time: $average_time"
echo "Average RAM usage: $average_ram"
done
10 changes: 10 additions & 0 deletions python/conway_game_of_life/larger_tests/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# Bash script to compile each c++ file into its own executable

# Loop over each file in the directory
for file in *.cpp
do
# Compile file into an executable, using output filename as the cpp filename
g++ $file -o ${file%.cpp} -O3 -march=native -std=c++23
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#include <algorithm>
#include <array>
#include <cstdio>
#include <type_traits>
#include <vector>
#include <fmt/format.h>

#include "parameters.hpp"

// necessary to keep the same algorithm as Python
constexpr auto floor_modulo(auto dividend, auto divisor)
{
return ((dividend % divisor) + divisor) % divisor;
}

using index_t = std::make_signed_t<std::size_t>;

struct Point
{
index_t x;
index_t y;

[[nodiscard]] constexpr Point operator+(Point rhs) const
{
return Point{ x + rhs.x, y + rhs.y };
}
};


// if we wanted to push this further, we would make the width and height compile-time constants
// that is for a future episode.
template<std::size_t Width, std::size_t Height>
struct Automata
{

constexpr auto width() const
{
return Width;
}

constexpr auto height() const
{
return Height;
}

std::array<bool, 9> born;
std::array<bool, 9> survives;

std::array<bool, Width * Height> data{};

constexpr Automata(std::array<bool, 9> born_, std::array<bool, 9> survives_)
: born(born_), survives(survives_) {}

[[nodiscard]] constexpr std::size_t index(Point p) const
{
return floor_modulo(p.y, static_cast<index_t>(Height)) * Width + floor_modulo(p.x, static_cast<index_t>(Width));
}

[[nodiscard]] constexpr bool get(Point p) const { return data[index(p)]; }

constexpr void set(Point p) { data[index(p)] = true; }

constexpr static std::array<Point, 8> neighbors{
Point{ -1, -1 },
Point{ 0, -1 },
Point{ 1, -1 },
Point{ -1, 0 },
Point{ 1, 0 },
Point{ -1, 1 },
Point{ 0, 1 },
Point{ 1, 1 }
};

constexpr std::size_t count_neighbors(Point p) const
{
return static_cast<std::size_t>(std::ranges::count_if(
neighbors, [&](auto offset) { return get(p + offset); }));
}

[[nodiscard]] constexpr Automata next() const
{
Automata<Width, Height> result{ born, survives };

for (std::size_t y = 0; y < Height; ++y) {
for (std::size_t x = 0; x < Width; ++x) {
Point p{ static_cast<index_t>(x), static_cast<index_t>(y) };
const auto neighbors = count_neighbors(p);
if (get(p)) {
if (survives[neighbors]) {
result.set(p);
}
} else {
if (born[neighbors]) {
result.set(p);
}
}
}
}

return result;
}
constexpr void add_glider(Point p)
{
set(p);
set(p + Point(1, 1));
set(p + Point(2, 1));
set(p + Point(0, 2));
set(p + Point(1, 2));
}
};

int main()
{
constexpr static auto initial_state = []() {
auto obj = Automata<WIDTH, HEIGHT>({ false, false, false, true, false, false, false, false, false }, { false, false, true, true, false, false, false, false, false });
obj.add_glider(Point(0, 18));
return obj;
}();

auto obj = initial_state;

for (int i = 0; i < ITERATIONS; ++i) {
obj = obj.next();
}

for (size_t y = 0; y < obj.height(); ++y) {
for (size_t x = 0; x < obj.width(); ++x) {
if (obj.get(Point(static_cast<index_t>(x),
static_cast<index_t>(y)))) {
std::putchar('X');
} else {
std::putchar('.');
}
}
std::puts("");
}
}
134 changes: 134 additions & 0 deletions python/conway_game_of_life/larger_tests/life-stack-based.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#include <algorithm>
#include <array>
#include <cstdio>
#include <type_traits>
#include <vector>
#include <fmt/format.h>

#include "parameters.hpp"

// necessary to keep the same algorithm as Python
constexpr auto floor_modulo(auto dividend, auto divisor)
{
return ((dividend % divisor) + divisor) % divisor;
}

using index_t = std::make_signed_t<std::size_t>;

struct Point
{
index_t x;
index_t y;

[[nodiscard]] constexpr Point operator+(Point rhs) const
{
return Point{ x + rhs.x, y + rhs.y };
}
};


// if we wanted to push this further, we would make the width and height compile-time constants
// that is for a future episode.
template<std::size_t Width, std::size_t Height>
struct Automata
{

constexpr auto width() const
{
return Width;
}

constexpr auto height() const
{
return Height;
}

std::array<bool, 9> born;
std::array<bool, 9> survives;

std::array<bool, Width * Height> data{};

constexpr Automata(std::array<bool, 9> born_, std::array<bool, 9> survives_)
: born(born_), survives(survives_) {}

[[nodiscard]] constexpr std::size_t index(Point p) const
{
return floor_modulo(p.y, static_cast<index_t>(Height)) * Width + floor_modulo(p.x, static_cast<index_t>(Width));
}

[[nodiscard]] constexpr bool get(Point p) const { return data[index(p)]; }

constexpr void set(Point p) { data[index(p)] = true; }

constexpr static std::array<Point, 8> neighbors{
Point{ -1, -1 },
Point{ 0, -1 },
Point{ 1, -1 },
Point{ -1, 0 },
Point{ 1, 0 },
Point{ -1, 1 },
Point{ 0, 1 },
Point{ 1, 1 }
};

constexpr std::size_t count_neighbors(Point p) const
{
return static_cast<std::size_t>(std::ranges::count_if(
neighbors, [&](auto offset) { return get(p + offset); }));
}

[[nodiscard]] constexpr Automata next() const
{
Automata<Width, Height> result{ born, survives };

for (std::size_t y = 0; y < Height; ++y) {
for (std::size_t x = 0; x < Width; ++x) {
Point p{ static_cast<index_t>(x), static_cast<index_t>(y) };
const auto neighbors = count_neighbors(p);
if (get(p)) {
if (survives[neighbors]) {
result.set(p);
}
} else {
if (born[neighbors]) {
result.set(p);
}
}
}
}

return result;
}
constexpr void add_glider(Point p)
{
set(p);
set(p + Point(1, 1));
set(p + Point(2, 1));
set(p + Point(0, 2));
set(p + Point(1, 2));
}
};

int main()
{

auto obj = Automata<WIDTH, HEIGHT>({ false, false, false, true, false, false, false, false, false }, { false, false, true, true, false, false, false, false, false });
obj.add_glider(Point(0, 18));


for (int i = 0; i < ITERATIONS; ++i) {
obj = obj.next();
}

for (size_t y = 0; y < obj.height(); ++y) {
for (size_t x = 0; x < obj.width(); ++x) {
if (obj.get(Point(static_cast<index_t>(x),
static_cast<index_t>(y)))) {
std::putchar('X');
} else {
std::putchar('.');
}
}
std::puts("");
}
}
Loading

0 comments on commit 6364de6

Please sign in to comment.