Created
September 22, 2017 13:56
-
-
Save MrSmith33/9cdb78ab35deac3f02847b5dca9fcfde to your computer and use it in GitHub Desktop.
Game of life with voxelman GUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module main; | |
import voxelman.graphics; | |
import voxelman.gui; | |
import voxelman.log; | |
import voxelman.math; | |
import voxelman.text.scale; | |
void main(string[] args) | |
{ | |
import std.stdio : stdout; | |
auto conciseLogger = new ConciseLogger(stdout); | |
conciseLogger.logLevel = LogLevel.info; | |
sharedLog = conciseLogger; | |
auto app = new App("Cells", ivec2(800, 800)); | |
app.run(args); | |
} | |
class App : GuiApp | |
{ | |
Bitmap bitmap; | |
Texture texture; | |
World world; | |
WidgetProxy imageWidget; | |
int scale = 4; | |
bool runSimulation; | |
enum minScale = 1; | |
enum maxScale = 10; | |
this(string title, ivec2 windowSize) | |
{ | |
super(title, windowSize); | |
maxFps = 60; | |
} | |
override void load(string[] args) | |
{ | |
super.load(args); | |
window.keyPressed.connect(&onKey); | |
bitmap = new Bitmap(0, 0); | |
texture = new Texture(TextureTarget.target2d, TextureFormat.rgba); | |
WidgetProxy root = WidgetProxy(guictx.roots[0], guictx); | |
root.setVLayout(0, padding4(0)); | |
auto viewport = root.createChild().hvexpand.handlers(&onViewportScroll); | |
imageWidget = viewport.createImage(ImageData(texture, irect(ivec2(0,0), bitmap.size), 4)).makeDraggable(PointerButton.PB_3); | |
imageWidget.handlers(&onGridPress, &onGridRelease, &onGridClick).addBorder; | |
root.hline; | |
auto buttons = PanelLogic.create(root, color_gray).hexpand.setHLayout(2, padding4(2)); | |
buttons.createTextButton("+", &plusScale); | |
buttons.createTextButton("-", &minusScale); | |
buttons.createTextButton("Reset", &world.reset); | |
buttons.createTextButton("Step", &world.simulateStep); | |
buttons.createTextButton("Run", &runPause); | |
world.init(); | |
} | |
override void userUpdate(double delta) | |
{ | |
if (runSimulation) world.simulateStep(); | |
world.fillBitmap(bitmap); | |
auto imageData = imageWidget.get!ImageData; | |
imageData.subRect = irect(ivec2(0,0), bitmap.size); | |
imageData.scale = scale; | |
texture.loadFromImage(bitmap); | |
debugText.putfln("FPS: %.1f", fpsHelper.fps); | |
debugText.putfln("Delta: %ss", scaledNumberFmt(fpsHelper.updateTime)); | |
} | |
void onKey(KeyCode key, uint modifiers) | |
{ | |
if (key == KeyCode.KEY_TILDE) | |
showDebugInfo = !showDebugInfo; | |
} | |
void plusScale() { scale = min(scale + 1, maxScale); } | |
void minusScale() { scale = max(scale - 1, minScale); } | |
void runPause() { runSimulation.toggle_bool; } | |
void onGridPress(WidgetProxy widget, ref PointerPressEvent event) { | |
if (event.button == PointerButton.PB_1 && event.bubbling) event.handled = true; | |
} | |
void onGridRelease(WidgetProxy widget, ref PointerReleaseEvent event) { | |
if (event.button == PointerButton.PB_1 && event.bubbling) event.handled = true; | |
} | |
void onGridClick(WidgetProxy widget, ref PointerClickEvent event) | |
{ | |
auto transform = widget.get!WidgetTransform; | |
ivec2 imagePos = event.pointerPosition - transform.absPos; | |
ivec2 gridPos = imagePos / scale; | |
world.onClick(gridPos); | |
} | |
void onViewportScroll(WidgetProxy widget, ref ScrollEvent event) { | |
auto power = cast(int)event.delta.y; | |
if (power > 0) scale = max(scale >> power, minScale); | |
else scale = min(scale << (-power), maxScale); | |
event.handled = true; | |
} | |
} | |
struct World | |
{ | |
Grid grid; | |
Grid nextGrid; | |
int size = 50; | |
void init() | |
{ | |
grid.init(size); | |
nextGrid.init(size); | |
reset(); | |
} | |
void reset() | |
{ | |
foreach(ref cell; grid.cells) | |
{ | |
import std.random; | |
cell.data = cast(ubyte)dice(70, 1); | |
} | |
} | |
void simulateSteps(int numSteps) | |
{ | |
foreach(_; 0..numSteps) simulateStep(); | |
} | |
void simulateStep() | |
{ | |
foreach(int i; 0..cast(int)grid.cells.length) | |
{ | |
immutable x = i % grid.w; | |
immutable y = i / grid.w; | |
size_t aliveNeghbours = | |
grid[x-1, y-1].data + grid[x+0, y-1].data + grid[x+1, y-1].data + | |
grid[x-1, y+0].data + grid[x+1, y+0].data + | |
grid[x-1, y+1].data + grid[x+0, y+1].data + grid[x+1, y+1].data; | |
static immutable ubyte[9] newAliveData = [0, 0, 1, 1, 0, 0, 0, 0, 0]; | |
static immutable ubyte[9] newDeadData = [0, 0, 0, 1, 0, 0, 0, 0, 0]; | |
if (grid[i].data == 1) | |
nextGrid[i].data = newAliveData[aliveNeghbours]; | |
else | |
nextGrid[i].data = newDeadData[aliveNeghbours]; | |
} | |
swapGrids(); | |
} | |
void swapGrids() | |
{ | |
auto temp = grid; | |
grid = nextGrid; | |
nextGrid = temp; | |
} | |
void onClick(ivec2 at) | |
{ | |
grid[at.x, at.y].data ^= 1; | |
} | |
void fillBitmap(Bitmap bitmap) | |
{ | |
bitmap.resize(grid.size); | |
size_t i; | |
foreach(y; 0..grid.w) | |
foreach(x; 0..grid.h) | |
{ | |
bitmap[x, y] = grid.cells[i].color; | |
++i; | |
} | |
} | |
} | |
struct Grid | |
{ | |
int w = 1; | |
int h = 1; | |
Cell[] cells; | |
ivec2 size() { return ivec2(w, h); } | |
void init(int s) { init(s, s); } | |
void init(int _w, int _h) | |
{ | |
w = _w; | |
h = _h; | |
cells = new Cell[w * h]; | |
} | |
ref Cell opIndex(int i) | |
{ | |
return cells[i]; | |
} | |
ref Cell opIndex(int x, int y) | |
{ | |
immutable effectiveX = (x + w) % w; | |
immutable effectiveY = (y + h) % h; | |
immutable index = effectiveX + effectiveY * w; | |
return cells[index]; | |
} | |
} | |
struct Cell | |
{ | |
ubyte data; | |
Color4ub color() { | |
if (data) return Colors.black; | |
return Colors.white; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment