diff --git a/demos/demo4-conway/CMakeLists.txt b/demos/demo4-conway/CMakeLists.txt index d539476..1a3b11f 100644 --- a/demos/demo4-conway/CMakeLists.txt +++ b/demos/demo4-conway/CMakeLists.txt @@ -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) diff --git a/demos/demo4-conway/src/ActiveTrackingConwayScene.cpp b/demos/demo4-conway/src/ActiveTrackingConwayScene.cpp new file mode 100644 index 0000000..e269015 --- /dev/null +++ b/demos/demo4-conway/src/ActiveTrackingConwayScene.cpp @@ -0,0 +1,69 @@ +// +// Created by Wouter Groeneveld on 30/11/18. +// + +#include +#include +#include "ActiveTrackingConwayScene.h" + +ActiveTrackingConwayScene::ActiveTrackingConwayScene(const std::shared_ptr &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++; + } + } + } +} \ No newline at end of file diff --git a/demos/demo4-conway/src/ActiveTrackingConwayScene.h b/demos/demo4-conway/src/ActiveTrackingConwayScene.h new file mode 100644 index 0000000..72a1458 --- /dev/null +++ b/demos/demo4-conway/src/ActiveTrackingConwayScene.h @@ -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 &engine, u8 percentageSeed); + + void tick(u16 keys) override; + + void postload() override; + +}; + + +#endif //GBA_SPRITE_ENGINE_PROJECT_ACTIVETRACKINGCONWAYSCENE_H diff --git a/demos/demo4-conway/src/ConwayScene.cpp b/demos/demo4-conway/src/ConwayScene.cpp index 02476b7..6a0e609 100644 --- a/demos/demo4-conway/src/ConwayScene.cpp +++ b/demos/demo4-conway/src/ConwayScene.cpp @@ -2,19 +2,18 @@ // Created by Wouter Groeneveld on 30/11/18. // +#include "ConwayScene.h" +#include "bg.h" + #include #include #include #include -#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 &engine, u8 percentageSeed) : Scene(engine), percentageSeed(percentageSeed) { -} - std::vector ConwayScene::sprites() { return {}; } @@ -23,25 +22,9 @@ std::vector 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(new BackgroundPaletteManager(conway_palette, sizeof(conway_palette))); + + seedRandomMap(((MAP_WIDTH * MAP_HEIGHT) / 100) * percentageSeed); + + bg = std::unique_ptr(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(new BackgroundPaletteManager(conway_palette, sizeof(conway_palette))); - - seedRandomMap(((MAP_WIDTH * MAP_HEIGHT) / 100) * percentageSeed); - - bg = std::unique_ptr(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); -} diff --git a/demos/demo4-conway/src/ConwayScene.h b/demos/demo4-conway/src/ConwayScene.h index 0f1c6e7..1968a11 100644 --- a/demos/demo4-conway/src/ConwayScene.h +++ b/demos/demo4-conway/src/ConwayScene.h @@ -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 + class ConwayScene : public Scene { -private: +protected: u8 percentageSeed; u16 generation; std::unique_ptr 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 &engine, u8 percentageSeed); + ConwayScene(const std::shared_ptr &engine, u8 percentageSeed) : Scene(engine), percentageSeed(percentageSeed) {} std::vector 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; }; diff --git a/demos/demo4-conway/src/ConwaySeeder.cpp b/demos/demo4-conway/src/ConwaySeeder.cpp index 253bab2..87c2d01 100644 --- a/demos/demo4-conway/src/ConwaySeeder.cpp +++ b/demos/demo4-conway/src/ConwaySeeder.cpp @@ -8,9 +8,10 @@ #include #include "ConwaySeeder.h" #include "bg.h" -#include "ConwayScene.h" +#include "NaiveConwayScene.h" +#include "ActiveTrackingConwayScene.h" -ConwaySeeder::ConwaySeeder(const std::shared_ptr &engine) : Scene(engine), percentage(30), delta(0) {} +ConwaySeeder::ConwaySeeder(const std::shared_ptr &engine) : Scene(engine), percentage(30), delta(0), impl(1) {} std::vector 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) { diff --git a/demos/demo4-conway/src/ConwaySeeder.h b/demos/demo4-conway/src/ConwaySeeder.h index 433b82d..d40a415 100644 --- a/demos/demo4-conway/src/ConwaySeeder.h +++ b/demos/demo4-conway/src/ConwaySeeder.h @@ -11,6 +11,7 @@ class ConwaySeeder : public Scene { private: int delta; + int impl; int percentage; std::unique_ptr conwaySprite; diff --git a/demos/demo4-conway/src/NaiveConwayScene.cpp b/demos/demo4-conway/src/NaiveConwayScene.cpp new file mode 100644 index 0000000..0314427 --- /dev/null +++ b/demos/demo4-conway/src/NaiveConwayScene.cpp @@ -0,0 +1,40 @@ +// +// Created by Wouter Groeneveld on 30/11/18. +// + +#include +#include +#include +#include + +#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() { + +} diff --git a/demos/demo4-conway/src/NaiveConwayScene.h b/demos/demo4-conway/src/NaiveConwayScene.h new file mode 100644 index 0000000..c37e11d --- /dev/null +++ b/demos/demo4-conway/src/NaiveConwayScene.h @@ -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 &engine, u8 percentageSeed) : ConwayScene(engine, percentageSeed) {} + + void tick(u16 keys) override; + + void postload() override; +}; + + +#endif //GBA_SPRITE_ENGINE_PROJECT_CONWAYSCENE_H