squishjs 0.6.42 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/.github/workflows/node.js.yml +1 -1
  2. package/LICENSE.md +21 -0
  3. package/README.md +116 -42
  4. package/index.js +11 -1
  5. package/oldsvgexport.js +89 -0
  6. package/package.json +1 -1
  7. package/render-tester.js +120 -0
  8. package/src/BaseNode.js +101 -0
  9. package/src/Game.js +15 -3
  10. package/src/GameNode.js +82 -79
  11. package/src/InternalGameNode.js +4 -3
  12. package/src/Squisher.js +230 -0
  13. package/src/ViewableGame.js +60 -0
  14. package/src/node-types.js +15 -0
  15. package/src/squish.js +90 -432
  16. package/src/squishHelpers/asset.js +63 -0
  17. package/src/squishHelpers/border.js +16 -0
  18. package/src/squishHelpers/color.js +16 -0
  19. package/src/squishHelpers/coordinates2d.js +98 -0
  20. package/src/squishHelpers/effect.js +55 -0
  21. package/src/squishHelpers/fill.js +16 -0
  22. package/src/squishHelpers/handleClick.js +16 -0
  23. package/src/squishHelpers/id.js +16 -0
  24. package/src/squishHelpers/input.js +22 -0
  25. package/src/squishHelpers/playerIds.js +12 -0
  26. package/src/squishHelpers/pos.js +23 -0
  27. package/src/squishHelpers/size.js +23 -0
  28. package/src/squishHelpers/subType.js +16 -0
  29. package/src/squishHelpers/text.js +154 -0
  30. package/src/subtype-mappings.js +10 -0
  31. package/src/subtypes.js +7 -0
  32. package/src/terrain/defs.ts +16 -0
  33. package/src/terrain/index.ts +156 -0
  34. package/src/terrain/terrainFunctions.ts +142 -0
  35. package/src/util/geometry.js +45 -0
  36. package/src/util/index.ts +6 -0
  37. package/src/util/listenable.ts +20 -0
  38. package/src/util/shapes.ts +18 -0
  39. package/src/util/views.js +108 -0
  40. package/test/Layers.test.js +63 -0
  41. package/test/Scale.test.js +235 -0
  42. package/test/Squisher.test.js +49 -0
  43. package/test/StateUpdates.js +10 -0
  44. package/test/Terrain.test.js +0 -1
  45. package/test/main.test.js +35 -37
  46. package/test/utils.js +91 -0
  47. package/testapp.html +26 -0
  48. package/testapp.js +988 -0
  49. package/tsconfig.json +22 -0
  50. package/license.txt +0 -674
