another algorithm for conway

This commit is contained in:
wgroeneveld 2018-11-30 16:32:22 +01:00
parent 0bab9368ba
commit df782208e2
9 changed files with 224 additions and 60 deletions

View File

@ -1,8 +1,8 @@
project(conway)
add_executable(${PROJECT_NAME}.elf
src/main.cpp
src/ConwayScene.cpp src/ConwayScene.h src/bg.h src/ConwaySeeder.cpp src/ConwaySeeder.h)
src/main.cpp src/ConwayScene.cpp src/ConwayScene.h
src/NaiveConwayScene.cpp src/NaiveConwayScene.h src/bg.h src/ConwaySeeder.cpp src/ConwaySeeder.h src/ActiveTrackingConwayScene.cpp src/ActiveTrackingConwayScene.h)
target_link_libraries(${PROJECT_NAME}.elf gba-sprite-engine)

View File

@ -0,0 +1,69 @@
//
// Created by Wouter Groeneveld on 30/11/18.
//
#include <libgba-sprite-engine/background/text_stream.h>
#include <libgba-sprite-engine/gba/tonc_core.h>
#include "ActiveTrackingConwayScene.h"
ActiveTrackingConwayScene::ActiveTrackingConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed)
: ConwayScene(engine, percentageSeed) {}
void ActiveTrackingConwayScene::tick(u16 keys) {
generation++;
int totalAmountAlive = 0;
dma3_cpy(buffer, map, sizeof(buffer));
// when starting slow (100 active), this totals to 100 * 9 = +900 instructions VS +30k in the naive impl
// when starting high, this will too slow down, worst case slightly slower than naive (SIZE * 9 instead of * 8)
// 1. loop through all "active" cells.
for(int i = 0; i < MAP_SIZE; i++) {
auto cell = activeCellIndex[i];
if(!cell.taken) {
break;
}
int x = cell.x, y = cell.y;
// 2. process those _AND_ the neighbours (could be dead ones becoming alive)
for(int x_i = x - 1; x_i <= x + 1; x_i++) {
for(int y_j = y - 1; y_j <= y + 1; y_j++) {
int toCheckPos = y_j * MAP_WIDTH + x_i;
if(toCheckPos >= 0 && toCheckPos < MAP_SIZE - 1) {
u16 state = getNextState(x_i, y_j);
if(state == ALIVE) {
// 3. Save the cell metadata if active for the next generation
activeCellIndexBuffer[totalAmountAlive].taken = true;
activeCellIndexBuffer[totalAmountAlive].x = x_i;
activeCellIndexBuffer[totalAmountAlive].y = y_j;
totalAmountAlive++;
}
buffer[toCheckPos] = state;
}
}
}
}
activeCellIndexBuffer[totalAmountAlive].taken = false;
TextStream::instance().setText(std::string("amount alive: ") + std::to_string(totalAmountAlive) + std::string(" of ") + std::to_string(MAP_SIZE), 1, 1);
TextStream::instance().setText(std::string("generation: ") + std::to_string(generation), 2, 1);
dma3_cpy(activeCellIndex, activeCellIndexBuffer, sizeof(activeCellIndex));
dma3_cpy(map, buffer, sizeof(map));
bg.get()->updateMap(map);
}
void ActiveTrackingConwayScene::postload() {
int i = 0;
for(int w = 0; w < MAP_WIDTH; w++) {
for(int h = 0; h < MAP_HEIGHT; h++) {
u8 index = h * MAP_WIDTH + w;
if(map[index] == ALIVE) {
activeCellIndex[i].taken = true;
activeCellIndex[i].x = w;
activeCellIndex[i].y = h;
i++;
}
}
}
}

View File

@ -0,0 +1,30 @@
//
// Created by Wouter Groeneveld on 30/11/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_ACTIVETRACKINGCONWAYSCENE_H
#define GBA_SPRITE_ENGINE_PROJECT_ACTIVETRACKINGCONWAYSCENE_H
#include "ConwayScene.h"
struct cell {
bool taken;
int x, y;
};
class ActiveTrackingConwayScene : public ConwayScene {
private:
cell activeCellIndex[MAP_SIZE] = {0}, activeCellIndexBuffer[MAP_SIZE] = {0};
public:
ActiveTrackingConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed);
void tick(u16 keys) override;
void postload() override;
};
#endif //GBA_SPRITE_ENGINE_PROJECT_ACTIVETRACKINGCONWAYSCENE_H

View File

