import 'dart:html' as html; import 'dart:math' as math; import 'package:rules_of_living/src/Grid.dart'; import 'package:rules_of_living/src/RuleSet.dart'; enum CellPattern { SpaceShip, Blinker } class Simulation { final Grid map; RuleSet rules = GameOfLife(); bool _dirty = true; bool _renderEdges = true; int _startingSeed; int _x; int _y; int _amount; int _dispersal; CellPattern _pattern; int get w => map.width; int get h => map.height; Simulation(int w, int h) : this.map = new Grid(w, h) { reset(); print("Grid Created"); } void reset() { map.setAll(0, List.filled(map.length, false)); _dirty = true; } void addPattern( {CellPattern pattern, int x, int y, int amount, int dispersal, int seed}) { _startingSeed = seed ?? DateTime.now().millisecondsSinceEpoch; math.Random rng = new math.Random(_startingSeed); _x = x; _y = y; _amount = amount ?? rng.nextInt(20); _dispersal = dispersal ?? 10; _pattern = pattern; int cx = x ?? rng.nextInt(map.width ~/ 3) + (map.width ~/ 3); int cy = y ?? rng.nextInt(map.height ~/ 3) + (map.height ~/ 3); switch (pattern) { default: int sanityCheck = 0; for (var i = 0; i < (_amount); i++) { sanityCheck++; getCellState(cx, cy) ? i-- : setCellState(cx + rng.nextInt(_dispersal), cy + rng.nextInt(_dispersal), true); if (sanityCheck > 100 && sanityCheck > i * 3) break; } break; } _dirty = true; } void setCellState(int x, int y, bool state) { if (y >= map.height || x >= map.width) return null; state ? map.set(x, y, true) : map.set(x, y, false); } bool getCellState(int x, int y) { if (y >= map.height || x >= map.width) return null; return map.get(x, y); } void toggleCellState(int x, int y) { if (y >= map.height || x >= map.width) return null; getCellState(x, y) == null ? setCellState(x, y, true) : setCellState(x, y, false); } Map update() { Map stateChanges = calculateNextState(map); stateChanges.forEach((i, el) => map[i] = el); stateChanges.length != 0 ? _dirty = true : false; return stateChanges; } Map calculateNextState(Grid oldState) { Map stateChanges = Map(); for (int i = 0; i < map.length; i++) { math.Point p = map.toCoordinates(i); bool cell = map[i]; int neighbors = getNeighbors(p.x, p.y, 1); if (cell == false && rules.checkBirth(neighbors) == true) { stateChanges[i] = true; } else if (cell == true && rules.checkSurvival(neighbors) == false) { stateChanges[i] = false; } } return stateChanges; } int getNeighbors(int x, int y, int range) { int count = 0; for (int ix = -range + x; ix <= range + x; ix++) { for (int iy = -range + y; iy <= range + y; iy++) { if (ix >= 0 && iy >= 0 && ix < map.width && iy < map.height && getCellState(ix, iy) == true && !(x == ix && y == iy)) count++; } } return count; } void render(html.CanvasElement canvas, [num interp]) { // only renders if any cells changed between renders if (!_dirty) return; html.CanvasRenderingContext2D ctx = canvas.getContext('2d'); int brickW = (canvas.width ~/ map.width); int brickH = (canvas.height ~/ map.height); ctx.clearRect(0, 0, canvas.width, canvas.height); for (int i = 0; i < map.length; i++) { math.Point p = map.toCoordinates(i); if (_renderEdges) { ctx.setStrokeColorRgb(100, 100, 100); ctx.strokeRect(p.x * brickW, p.y * brickH, brickW, brickH); } if (map[i] == true) ctx.setFillColorRgb(155, 155, 255); else ctx.setFillColorRgb(0, 0, 0); ctx.fillRect(p.x * brickW, p.y * brickH, brickW, brickH); } _dirty = false; } void set renderEdges(bool on) { _renderEdges = on; _dirty = true; } bool get renderEdges => _renderEdges; }