@@ -0,0 +1,98 @@
1
+ const { getFractional } = require('../util');
2
+ const subtypes = require('../subtypes');
3
+
4
+ const COORDINATES_2D_SUBTYPE = 52;
5
+
6
+ const squishHelper = (scale, coord) => {
7
+ const scaledCenter = scale * coord;
8
+ const removedSpaceCenter = Math.round(100 * (1 - scale));
9
+ return scaledCenter + (removedSpaceCenter / 2);
10
+ }
11
+
12
+ const squishCoordinates2d = {
13
+ type: COORDINATES_2D_SUBTYPE,
14
+ squish: (p, scale, node) => {
15
+ const originalCoords = p.flat();
16
+ const squished = new Array(originalCoords.length * 2);
17
+
18
+ if (node.subType == subtypes.SHAPE_2D_CIRCLE) {
19
+ if (scale) {
20
+ const shiftedCenterX = squishHelper(scale.x, originalCoords[0])
21
+ squished[0] = shiftedCenterX;
22
+ squished[1] = getFractional(shiftedCenterX);
23
+
24
+ const shiftedCenterY = squishHelper(scale.y, originalCoords[1])
25
+ squished[2] = shiftedCenterY;
26
+ squished[3] = getFractional(shiftedCenterY);
27
+
28
+ let diagonal;
29
+ if (scale.x === scale.y) {
30
+ diagonal = scale.x * originalCoords[2];
31
+ } else {
32
+ // probably broken
33
+ diagonal = Math.sqrt( Math.pow(100 * scale.x, 2) + Math.pow(100 * scale.y, 2)) * (originalCoords[2] / 100);
34
+ }
35
+
36
+ squished[4] = Math.floor(diagonal);
37
+ squished[5] = getFractional(diagonal);
38
+ } else {
39
+ const centerX = originalCoords[0];
40
+ squished[0] = Math.floor(centerX);
41
+ squished[1] = getFractional(centerX);
42
+
43
+ const centerY = originalCoords[1];
44
+ squished[2] = Math.floor(centerY);
45
+ squished[3] = getFractional(centerY);
46
+
47
+ const radius = originalCoords[2];
48
+ squished[4] = Math.round(radius);
49
+ squished[5] = getFractional(radius);
50
+ }
51
+ } else {
52
+ for (const i in originalCoords) {
53
+ if (scale) {
54
+ const isX = i % 2 == 0;
55
+ const scaleValue = isX ? scale.x : scale.y;
56
+ const scaled = scaleValue * originalCoords[i];
57
+
58
+ const removedSpace = Math.round(100 * (1 - scaleValue));
59
+
60
+ const shifted = scaled + (removedSpace / 2);
61
+
62
+ squished[2 * i] = shifted;
63
+ squished[(2 * i) + 1] = getFractional(shifted);
64
+
65
+ } else {
66
+ squished[2 * i] = Math.floor(originalCoords[i]);
67
+ squished[(2 * i) + 1] = Math.round(100 * (originalCoords[i] - Math.floor(originalCoords[i])));
68
+ }
69
+ }
70
+ }
71
+
72
+ return squished;
73
+ },
74
+ dependsOn: ['subType'],
75
+ unsquish: (squished, { subType }) => {
76
+ const unsquished = new Array(squished.length / 2);
77
+ for (let i = 0; i < squished.length; i += 2) {
78
+ const value = squished[i] + (squished[i + 1] / 100);
79
+ unsquished[i / 2] = value;
80
+ }
81
+
82
+ if (subType === subtypes.SHAPE_2D_POLYGON || subType === subtypes.SHAPE_2D_LINE) {
83
+ const coordPairs = new Array(unsquished.length / 2);
84
+ for (let i = 0; i < unsquished.length; i += 2) {
85
+ coordPairs[i / 2] = [unsquished[i], unsquished[i + 1]];
86
+ }
87
+
88
+ return coordPairs;
89
+ }
90
+
91
+ return unsquished;
92
+ }
93
+ }
94
+
95
+ module.exports = {
96
+ COORDINATES_2D_SUBTYPE,
97
+ squishCoordinates2d
98
+ };
@@ -0,0 +1,55 @@
1
+ const EFFECTS_SUBTYPE = 49;
2
+
3
+ const squishEffect = {
4
+ type: EFFECTS_SUBTYPE,
5
+ squish: (a) => {
6
+ if (a['shadow']) {
7
+ const assetKey = 'shadow';
8
+ let squishedLength = assetKey.length + 4; // + 4 for color
9
+ if (a['shadow'].blur) {
10
+ squishedLength += 2;
11
+ }
12
+ const squishedEffects = new Array(squishedLength);
13
+ for (let i = 0; i < assetKey.length; i++) {
14
+ squishedEffects[i] = assetKey.codePointAt(i);
15
+ }
16
+ squishedEffects[assetKey.length] = a.shadow.color[0];
17
+ squishedEffects[assetKey.length + 1] = a.shadow.color[1];
18
+ squishedEffects[assetKey.length + 2] = a.shadow.color[2];
19
+ squishedEffects[assetKey.length + 3] = a.shadow.color[3];
20
+
21
+ if (a.shadow.blur) {
22
+ squishedEffects[assetKey.length + 4] = Math.floor(a.shadow.blur / 10)
23
+ squishedEffects[assetKey.length + 5] = a.shadow.blur % 10
24
+ }
25
+
26
+ return squishedEffects;
27
+ }
28
+ },
29
+ unsquish: (squished) => {
30
+ // 'shadow' is all (for now)
31
+ const assetKey = String.fromCodePoint.apply(null, squished.slice(0, 6));
32
+ const color = squished.slice(6, 10);
33
+ let blur;
34
+ if (squished.length > 10) {
35
+ blur = squished[10] * 10 + squished[11];
36
+ }
37
+
38
+ const unsquished = {
39
+ [assetKey]: {
40
+ color
41
+ }
42
+ };
43
+
44
+ if (blur) {
45
+ unsquished[assetKey].blur = blur;
46
+ }
47
+
48
+ return unsquished;
49
+ }
50
+ }
51
+
52
+ module.exports = {
53
+ EFFECTS_SUBTYPE,
54
+ squishEffect
55
+ };
@@ -0,0 +1,16 @@
1
+ const FILL_SUBTYPE = 53;
2
+
3
+ const squishFill = {
4
+ type: FILL_SUBTYPE,
5
+ squish: (c) => {
6
+ return [c[0], c[1], c[2], c[3]];
7
+ },
8
+ unsquish: (squished) => {
9
+ return [squished[0], squished[1], squished[2], squished[3]];
10
+ }
11
+ }
12
+
13
+ module.exports = {
14
+ FILL_SUBTYPE,
15
+ squishFill
16
+ };
@@ -0,0 +1,16 @@
1
+ const ONCLICK_SUBTYPE = 50;
2
+
3
+ const squishHandleClick = {
4
+ type: ONCLICK_SUBTYPE,
5
+ squish: (a) => {
6
+ return a ? [1] : [0];
7
+ },
8
+ unsquish: (a) => {
9
+ return a[0] === 1;
10
+ }
11
+ }
12
+
13
+ module.exports = {
14
+ ONCLICK_SUBTYPE,
15
+ squishHandleClick
16
+ };
@@ -0,0 +1,16 @@
1
+ const ID_SUBTYPE = 43;
2
+
3
+ const squishId = {
4
+ type: ID_SUBTYPE,
5
+ squish: (i) => {
6
+ return [i];
7
+ },
8
+ unsquish: (arr) => {
9
+ return arr[0];
10
+ }
11
+ }
12
+
13
+ module.exports = {
14
+ ID_SUBTYPE,
15
+ squishId
16
+ };
@@ -0,0 +1,22 @@
1
+ const INPUT_SUBTYPE = 51;
2
+
3
+ const squishInput = {
4
+ type: INPUT_SUBTYPE,
5
+ squish: (a) => {
6
+ const squished = new Array(a.type.length);
7
+ for (let i = 0; i < a.type.length; i++) {
8
+ squished[i] = a.type.codePointAt(i);
9
+ }
10
+ return squished;
11
+ },
12
+ unsquish: (squished) => {
13
+ return {
14
+ type: String.fromCodePoint.apply(null, squished)
15
+ }
16
+ }
17
+ }
18
+
19
+ module.exports = {
20
+ INPUT_SUBTYPE,
21
+ squishInput
22
+ };
@@ -0,0 +1,12 @@
1
+ const PLAYER_ID_SUBTYPE = 44;
2
+
3
+ const squishPlayerIds = {
4
+ type: PLAYER_ID_SUBTYPE,
5
+ squish: (i) => i,
6
+ unsquish: (squished) => squished
7
+ }
8
+
9
+ module.exports = {
10
+ PLAYER_ID_SUBTYPE,
11
+ squishPlayerIds
12
+ };
@@ -0,0 +1,23 @@
1
+ const POS_SUBTYPE = 45;
2
+
3
+ const squishHelper = (position) => {
4
+ return Math.round(100 * (position - Math.floor(position)))
5
+ }
6
+
7
+ const squishPos = {
8
+ type: POS_SUBTYPE,
9
+ squish: (p) => {
10
+ return [Math.floor(p.x), squishHelper(p.x), Math.floor(p.y), squishHelper(p.y)]
11
+ },
12
+ unsquish: (squished) => {
13
+ return {
14
+ x: squished[0] + squished[1] / 100,
15
+ y: squished[2] + squished[3] / 100
16
+ }
17
+ }
18
+ }
19
+
20
+ module.exports = {
21
+ POS_SUBTYPE,
22
+ squishPos
23
+ };
@@ -0,0 +1,23 @@
1
+ const SIZE_SUBTYPE = 46;
2
+
3
+ const squishHelper = (size) => {
4
+ Math.round(100 * (size- Math.floor(size)))
5
+ }
6
+
7
+ const squishSize = {
8
+ type: SIZE_SUBTYPE,
9
+ squish: (s) => {
10
+ return [Math.floor(s.x), squishHelper(s.x), Math.floor(s.y), squishHelper(s.y)]
11
+ },
12
+ unsquish: (squished) => {
13
+ return {
14
+ x: squished[0] + squished[1] / 100,
15
+ y: squished[2] + squished[3] / 100
16
+ }
17
+ }
18
+ }
19
+
20
+ module.exports = {
21
+ SIZE_SUBTYPE,
22
+ squishSize
23
+ };
@@ -0,0 +1,16 @@
1
+ const SUBTYPE_TYPE = 55;
2
+
3
+ const squishSubType = {
4
+ type: SUBTYPE_TYPE,
5
+ squish: (a) => {
6
+ return [a];
7
+ },
8
+ unsquish: (s) => {
9
+ return s[0];
10
+ }
11
+ }
12
+
13
+ module.exports = {
14
+ SUBTYPE_TYPE,
15
+ squishSubType
16
+ };
@@ -0,0 +1,154 @@
1
+ const { hypLength } = require('../util')
2
+ const Colors = require('../Colors');
3
+ const { squishColor } = require('./color')
4
+
5
+ const TEXT_SUBTYPE = 47;
6
+
7
+ const squishText = {
8
+ type: TEXT_SUBTYPE,
9
+ squish: (t, scale) => {
10
+ const textX = scale ? (t.x * scale.x) + Math.round(100 * (1 - scale.x)) / 2 : t.x;
11
+ const textY = scale ? (t.y * scale.y) + Math.round(100 * (1 - scale.y)) / 2 : t.y;
12
+
13
+ const align = t.align || 'left';
14
+ const squishedText = new Array(t.text.length + 10 + align.length);
15
+
16
+ squishedText[0] = Math.floor(textX);
17
+ squishedText[1] = Math.round(100 * (textX - Math.floor(textX)));
18
+
19
+ squishedText[2] = Math.floor(textY);
20
+ squishedText[3] = Math.round(100 * (textY - Math.floor(textY)));
21
+
22
+ const textSize = t.size || 1;
23
+ const scaledTextSize = scale ? textSize * hypLength(scale.x, scale.y) : textSize;
24
+
25
+ squishedText[4] = Math.floor(scaledTextSize);
26
+ squishedText[5] = Math.round(100 * (scaledTextSize - Math.floor(scaledTextSize)));
27
+
28
+ const textColor = t.color || Colors.BLACK;
29
+ const squishedTextColor = squishColor.squish(textColor);
30
+
31
+ for (let i = 0; i < squishedTextColor.length; i++) {
32
+ squishedText[6 + i] = squishedTextColor[i];
33
+ }
34
+
35
+ squishedText[6 + squishedTextColor.length] = 3 * [...align].length;
36
+
37
+ let j = 0;
38
+ for (let i = 0; i < [...align].length; i++) {
39
+ const codePointToInsert = [...align][i].codePointAt(0);
40
+ const codePointString = codePointToInsert.toString();
41
+ let ting;
42
+ if (codePointString.length == 1) {
43
+ ting = [`00`, `00`, `0${codePointString}`];
44
+ } else if (codePointString.length == 2) {
45
+ ting = [`00`, `00`, `${codePointString}`];
46
+ } else if (codePointString.length == 3) {
47
+ ting = [`00`, `0${codePointString.charAt(0)}`, `${codePointString.charAt(1)}${codePointString.charAt(2)}`];
48
+ } else if (codePointString.length == 4) {
49
+ ting = [`00`, `${codePointString.charAt(0)}${codePointString.charAt(1)}`, `${codePointString.charAt(2)}${codePointString.charAt(3)}`];
50
+ } else if (codePointString.length == 5) {
51
+ ting = [`0${codePointString.charAt(0)}`, `${codePointString.charAt(1)}${codePointString.charAt(2)}`, `${codePointString.charAt(3)}${codePointString.charAt(4)}`];
52
+ } else {
53
+ ting = [`${codePointString.charAt(0)}${codePointString.charAt(1)}`, `${codePointString.charAt(2)}${codePointString.charAt(3)}`, `${codePointString.charAt(4)}${codePointString.charAt(5)}`];
54
+ }
55
+ squishedText[6 + squishedTextColor.length + 1 + j] = Number(ting[0]);
56
+ squishedText[6 + squishedTextColor.length + 1 + j + 1] = Number(ting[1]);
57
+ squishedText[6 + squishedTextColor.length + 1 + j + 2] = Number(ting[2]);
58
+ j += 3;
59
+ }
60
+
61
+ let k = 0;
62
+ for (let i = 0; i < [...t.text].length; i++) {
63
+ const codePointToInsert = [...t.text][i].codePointAt(0);
64
+ const codePointString = codePointToInsert.toString();
65
+ let ting;
66
+ if (codePointString.length == 1) {
67
+ ting = [`00`, `00`, `0${codePointString}`];
68
+ } else if (codePointString.length == 2) {
69
+ ting = [`00`, `00`, `${codePointString}`];
70
+ } else if (codePointString.length == 3) {
71
+ ting = [`00`, `0${codePointString.charAt(0)}`, `${codePointString.charAt(1)}${codePointString.charAt(2)}`];
72
+ } else if (codePointString.length == 4) {
73
+ ting = [`00`, `${codePointString.charAt(0)}${codePointString.charAt(1)}`, `${codePointString.charAt(2)}${codePointString.charAt(3)}`];
74
+ } else if (codePointString.length == 5) {
75
+ ting = [`0${codePointString.charAt(0)}`, `${codePointString.charAt(1)}${codePointString.charAt(2)}`, `${codePointString.charAt(3)}${codePointString.charAt(4)}`];
76
+ } else {
77
+ ting = [`${codePointString.charAt(0)}${codePointString.charAt(1)}`, `${codePointString.charAt(2)}${codePointString.charAt(3)}`, `${codePointString.charAt(4)}${codePointString.charAt(5)}`];
78
+ }
79
+ squishedText[6 + squishedTextColor.length + 1 + j + k] = Number(ting[0]);
80
+ squishedText[6 + squishedTextColor.length + 1 + j + k + 1] = Number(ting[1]);
81
+ squishedText[6 + squishedTextColor.length + 1 + j + k + 2] = Number(ting[2]);
82
+
83
+ k += 3;
84
+ }
85
+
86
+ return squishedText;
87
+ },
88
+ unsquish: (squished) => {
89
+ const textPosX = squished[0] + squished[1] / 100;
90
+ const textPosY = squished[2] + squished[3] / 100;
91
+ const textSize = squished[4] + squished[5] / 100;
92
+ const textColor = squished.slice(6, 10);
93
+ const textAlignLength = squished[10];
94
+ const textAlignVal = squished.slice(11, 11 + textAlignLength);
95
+ const textVal = squished.slice(11 + textAlignLength);
96
+
97
+ let alignCodePoints = [];
98
+ for (let i = 0; i < textAlignVal.length; i+=3) {
99
+ let firstChunk = textAlignVal[i].toString();
100
+ if (firstChunk.length == 1) {
101
+ firstChunk = `0${firstChunk}`;
102
+ }
103
+
104
+ let secondChunk = textAlignVal[i + 1].toString();
105
+ if (secondChunk.length == 1) {
106
+ secondChunk = `0${secondChunk}`;
107
+ }
108
+
109
+ let thirdChunk = textAlignVal[i + 2].toString();
110
+ if (thirdChunk.length == 1) {
111
+ thirdChunk = `0${thirdChunk}`;
112
+ }
113
+
114
+ const codePoint = firstChunk + secondChunk + thirdChunk;
115
+ alignCodePoints.push(codePoint);
116
+ }
117
+
118
+ const textCodePoints = [];
119
+ for (let i = 0; i < textVal.length; i+=3) {
120
+ let firstChunk = textVal[i].toString();
121
+ if (firstChunk.length == 1) {
122
+ firstChunk = `0${firstChunk}`;
123
+ }
124
+ let secondChunk = textVal[i + 1].toString();
125
+ if (secondChunk.length == 1) {
126
+ secondChunk = `0${secondChunk}`;
127
+ }
128
+ let thirdChunk = textVal[i + 2].toString();
129
+ if (thirdChunk.length == 1) {
130
+ thirdChunk = `0${thirdChunk}`;
131
+ }
132
+
133
+ const codePoint = firstChunk + secondChunk + thirdChunk;
134
+ textCodePoints.push(codePoint);
135
+ }
136
+
137
+ const align = String.fromCodePoint.apply(null, alignCodePoints);
138
+ const text = String.fromCodePoint.apply(null, textCodePoints);
139
+
140
+ return {
141
+ x: textPosX,
142
+ y: textPosY,
143
+ text: text,
144
+ size: textSize,
145
+ color: textColor,
146
+ align
147
+ };
148
+ }
149
+ }
150
+
151
+ module.exports = {
152
+ TEXT_SUBTYPE,
153
+ squishText
154
+ };
@@ -0,0 +1,10 @@
1
+ const { GameNode } = require('./GameNode');
2
+ const subtypes = require('./subtypes');
3
+
4
+ module.exports = {
5
+ [[subtypes.ASSET]]: GameNode.Asset,
6
+ [[subtypes.TEXT]]: GameNode.Text,
7
+ [[subtypes.SHAPE_2D_POLYGON]]: GameNode.Shape,
8
+ [[subtypes.SHAPE_2D_CIRCLE]]: GameNode.Shape,
9
+ [[subtypes.SHAPE_2D_LINE]]: GameNode.Shape
10
+ };
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ ASSET: 1,
3
+ TEXT: 2,
4
+ SHAPE_2D_POLYGON: 3,
5
+ SHAPE_2D_CIRCLE: 4,
6
+ SHAPE_2D_LINE: 5
7
+ };
@@ -0,0 +1,16 @@
1
+ export interface pointDef {
2
+ x: number;
3
+ y: number;
4
+ };
5
+
6
+ export interface boardInfo {
7
+ filled: boolean;
8
+ north: boolean;
9
+ south: boolean;
10
+ east: boolean;
11
+ west: boolean;
12
+ wentSouth?: boolean;
13
+ wentEast?: boolean;
14
+ wentNorth?: boolean;
15
+ wentWest?: boolean;
16
+ };
@@ -0,0 +1,156 @@
1
+ import { getRandomTerrain, getRandomStartPoint } from './terrainFunctions';
2
+ import { pointDef, boardInfo } from './defs';
3
+
4
+ interface dimensionDef {
5
+ width: number;
6
+ height: number;
7
+ };
8
+
9
+ export const terrainGenerator = (boardBounds: pointDef = { x: 100, y: 100 }, terrainBounds: pointDef = { x: 5, y: 5 }, numberOfTerrainElements = 5) => {
10
+ const { width, height } = getBoardDimensions(boardBounds, terrainBounds);
11
+ let board = initializeBoard(width, height);
12
+ const keyPoint = {
13
+ x: Math.floor(Math.random() * width),
14
+ y: Math.floor(Math.random() * height)
15
+ };
16
+ board = generateTerrain(board, keyPoint, numberOfTerrainElements, width, height);
17
+ return board;
18
+ };
19
+
20
+ const getBoardDimensions = (boardBounds: pointDef, terrainBounds: pointDef): dimensionDef => {
21
+ const boardX = boardBounds.x;
22
+ const boardY = boardBounds.y;
23
+ const terrainX = terrainBounds.x;
24
+ const terrainY = terrainBounds.y;
25
+ return {
26
+ width: Math.floor(boardX/terrainX),
27
+ height: Math.floor(boardY/terrainY)
28
+ };
29
+ };
30
+
31
+ const initializeBoard = (width: number, height: number): boardInfo[][] => {
32
+ const board: boardInfo[][] = [];
33
+
34
+ const defaultInfo: boardInfo = {
35
+ filled: false,
36
+ north: true,
37
+ south: true,
38
+ east: true,
39
+ west: true
40
+ };
41
+
42
+ for(let i = 0; i < width; i++) {
43
+ board[i] = [];
44
+ for (let j = 0; j < height; j++) {
45
+ const info = { ...defaultInfo };
46
+ if (i === 0) {
47
+ info.west = false;
48
+ } else if (i === width - 1) {
49
+ info.east = false;
50
+ }
51
+ if (j === 0) {
52
+ info.north = false;
53
+ } else if (j === height - 1) {
54
+ info.south = false;
55
+ }
56
+ board[i].push(info);
57
+ }
58
+ }
59
+ return board;
60
+ };
61
+
62
+ const deepClone = (toCopy: boardInfo[][]) => {
63
+ const toReturn: boardInfo[][] = [];
64
+ toCopy.forEach((row, rowNum) => {
65
+ toReturn[rowNum] = [];
66
+ row.forEach(elem => {
67
+ toReturn[rowNum].push({ ...elem });
68
+ });
69
+ });
70
+ return toReturn;
71
+ };
72
+
73
+ const cleanUpBoard = (board: boardInfo[][]) => {
74
+ for (let i = 0; i < board.length; i++) {
75
+ for (let j = 0; j < board[i].length; j++) {
76
+ board[i][j] = {
77
+ filled: board[i][j].filled,
78
+ north: board[i][j].north,
79
+ south: board[i][j].south,
80
+ east: board[i][j].east,
81
+ west: board[i][j].west
82
+ };
83
+ }
84
+ }
85
+ };
86
+
87
+ const verifyKeyPoint = (board: boardInfo[][], keyPoint: pointDef, boardWidth: number, boardHeight: number) => {
88
+ const pointsVerified = [];
89
+ for (let x = 0; x < boardWidth; x++) {
90
+ for (let y = 0; y < boardHeight; y++) {
91
+ const pointsOnCurrentPath = [];
92
+ const currentPoint = { x, y };
93
+ while(true) {
94
+ const boardPoint = board[currentPoint.x][currentPoint.y];
95
+ if (
96
+ (pointsVerified.findIndex(point => point.x === currentPoint.x && point.y === currentPoint.y) > -1) ||
97
+ (currentPoint.x === keyPoint.x && currentPoint.y === keyPoint.y) ||
98
+ (boardPoint.filled)
99
+ ) {
100
+ /*
101
+ if we already know we can reach the keyPoint from our test point or
102
+ if we are at the key point or
103
+ if we are in a filled point, just skip the iteration, no need to test this point further
104
+ */
105
+ pointsVerified.push(...pointsOnCurrentPath);
106
+ break;
107
+ } else if(
108
+ (!boardPoint.north || boardPoint.wentNorth) &&
109
+ (!boardPoint.south || boardPoint.wentSouth) &&
110
+ (!boardPoint.east || boardPoint.wentEast) &&
111
+ (!boardPoint.west || boardPoint.wentWest)
112
+ ) {
113
+ /*
114
+ if we have exhausted all possible movements for a point, and not reached the keypoint
115
+ then this possibleBoard is not workable
116
+ */
117
+ return false;
118
+ }
119
+ pointsOnCurrentPath.push({ ...currentPoint });
120
+ if (boardPoint.south && !boardPoint.wentSouth) {
121
+ boardPoint.wentSouth = true;
122
+ currentPoint.y = currentPoint.y + 1;
123
+ } else if (boardPoint.east && !boardPoint.wentEast) {
124
+ boardPoint.wentEast = true;
125
+ currentPoint.x = currentPoint.x + 1;
126
+ } else if (boardPoint.north && !boardPoint.wentNorth) {
127
+ boardPoint.wentNorth = true;
128
+ currentPoint.y = currentPoint.y - 1;
129
+ } else if (boardPoint.west && !boardPoint.wentWest) {
130
+ boardPoint.wentWest = true;
131
+ currentPoint.x = currentPoint.x - 1;
132
+ } else {
133
+ return false;
134
+ }
135
+ }
136
+ cleanUpBoard(board);
137
+ }
138
+ }
139
+ return true;
140
+ };
141
+
142
+ const generateTerrain = (board: boardInfo[][], keyPoint: pointDef, toPlace: number, boardWidth: number, boardHeight: number, numberOfIterations = 0): boardInfo[][] => {
143
+ if (toPlace === 0 || numberOfIterations === 3) {
144
+ return board;
145
+ }
146
+ const possibleBoard = deepClone(board);
147
+ const terrainFunction = getRandomTerrain();
148
+ const terrainStartPoint = getRandomStartPoint(boardWidth, boardHeight, keyPoint);
149
+ terrainFunction(possibleBoard, terrainStartPoint, boardWidth, boardHeight, keyPoint);
150
+ const temp = verifyKeyPoint(possibleBoard, keyPoint, boardWidth, boardHeight);
151
+ if (temp) {
152
+ return generateTerrain(possibleBoard, keyPoint, toPlace - 1, boardWidth, boardHeight, 0);
153
+ } else {
154
+ return generateTerrain(board, keyPoint, toPlace, boardWidth, boardHeight, numberOfIterations + 1);
155
+ }
156
+ };