diff --git a/.gitignore b/.gitignore
index 496ee2c..91dfed8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-.DS_Store
\ No newline at end of file
+.DS_Store
+node_modules
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..16fd9f4
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,7 @@
+{
+ "switchSettings.current": {
+ "env": {
+ "userindex": 0
+ }
+ }
+}
\ No newline at end of file
diff --git a/grid.js b/grid.js
new file mode 100644
index 0000000..2d007ea
--- /dev/null
+++ b/grid.js
@@ -0,0 +1,140 @@
+///
+
+/**
+ * @typedef {{
+ * initializer?:()=>Cell
+ * }} TileGridOptions
+ */
+
+
+class TileGrid {
+
+ /**
+ * @type {number}
+ */
+ dimensions
+
+ /**
+ * @type {Array| }
+ */
+ grid = []
+
+
+ /**
+ *
+ * @param {number} dimensions
+ * @param {TileGridOptions} [options]
+ */
+ constructor(dimensions, options) {
+ this.dimensions = dimensions
+ this.options = options
+
+
+ const initializer = this.options.initializer
+ if(initializer) {
+ this.forEachPosition(pos => {
+ this.grid[this.vectorToIndex(pos)] = initializer(pos)
+ })
+ }
+ }
+
+ getMapSize() {
+ return createVector(this.dimensions, this.dimensions)
+ }
+ /**
+ *
+ * @param {import("p5").Vector} position
+ */
+ getCellForPosition(position) {
+ return this.grid[this.vectorToIndex(position)];
+ }
+
+ /**
+ *
+ * @param {((pos: import("p5").Vector) => void)} cb
+ */
+ forEachPosition(cb) {
+ for(let y = 0; y < this.getMapSize().x; y++) {
+ for(let x = 0; x < this.getMapSize().y; x++) {
+ cb(createVector(y, x));
+ }
+ }
+ }
+ /**
+ * @param { import("p5").Vector} position
+ * @param {((pos: import("p5").Vector,directionString:"up"|"down"|"left"|"right") => void)} cb
+ */
+ forEachDirection(position, cb) {
+ const directions = [
+ [this.up(position), "up"],
+ [this.down(position), "down"],
+ [this.left(position), "left"],
+ [this.right(position), "right"]]
+ for(const direction of directions) {
+ if(direction !== null) {
+ cb(direction[0], direction[1]);
+ }
+ }
+ }
+ /**
+ * @param {import("p5").Vector} vector
+ */
+ vectorToIndex(vector) {
+ return vector.y * this.getMapSize().x + vector.x
+ }
+
+ /**
+ *
+ * @param {import("p5").Vector} vector
+ */
+ validPosition(vector) {
+ return vector.x >= 0 && vector.y >= 0
+ && vector.x <= this.getMapSize().x && vector.y <= this.getMapSize().y
+ && vector.x % 1 === 0 && vector.y % 1 === 0
+ }
+
+ /**
+ * @param {import("p5").Vector} vector
+ * @param {number} [increments]
+ */
+ up(vector, increments = 1) {
+ const newVector = vector.add(0, -1 * increments)
+ if(!this.validPosition(newVector)) {
+ return null;
+ }
+ return newVector
+ }
+ /**
+ * @param {import("p5").Vector} vector
+ * @param {number} [increments]
+ */
+ down(vector, increments = 1) {
+ const newVector = vector.add(0, 1 * increments)
+ if(!this.validPosition(newVector)) {
+ return null;
+ }
+ return newVector
+ }
+ /**
+ * @param {import("p5").Vector} vector
+ * @param {number} [increments]
+ */
+ right(vector, increments = 1) {
+ const newVector = vector.add(1 * increments, 0)
+ if(!this.validPosition(newVector)) {
+ return null;
+ }
+ return newVector
+ }
+ /**
+ * @param {import("p5").Vector} vector
+ * @param {number} [increments]
+ */
+ left(vector, increments = 1) {
+ const newVector = vector.add(-1 * increments, 0)
+ if(!this.validPosition(newVector)) {
+ return null;
+ }
+ return newVector
+ }
+}
\ No newline at end of file
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 0000000..7195a00
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1,3 @@
+import "p5/global"
+import "p5/constants"
+import "p5"
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..ed40692
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,26 @@
+{
+ "name": "Wave-Function-Collapse",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "@types/p5": "^1.4.2"
+ }
+ },
+ "node_modules/@types/p5": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.2.tgz",
+ "integrity": "sha512-tzJ2PdmeXlX8tidbA1/pQEhs0MHVWam0K4ux5ri0GrZXhBU3QrpTpSVzNaBDuo6KheryHdH8wR82x1nPvxo42g==",
+ "dev": true
+ }
+ },
+ "dependencies": {
+ "@types/p5": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.2.tgz",
+ "integrity": "sha512-tzJ2PdmeXlX8tidbA1/pQEhs0MHVWam0K4ux5ri0GrZXhBU3QrpTpSVzNaBDuo6KheryHdH8wR82x1nPvxo42g==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3ef2119
--- /dev/null
+++ b/package.json
@@ -0,0 +1,5 @@
+{
+ "devDependencies": {
+ "@types/p5": "^1.4.2"
+ }
+}
diff --git a/sketch.js b/sketch.js
index a3696aa..d6e1c3f 100644
--- a/sketch.js
+++ b/sketch.js
@@ -1,7 +1,10 @@
let tiles = [];
const tileImages = [];
-let grid = [];
+/**
+ * @type {TileGrid}
+ */
+let grid;
const DIM = 25;
@@ -12,14 +15,14 @@ function preload() {
// }
const path = 'tiles/circuit-coding-train';
- for (let i = 0; i < 13; i++) {
+ for(let i = 0; i < 13; i++) {
tileImages[i] = loadImage(`${path}/${i}.png`);
}
}
function removeDuplicatedTiles(tiles) {
const uniqueTilesMap = {};
- for (const tile of tiles) {
+ for(const tile of tiles) {
const key = tile.edges.join(','); // ex: "ABB,BCB,BBA,AAA"
uniqueTilesMap[key] = tile;
}
@@ -53,14 +56,14 @@ function setup() {
tiles[11] = new Tile(tileImages[11], ['BCB', 'BCB', 'BBB', 'BBB']);
tiles[12] = new Tile(tileImages[12], ['BBB', 'BCB', 'BBB', 'BCB']);
- for (let i = 0; i < 12; i++) {
+ for(let i = 0; i < 12; i++) {
tiles[i].index = i;
}
const initialTileCount = tiles.length;
- for (let i = 0; i < initialTileCount; i++) {
+ for(let i = 0; i < initialTileCount; i++) {
let tempTiles = [];
- for (let j = 0; j < 4; j++) {
+ for(let j = 0; j < 4; j++) {
tempTiles.push(tiles[i].rotate(j));
}
tempTiles = removeDuplicatedTiles(tempTiles);
@@ -69,7 +72,7 @@ function setup() {
console.log(tiles.length);
// Generate the adjacency rules based on edges
- for (let i = 0; i < tiles.length; i++) {
+ for(let i = 0; i < tiles.length; i++) {
const tile = tiles[i];
tile.analyze(tiles);
}
@@ -78,21 +81,20 @@ function setup() {
}
function startOver() {
- // Create cell for each spot on the grid
- for (let i = 0; i < DIM * DIM; i++) {
- grid[i] = new Cell(tiles.length);
- }
+ grid = new TileGrid(DIM, {
+ initializer: () => new Cell(tiles.length)
+ })
}
function checkValid(arr, valid) {
//console.log(arr, valid);
- for (let i = arr.length - 1; i >= 0; i--) {
+ for(let i = arr.length - 1; i >= 0; i--) {
// VALID: [BLANK, RIGHT]
// ARR: [BLANK, UP, RIGHT, DOWN, LEFT]
// result in removing UP, DOWN, LEFT
let element = arr[i];
// console.log(element, valid.includes(element));
- if (!valid.includes(element)) {
+ if(!valid.includes(element)) {
arr.splice(i, 1);
}
}
@@ -109,10 +111,10 @@ function draw() {
const w = width / DIM;
const h = height / DIM;
- for (let j = 0; j < DIM; j++) {
- for (let i = 0; i < DIM; i++) {
+ for(let j = 0; j < DIM; j++) {
+ for(let i = 0; i < DIM; i++) {
let cell = grid[i + j * DIM];
- if (cell.collapsed) {
+ if(cell.collapsed) {
let index = cell.options[0];
image(tiles[index].img, i * w, j * h, w, h);
} else {
@@ -129,7 +131,7 @@ function draw() {
// console.table(grid);
// console.table(gridCopy);
- if (gridCopy.length == 0) {
+ if(gridCopy.length == 0) {
return;
}
gridCopy.sort((a, b) => {
@@ -138,66 +140,79 @@ function draw() {
let len = gridCopy[0].options.length;
let stopIndex = 0;
- for (let i = 1; i < gridCopy.length; i++) {
- if (gridCopy[i].options.length > len) {
+ for(let i = 1; i < gridCopy.length; i++) {
+ if(gridCopy[i].options.length > len) {
stopIndex = i;
break;
}
}
- if (stopIndex > 0) gridCopy.splice(stopIndex);
+ if(stopIndex > 0) gridCopy.splice(stopIndex);
const cell = random(gridCopy);
cell.collapsed = true;
const pick = random(cell.options);
- if (pick === undefined) {
+ if(pick === undefined) {
startOver();
return;
}
cell.options = [pick];
const nextGrid = [];
- for (let j = 0; j < DIM; j++) {
- for (let i = 0; i < DIM; i++) {
+
+ grid.forEachPosition(pos => {
+ grid.forEachDirection(pos, (directedPosition, directionStr) => {
+ const directedCell = grid.getCellForPosition(directedPosition)
+ let validOptions = [];
+ for(let option of directedCell.options) {
+ let valid = tiles[option].down;
+ validOptions = validOptions.concat(valid);
+ }
+ checkValid(options, validOptions);
+ })
+ })
+
+ for(let j = 0; j < DIM; j++) {
+ for(let i = 0; i < DIM; i++) {
let index = i + j * DIM;
- if (grid[index].collapsed) {
+ 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) {
+ if(j > 0) {
let up = grid[i + (j - 1) * DIM];
let validOptions = [];
- for (let option of up.options) {
+ for(let option of up.options) {
let valid = tiles[option].down;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look right
- if (i < DIM - 1) {
+ if(i < DIM - 1) {
let right = grid[i + 1 + j * DIM];
let validOptions = [];
- for (let option of right.options) {
+ for(let option of right.options) {
let valid = tiles[option].left;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look down
- if (j < DIM - 1) {
+ if(j < DIM - 1) {
let down = grid[i + (j + 1) * DIM];
let validOptions = [];
- for (let option of down.options) {
+ for(let option of down.options) {
let valid = tiles[option].up;
validOptions = validOptions.concat(valid);
}
checkValid(options, validOptions);
}
// Look left
- if (i > 0) {
+ if(i > 0) {
let left = grid[i - 1 + j * DIM];
let validOptions = [];
- for (let option of left.options) {
+ for(let option of left.options) {
let valid = tiles[option].right;
validOptions = validOptions.concat(valid);
}
|