Skip to content

Instantly share code, notes, and snippets.

@MrSmith33
Created September 22, 2017 13:56
Show Gist options
  • Save MrSmith33/9cdb78ab35deac3f02847b5dca9fcfde to your computer and use it in GitHub Desktop.
Save MrSmith33/9cdb78ab35deac3f02847b5dca9fcfde to your computer and use it in GitHub Desktop.
Game of life with voxelman GUI
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