Skip to content

Optimize grid updates #40

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions p5js/tiled-model/cell.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class Cell {
constructor(value) {
// Initialize the cell as not collapsed
this.collapsed = false;
constructor(i, j, value) {
this.i = i;
this.j = j;
// Is an array passed in?
if (value instanceof Array) {
// Set options to the provided array
Expand All @@ -13,5 +13,29 @@ class Cell {
this.options[i] = i;
}
}
this.collapsed = this.options.length == 1;
}

draw(w, h) {
if (this.collapsed) {
let index = this.options[0];
image(tiles[index].img, this.i * w, this.j * h, w, h);
} else {
noFill();
stroke(51);
rect(this.i * w, this.j * h, w, h);
}
}

validOptions(dir) {
let validOptions = new Set();
for (let option of this.options) {
let valid = tiles[option].compatibles(dir);
for (let opt of valid) {
validOptions.add(opt);
}
}

return validOptions;
}
}
160 changes: 72 additions & 88 deletions p5js/tiled-model/sketch.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
let tiles = [];
const tileImages = [];

let grid = [];

const DIM = 25;
const NB_UPDATES_PER_TICK=1;

function preload() {
// Load tile images
Expand Down Expand Up @@ -70,15 +70,15 @@ function setup() {
function startOver() {
// Initialize grid with cells
for (let i = 0; i < DIM * DIM; i++) {
grid[i] = new Cell(tiles.length);
grid[i] = new Cell(i % DIM, floor(i / DIM), tiles.length);
}
}

function checkValid(arr, valid) {
// Remove invalid options from array
for (let i = arr.length - 1; i >= 0; i--) {
let element = arr[i];
if (!valid.includes(element)) {
if (!valid.has(element)) {
arr.splice(i, 1);
}
}
Expand All @@ -92,102 +92,86 @@ function draw() {
const h = height / DIM;
for (let j = 0; j < DIM; j++) {
for (let i = 0; i < DIM; i++) {
let cell = grid[i + j * DIM];
if (cell.collapsed) {
let index = cell.options[0];
image(tiles[index].img, i * w, j * h, w, h);
} else {
noFill();
stroke(51);
rect(i * w, j * h, w, h);
}
grid[posIdx(i, j)].draw(w, h);
}
}

// Pick cell with least entropy
let gridCopy = grid.slice();
gridCopy = gridCopy.filter((a) => !a.collapsed);
for (let idxUpdate = 0; idxUpdate < NB_UPDATES_PER_TICK; idxUpdate++) {
// Pick cell with least entropy
let gridCopy = grid.slice();
gridCopy = gridCopy.filter((a) => !a.collapsed);

if (gridCopy.length == 0) {
return;
}
gridCopy.sort((a, b) => {
return a.options.length - b.options.length;
});

let len = gridCopy[0].options.length;
let stopIndex = 0;
for (let i = 1; i < gridCopy.length; i++) {
if (gridCopy[i].options.length > len) {
stopIndex = i;
break;
if (gridCopy.length == 0) {
return;
}
}
gridCopy.sort((a, b) => {
return a.options.length - b.options.length;
});

let len = gridCopy[0].options.length;
let stopIndex = 0;
for (let i = 1; i < gridCopy.length; i++) {
if (gridCopy[i].options.length > len) {
stopIndex = i;
break;
}
}

if (stopIndex > 0) gridCopy.splice(stopIndex);
const cell = random(gridCopy);
cell.collapsed = true;
const pick = random(cell.options);
if (pick === undefined) {
startOver();
return;
}
cell.options = [pick];

if (stopIndex > 0) gridCopy.splice(stopIndex);
const cell = random(gridCopy);
cell.collapsed = true;
const pick = random(cell.options);
if (pick === undefined) {
startOver();
return;
grid = nextGrid(cell);
}
cell.options = [pick];
}

// Update grid with new options
const nextGrid = [];
for (let j = 0; j < DIM; j++) {
for (let i = 0; i < DIM; i++) {
let index = i + j * DIM;
if (grid[index].collapsed) {
nextGrid[index] = grid[index];
} else {
let options = new Array(tiles.length).fill(0).map((x, i) => i);
// Look up
if (j > 0) {
let up = grid[i + (j - 1) * DIM];
let validOptions = [];
for (let option of up.options) {
let valid = tiles[option].down;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look right
if (i < DIM - 1) {
let right = grid[i + 1 + j * DIM];
let validOptions = [];
for (let option of right.options) {
let valid = tiles[option].left;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look down
if (j < DIM - 1) {
let down = grid[i + (j + 1) * DIM];
let validOptions = [];
for (let option of down.options) {
let valid = tiles[option].up;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look left
if (i > 0) {
let left = grid[i - 1 + j * DIM];
let validOptions = [];
for (let option of left.options) {
let valid = tiles[option].right;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// propagate options from src to dest. If dest is above src, dir == UP.
function propagate(src, dest, dir) {
let oldLen = dest.options.length;
checkValid(dest.options, src.validOptions(dir));
return oldLen != dest.options.length;
}

function nextGrid(pick) {
let touched = [posIdx(pick.i, pick.j)];

while (touched.length > 0) {
let cell = grid[touched.pop()];

nextGrid[index] = new Cell(options);
let check = function (i, j, dir) {
const idx = posIdx(i, j);
if (propagate(cell, grid[idx], dir)) {
if (!touched.includes(idx)) {
touched.push(idx);
}
}
};

if (cell.i > 0) {
check(cell.i - 1, cell.j, LEFT);
}

if (cell.i < DIM - 1) {
check(cell.i + 1, cell.j, RIGHT);
}

if (cell.j > 0) {
check(cell.i, cell.j - 1, UP);
}

if (cell.j < DIM - 1) {
check(cell.i, cell.j + 1, DOWN);
}
}
return grid;
}

grid = nextGrid;
function posIdx(i, j) {
return i + j * DIM;
}
28 changes: 23 additions & 5 deletions p5js/tiled-model/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ function reverseString(s) {
}

// Compare if one edge matches the reverse of another
function compareEdge(a, b) {
function compatibleEdges(a, b) {
return a == reverseString(b);
}

const UP = 0;
const RIGHT = 1;
const DOWN = 2;
const LEFT = 3;

// Class for each tile
class Tile {
constructor(img, edges, i) {
Expand All @@ -34,24 +39,37 @@ class Tile {
if (tile.index == 5 && this.index == 5) continue;

// Check if the current tile's bottom edge matches this tile's top edge
if (compareEdge(tile.edges[2], this.edges[0])) {
if (compatibleEdges(tile.edges[DOWN], this.edges[UP])) {
this.up.push(i);
}
// Check if the current tile's left edge matches this tile's right edge
if (compareEdge(tile.edges[3], this.edges[1])) {
if (compatibleEdges(tile.edges[LEFT], this.edges[RIGHT])) {
this.right.push(i);
}
// Check if the current tile's top edge matches this tile's bottom edge
if (compareEdge(tile.edges[0], this.edges[2])) {
if (compatibleEdges(tile.edges[UP], this.edges[DOWN])) {
this.down.push(i);
}
// Check if the current tile's right edge matches this tile's left edge
if (compareEdge(tile.edges[1], this.edges[3])) {
if (compatibleEdges(tile.edges[RIGHT], this.edges[LEFT])) {
this.left.push(i);
}
}
}

compatibles(dir) {
switch (dir) {
case UP:
return this.up;
case RIGHT:
return this.right;
case DOWN:
return this.down;
case LEFT:
return this.left;
}
}

// Rotate the tile image and edges
rotate(num) {
const w = this.img.width;
Expand Down