@ -2,19 +2,18 @@
// Created by Wouter Groeneveld on 30/11/18.
//
#include "ConwayScene.h"
#include "bg.h"
#include <libgba-sprite-engine/gba_engine.h>
#include <libgba-sprite-engine/sprites/sprite_builder.h>
#include <libgba-sprite-engine/gba/tonc_core.h>
#include <libgba-sprite-engine/background/text_stream.h>
#include "ConwayScene.h"
#include "bg.h"
// See http://www.coranac.com/tonc/text/gfx.htm#cd-qran
int __qran_seed= 42; // Seed / rnd holder
ConwayScene::ConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed) : Scene(engine), percentageSeed(percentageSeed) {
}
std::vector<Sprite *> ConwayScene::sprites() {
return {};
}
@ -23,25 +22,9 @@ std::vector<Background *> ConwayScene::backgrounds() {
return { bg.get() };
}
int ConwayScene::countAmountOfNeighbouringCellsAlive(int x, int y) {
int amountOfNeightbouringCellsAlive = 0;
int pos = y * MAP_WIDTH + x;
for(int x_i = x - 1; x_i <= x + 1; x_i++) {
for(int y_j = y - 1; y_j <= y + 1; y_j++) {
int toCheckPos = y_j * MAP_WIDTH + x_i;
if(toCheckPos >= 0 && toCheckPos < MAP_SIZE - 1 && pos != toCheckPos && map[toCheckPos] == ALIVE) {
amountOfNeightbouringCellsAlive++;
}
}
}
return amountOfNeightbouringCellsAlive;
}
u16 ConwayScene::getNextState(int x, int y) {
int pos = y * MAP_WIDTH + x;
int amountAlive = countAmountOfNeighbouringCellsAlive(x, y);
int amountAlive = countAmountOfNeighbouringCellsAlive(pos, x, y);
int currentState = map[pos];
@ -58,6 +41,32 @@ u16 ConwayScene::getNextState(int x, int y) {
}
}
int ConwayScene::countAmountOfNeighbouringCellsAlive(int pos, int x, int y) {
int amountOfNeightbouringCellsAlive = 0;
for(int x_i = x - 1; x_i <= x + 1; x_i++) {
for(int y_j = y - 1; y_j <= y + 1; y_j++) {
int toCheckPos = y_j * MAP_WIDTH + x_i;
if(toCheckPos >= 0 && toCheckPos < MAP_SIZE - 1 && pos != toCheckPos && map[toCheckPos] == ALIVE) {
amountOfNeightbouringCellsAlive++;
}
}
}
return amountOfNeightbouringCellsAlive;
}
void ConwayScene::load() {
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(conway_palette, sizeof(conway_palette)));
seedRandomMap(((MAP_WIDTH * MAP_HEIGHT) / 100) * percentageSeed);
bg = std::unique_ptr<Background>(new Background(1, conway_data, sizeof(conway_data), map, sizeof(map)));
bg.get()->useMapScreenBlock(16);
postload();
}
void ConwayScene::seedRandomMap(int seedcount) {
for(int i = 0; i < MAP_SIZE; i++) {
map[i] = DEAD;
@ -70,30 +79,3 @@ void ConwayScene::seedRandomMap(int seedcount) {
}
}
void ConwayScene::load() {
backgroundPalette = std::unique_ptr<BackgroundPaletteManager>(new BackgroundPaletteManager(conway_palette, sizeof(conway_palette)));
seedRandomMap(((MAP_WIDTH * MAP_HEIGHT) / 100) * percentageSeed);
bg = std::unique_ptr<Background>(new Background(1, conway_data, sizeof(conway_data), map, sizeof(map)));
bg.get()->useMapScreenBlock(16);
}
void ConwayScene::tick(u16 keys) {
generation++;
int totalAmountAlive = 0;
dma3_cpy(buffer, map, sizeof(buffer));
for(int w = 0; w < MAP_WIDTH; w++) {
for(int h = 0; h < MAP_HEIGHT; h++) {
u16 state = getNextState(w, h);
if(state == ALIVE) totalAmountAlive++;
buffer[h * MAP_WIDTH + w] = state;
}
}
TextStream::instance().setText(std::string("amount alive: ") + std::to_string(totalAmountAlive) + std::string(" of ") + std::to_string(MAP_SIZE), 1, 1);
TextStream::instance().setText(std::string("generation: ") + std::to_string(generation), 2, 1);
dma3_cpy(map, buffer, sizeof(map));
bg.get()->updateMap(buffer);
}

View File

@ -5,6 +5,7 @@
#ifndef GBA_SPRITE_ENGINE_PROJECT_CONWAYSCENE_H
#define GBA_SPRITE_ENGINE_PROJECT_CONWAYSCENE_H
#define MAP_WIDTH 64
#define MAP_HEIGHT 64
#define MAP_SIZE 64 * 64
@ -14,19 +15,20 @@
#include <libgba-sprite-engine/scene.h>
class ConwayScene : public Scene {
private:
protected:
u8 percentageSeed;
u16 generation;
std::unique_ptr<Background> bg;
u16 map[MAP_SIZE], buffer[MAP_SIZE];
int countAmountOfNeighbouringCellsAlive(int x, int y);
void seedRandomMap(int seedcount);
int countAmountOfNeighbouringCellsAlive(int pos, int x, int y);
u16 getNextState(int x, int y);
public:
ConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed);
ConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed) : Scene(engine), percentageSeed(percentageSeed) {}
std::vector<Sprite *> sprites() override;
@ -34,7 +36,9 @@ public:
void load() override;
void tick(u16 keys) override;
virtual void postload() = 0;
virtual void tick(u16 keys) = 0;
};

View File

@ -8,9 +8,10 @@
#include <libgba-sprite-engine/gba_engine.h>
#include "ConwaySeeder.h"
#include "bg.h"
#include "ConwayScene.h"
#include "NaiveConwayScene.h"
#include "ActiveTrackingConwayScene.h"
ConwaySeeder::ConwaySeeder(const std::shared_ptr<GBAEngine> &engine) : Scene(engine), percentage(30), delta(0) {}
ConwaySeeder::ConwaySeeder(const std::shared_ptr<GBAEngine> &engine) : Scene(engine), percentage(30), delta(0), impl(1) {}
std::vector<Background *> ConwaySeeder::backgrounds() {
return {};
@ -33,15 +34,27 @@ void ConwaySeeder::load() {
.buildPtr();
TextStream::instance().setText(std::string("Conways Game of Life"), 1, 1);
TextStream::instance().setText(std::string("Up/Down to adjust"), 10, 1);
TextStream::instance().setText(std::string("Start to render"), 11, 1);
TextStream::instance().setText(std::string("Up/Down to adjust seed"), 10, 1);
TextStream::instance().setText(std::string("A/B to adjust impl"), 11, 1);
TextStream::instance().setText(std::string("Start to render"), 12, 1);
}
void ConwaySeeder::tick(u16 keys) {
TextStream::instance().setText(std::string("Seed percentage: ") + std::to_string(percentage), 5, 1);
TextStream::instance().setText(std::string("Seed impl: ") + (impl == 1 ? std::string("naive") : std::string("fast")), 6, 1);
if(keys & KEY_A) {
impl = 1;
} else if(keys & KEY_B) {
impl = 2;
}
if(keys & KEY_START) {
engine.get()->setScene(new ConwayScene(engine, percentage));
if(impl == 1) {
engine.get()->setScene(new NaiveConwayScene(engine, percentage));
} else {
engine.get()->setScene(new ActiveTrackingConwayScene(engine, percentage));
}
} else if(keys & KEY_UP && percentage < 90) {
delta = 1;
} else if(keys & KEY_DOWN && percentage > 10) {

View File

@ -11,6 +11,7 @@
class ConwaySeeder : public Scene {
private:
int delta;
int impl;
int percentage;
std::unique_ptr<Sprite> conwaySprite;

View File

@ -0,0 +1,40 @@
//
// Created by Wouter Groeneveld on 30/11/18.
//
#include <libgba-sprite-engine/gba_engine.h>
#include <libgba-sprite-engine/sprites/sprite_builder.h>
#include <libgba-sprite-engine/gba/tonc_core.h>
#include <libgba-sprite-engine/background/text_stream.h>
#include "NaiveConwayScene.h"
#include "ConwayScene.h"
void NaiveConwayScene::tick(u16 keys) {
generation++;
int totalAmountAlive = 0;
dma3_cpy(buffer, map, sizeof(buffer));
// Naïve implementation:
// 1. O(n^2) loop
// 2. for each element, check x-1, x, x+y, y-1, y,y+1
// totals to min. 4096 * 8 = +32.768 instructions, each update(), at only 16.8 MHz!
for(int w = 0; w < MAP_WIDTH; w++) {
for(int h = 0; h < MAP_HEIGHT; h++) {
u16 state = getNextState(w, h);
if(state == ALIVE) totalAmountAlive++;
buffer[h * MAP_WIDTH + w] = state;
}
}
TextStream::instance().setText(std::string("amount alive: ") + std::to_string(totalAmountAlive) + std::string(" of ") + std::to_string(MAP_SIZE), 1, 1);
TextStream::instance().setText(std::string("generation: ") + std::to_string(generation), 2, 1);
dma3_cpy(map, buffer, sizeof(map));
bg.get()->updateMap(map);
}
void NaiveConwayScene::postload() {
}

View File

@ -0,0 +1,25 @@
//
// Created by Wouter Groeneveld on 30/11/18.
//
#ifndef GBA_SPRITE_ENGINE_PROJECT_NAIVE_CONWAYSCENE_H
#define GBA_SPRITE_ENGINE_PROJECT_NAIVE_CONWAYSCENE_H
#include "ConwayScene.h"
class NaiveConwayScene : public ConwayScene {
private:
int countAmountOfNeighbouringCellsAlive(int pos, int x, int y);
public:
NaiveConwayScene(const std::shared_ptr<GBAEngine> &engine, u8 percentageSeed) : ConwayScene(engine, percentageSeed) {}
void tick(u16 keys) override;
void postload() override;
};
#endif //GBA_SPRITE_ENGINE_PROJECT_CONWAYSCENE_H