squishjs 1.0.5 → 1.0.7

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.
package/README.md CHANGED
@@ -1,119 +1,59 @@
1
- Squish is a way to utility package for [Homegames-core](https://github.com/homegamesio/homegames-core) and [Homegames-web](https://github.com/homegamesio/homegames-web). It contains several utilities and the class definitions for the entities most important to running Homegames.
1
+ ## Squish
2
+ This repo contains the game library used by homegames-core and homegames-web to efficiently serialize & deserialize game data.
2
3
 
3
- Below the term `First Class` is used to describe several functions and variables. This means those values should be treated as immutable, and in the case of functions are called automatically.
4
+ ## squish/unsquish
5
+ This is just serialization/deserialization. Conceptually similar to [protobufs](https://protobuf.dev/), squish contains schema definitions shared by the server and client that lets us transmit game data as efficiently as possible.
4
6
 
5
- ## Exported Features
6
- * squish
7
- * unsquish
8
- * gameNode
9
- * GameNode
10
- * Colors
11
- * Game
12
- * Shapes
13
- * ShapeUtils
14
- * terrainGenerator
15
-
16
- ## squish
17
- squish is a first class function that is used to compress the information of the game down to a set of integers. This compresses the data required to rendering down.
18
-
19
- ## unsquish
20
- unsquish is a first class function that is used to uncompress squished entities. This uncompresses the data that was squished down for more easy manipulation and usage.
21
-
22
- ## gameNode
23
- gameNode is the most foundational component to Homegames. The contents of a game is made up of a set of gameNodes that define entities, and display logic. Wrapped in a listener it contains the following:
24
- * id :- a first calss numeric identification number. It is unique in a Game.
25
- * color :- Defines the display color of the gameNode. See **Colors**
26
- * handleClick :- a function that defines what happens if a user clicks on the gameNode. The function passed in will automatically be called, and will pass the player who clicked as the argument.
27
- * coordinates2d :- an array of coordinates in a two dimensional plane that defines the vertices of the gameNode. It is recommened that **ShapeUtils** is used
28
- * border :- a number that defines the thickness of the border on the gameNode
29
- * fill :- Defines the color that fills the gameNode. See **Colors**
30
- * text :- Defines the text (if any) that will be displayed by the gameNode. It has the form
31
- * text :- The text to be displayed;
32
- * x :- The x-position of the top-left corner of the text. This is relative to the entire screen
33
- * y :- The y-position of the top-left corner of the text. This is relative to the entire screen
34
- * size :- The font size of the text
35
- * align :- 'left' | 'center' | 'right'
36
- * color :- The Color of the text, see **Colors**
37
- * asset :- Defines an asset (image, video, etc) to be displayed by the gameNode. Takes the form:
38
- * assetInfo :- The information for the display of the asset.
39
- * onClick :- a function that defines what happens if a user clicks on the asset. The function passed in will automatically be called, and will pass the player who clicked as the argument.
40
- * coordinates2d :- an array of coordinates in a two dimensional plane that defines the vertices of the gameNode. It is recommened that **ShapeUtils** is used
41
- * playerIds :- An array of player identification numbers. Defining who can see this asset.
42
- * effects :- Defines any additional effects applied to the gameNode.
43
- * input :- Defines whether the gameNode should accept an input or not. Takes the form:
44
- * type :- 'text' | 'file'
45
- * oninput :- A function that will be called with the inputting player's identification number and the data of the input. (player: number, data: any) => void;
46
- * playerIds :- An array of player identification numbers, that defines which players can see and interact with the gameNode.
47
- * listeners :- The set of listeners attached to the gameNode.
48
- * _animation :- An interval or timeout that can be used to modify the gameNode, such as lightening the color over time to give it a fade in, or darkening the color over time to give it a fade out.
49
- * onStateChange :- A first class function that will notify listeners of a state change.
50
- * addListener :- A first class function that will attach an additional listener to the gameNode.
51
-
52
- ## GameNode
53
- A helpful wrapper and the recommended way of generatin a gameNode. It gives you the following:
54
- * Asset :- Generates a gameNode with an asset attached to it
55
- * Example Call: `Asset(onClick: (player) => console.log(player), coordinates2d: [[0, 1], [2, 1], [2, 3], [0, 3], [0, 1]], assetInfo: { myAsset: { pos: { x: 0, y: 1 }, size: { x: 2, y: 3 }}}, playerIds: [2])`
56
- * This will generate an asset based on `myAsset` and on clicking on the asset the asset will log out the clicking player.
57
- * Shape :- Generates a generic two dimenstional gameNode
58
- * Example Call: `Shape({ color: COLORS.CANDY_RED, onClick: (player) => console.log(player), shapeType: Shapes.POLYGON, coordinates2d: [[0, 1], [2, 1], [2, 3], [0, 3], [0, 1]], border: 2, fill: COLORS.CANDY_RED, playerIds: [2], null, null })`
59
- * This will generate a rectangle of color COLORS.CANDY_RED, visible to player of identification number 2, and onClick will log out the player clicking.
60
- * Text :- Generates a gameNode with text attached to it.
61
- * Example Call: `Text({ textInfo: { text: "My Text", color: COLORS.CANDY_RED, align: 'center', x: 2, y: 3, size: 1 }, playerIds: [2] })`
62
- * This will generate a gameNode with texting starting at the (2, 3) position, visible to player of identification number 2. The Text will be centered and CANDY_RED, and will be "My Text".
63
-
64
- ## Colors
65
- Colors is a utility for using shared homegames RGBA values. Colors is exported as an object with two keys: COLORS & randomColor.
66
-
67
- **COLORS** is an object that takes the form of: `string -> RGBA Array`. For example we can GET the RBGA value for Homegames CANDY_RED (RGBA of [246, 84, 106, 255]) by doing the following `COLORS.CANDY_RED`. Thus COLORS serves as a map between strings and in-built colors.
68
-
69
- **randomColor** is a function that takes the form: `string[] => RGBA array`. It is a function to get a random RGBA from the set of RGBA that Homegames provides. It can take in array of strings that limits the options, allowing for the exclusion of certain RGBA values from returning. For example we can call this function like `randomColor(["CANDY_RED"])` which would return an RGBA array from the set of COLORS while preventing CANDY_RED from being a possible return value.
70
-
71
-
72
- ## Game
73
- Game is the base class definition for a game in Homegames. Most games will extend the definition to Game, and leverage most or all of its internal logic. Game holds the following Member Variables:
74
- * players :- Object of the form `player id -> player object`
75
- * timeouts :- Array of NodeJS.Timeout that holds the timeout triggered logic for the Game
76
- * intervals :- Array of NodeJS.Timeout that holds the interval triggered logic for the Game
77
- * listeners :- the list of all listeners on this Game.
78
- * root :- The base GameNode of this game, for which all other GameNodes are children
79
-
80
- Game also holds the following Instance Functions
81
- * _hgAddPlayer :- This is a first class function, and is used to add a player to the Game's `players` value.
82
- * _hgRemovePlayer :- This is a first class function, and is used to remmove a player from the Game's `players` value.
83
- * addStateListener :- This is a first class function, and is used to add a listener to the Game's `listeners` value.
84
- * removeStateListener :- This is a first class function, and is used to remove a listener from the Game's `listeners` value.
85
- * getRoot :- This is a function and returns the Game's `root` value. An example call would be: `getRoot()`. It is expected that this function is overwritten in the extending class's definition.
86
- * setTimeout :- This is a function, and is used to add a timeout to the Game's `timeouts` value. It takes in two arguments a function to be called at the end of the timer, and how long the timer should be. An example call would be: `setTimeout( () => console.log("Welcome to Homegames"), 200)`
87
- * setInterval :- This is a function, and is used to add an interval to the Game's `intervals` value. It takes in two arguments a function to be called at the end of the timer, and how long the timer should be. An example call would be: `intervals(() => console.log("Welcome to Homegames"), 200)`
88
- * close :- This is a first class function, and is used to clear all `timeouts` and `intervals` from the Game, as the Game instance is spun down.
89
-
90
- ## Shapes
91
- Shapes is functionally an enum of the types of shapes Homegames supports.
92
-
93
- ## ShapeUtils
94
- ShapeUtils is a utility for building shapes for use in GameNodes. ShapeUtils is exported as an object with two keys rectangle & triangle.
95
-
96
- **rectangle** is a function that takes the form `(startX, startY, width, height) => the rectangle's coordinate set`. Thus rectangle creates an array from the starting x position, the starting y position, the width, and the height that defines the vertices of the rectangle. An example call would be: `rectangle(0, 1, 2, 3)` which would create a rectangle with the top-left corner at (0,1), and bottom-right corner at (2, 4).
97
-
98
- **triangle** is a function that takes the form `(x1, y1, x2, y2, x3, y3) => the triangle's coordinate set`. Thus triangle creates an array from provided vertex pairings. An example call would be `triangle(0, 1, 2, 3, 4, 5)` which would create a triangle with vertices at (0, 1), (2, 3), (4, 5).
99
-
100
- ## terrainGenerator
101
- terrainGenerator is a utility function that generates a "board" from the provided information. This "board" functions as a map, defining regions of the board that are randomly marked to be "filled". This can be used to procedurally generate fields of play with parts of the map unaccessible to players. The "board" is guaranteed that all un-filled points are contiguous. An example call would be: `terrainGenerator({ x: 50, y: 100 }, { x: 2, y: 3 }, 5)`. This would generate board of area 50 * 100. The "filled" regions would be of area 2 * 3, and a maximum of 5 such "filled" regions will be placed. The outcome of this would be two-dimensionl area, where each element has the form
102
7
  ```
103
- {
104
- filled: boolean
105
- north: boolean
106
- south: boolean
107
- east: boolean
108
- west: boolean
8
+ const { squish, unsquish, Colors, GameNode, Shapes, ShapeUtils } = require('squish-1006');
9
+ const node = new GameNode.Shape({
10
+ shapeType: Shapes.POLYGON,
11
+ fill: Colors.COLORS.RED,
12
+ coordinates2d: ShapeUtils.rectangle(20, 20, 10, 10)
13
+ });
14
+
15
+ const squished = squish(node);
16
+ [
17
+ 3, 0, 0, 53,
18
+ 2, 43, 0, 11,
19
+ [Number: 0], [Number: 0], [Number: 32], [Number: 61],
20
+ [Number: 3], [Number: 90], [Number: 63], [Number: 44],
21
+ 44, 0, 3, 53,
22
+ 0, 7, 255, 0,
23
+ 0, 255, 55, 0,
24
+ 4, 3, 52, 0,
25
+ 23, 20, 0, 20,
26
+ 0, 30, 0, 20,
27
+ 0, 30, 0, 30,
28
+ 0, 20, 0, 30,
29
+ 0, 20, 0, 20,
30
+ 0
31
+ ]
32
+
33
+ const unsquished = unsquish(squished);
34
+ Shape {
35
+ node: InternalGameNode {
36
+ id: [Number: 326103906344],
37
+ children: [],
38
+ color: undefined,
39
+ handleClick: undefined,
40
+ coordinates2d: [ [Array], [Array], [Array], [Array], [Array] ],
41
+ border: undefined,
42
+ fill: [ 255, 0, 0, 255 ],
43
+ text: undefined,
44
+ asset: undefined,
45
+ effects: null,
46
+ input: null,
47
+ listeners: Set(0) {},
48
+ playerIds: [],
49
+ subType: 3
50
+ },
51
+ id: [Number: 326103906344]
109
52
  }
110
53
  ```
111
- **filled** of true means this place can be marked as inaccessible to players
112
-
113
- **north** of true means from this place a player can move "north".
114
54
 
115
- **south** of true means from this place a player can move "south".
116
-
117
- **east** of true means from this place a player can move "east".
55
+ ## Game
56
+ The `Game` class is a base class that allows external components (eg. a `Squisher`) to listen to state updates. All games in Homegames extend the `Game` class.
118
57
 
119
- **west** of true means from this place a player can move "west".
58
+ ## Versioning
59
+ Homegames core & web use multiple versions of squish. This allows games using older versions of squish to remain compatible with newer versions of Homegames.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squishjs",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "squish & unsquish stuff",
5
5
  "scripts": {
6
6
  "test": "node testRunner.js"
package/src/BaseNode.js CHANGED
@@ -1,17 +1,15 @@
1
- const listenable = require("./util/listenable");
2
1
  const InternalGameNode = require('./InternalGameNode');
3
2
 
4
- const gameNode = (color, onClick, coordinates2d, border, fill, text, asset, playerIds, effects, input, subtype, id) => {
5
- const node = new InternalGameNode(color, onClick, coordinates2d, border, fill, text, asset, playerIds, effects, input, subtype, id);
6
- return listenable(node, node.onStateChange.bind(node));
3
+ const gameNode = (color, onClick, coordinates2d, border, fill, text, asset, playerIds, effects, input, subtype, id, onHover, offHover) => {
4
+ return new InternalGameNode(color, onClick, coordinates2d, border, fill, text, asset, playerIds, effects, input, subtype, id, onHover, offHover);
7
5
  };
8
6
 
9
7
  class BaseNode {
10
- constructor({color, onClick, coordinates2d, border, fill, textInfo, assetInfo, playerIds, effects, input, subtype, node, id}) {
8
+ constructor({color, onClick, coordinates2d, border, fill, textInfo, assetInfo, playerIds, effects, input, subtype, node, id, onHover, offHover }) {
11
9
  if (node) {
12
10
  this.node = node;
13
11
  } else {
14
- this.node = gameNode(color, onClick, coordinates2d, border, fill, textInfo, assetInfo, playerIds, effects, input, subtype, id);
12
+ this.node = gameNode(color, onClick, coordinates2d, border, fill, textInfo, assetInfo, playerIds, effects, input, subtype, id, onHover, offHover);
15
13
  }
16
14
 
17
15
  this.id = this.node.id;
@@ -45,22 +43,30 @@ class BaseNode {
45
43
  }
46
44
  }
47
45
 
48
- addChild(child) {
46
+ addChild(child, notifyListeners = true) {
49
47
  this.node.addChild(child);
48
+ if (notifyListeners) {
49
+ this.node.onStateChange();
50
+ }
50
51
  }
51
52
 
52
53
  addChildren(...nodes) {
53
54
  for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex++) {
54
- this.addChild(nodes[nodeIndex]);
55
+ this.addChild(nodes[nodeIndex], false);
55
56
  }
57
+
58
+ this.node.onStateChange();
56
59
  }
57
60
 
58
61
  getChildren() {
59
62
  return this.node.children;
60
63
  }
61
64
 
62
- removeChild(nodeId) {
65
+ removeChild(nodeId, notifyListeners = true) {
63
66
  this.node.removeChild(nodeId);
67
+ if (notifyListeners) {
68
+ this.node.onStateChange();
69
+ }
64
70
  }
65
71
 
66
72
  addListener(listener) {
@@ -89,14 +95,23 @@ class BaseNode {
89
95
 
90
96
  clearChildren(excludedNodeIds) {
91
97
  this.node.clearChildren(excludedNodeIds);
98
+ this.node.onStateChange();
92
99
  }
93
100
 
94
- clone() {
95
- throw new Error("Clone not implemented");
101
+ update( params = {} ) {
102
+ if (params) {
103
+ if (params.fill) {
104
+ this.node.fill = params.fill;
105
+ }
106
+ if (params.coordinates2d) {
107
+ this.node.coordinates2d = coordinates2d;
108
+ }
109
+ }
110
+ this.node.onStateChange();
96
111
  }
97
112
 
98
- free() {
99
- this.node.free();
113
+ clone() {
114
+ throw new Error("Clone not implemented");
100
115
  }
101
116
  }
102
117
 
package/src/GameNode.js CHANGED
@@ -15,7 +15,7 @@ const subtypeToShapeType = {
15
15
  };
16
16
 
17
17
  class Shape extends BaseNode {
18
- constructor({ color, onClick, shapeType, coordinates2d, border, fill, playerIds, effects, input, node, id }) {
18
+ constructor({ color, onClick, shapeType, coordinates2d, border, fill, playerIds, effects, input, node, id, onHover, offHover }) {
19
19
  if ((!coordinates2d || !shapeType) && !(node)) {
20
20
  throw new Error("Shape requires coordinates2d and shapeType");
21
21
  }
@@ -31,7 +31,9 @@ class Shape extends BaseNode {
31
31
  input,
32
32
  node,
33
33
  subtype: shapeTypeToSubtype[shapeType],
34
- id
34
+ id,
35
+ onHover,
36
+ offHover
35
37
  });
36
38
  }
37
39
 
@@ -81,7 +83,7 @@ class Text extends BaseNode {
81
83
  }
82
84
 
83
85
  class Asset extends BaseNode {
84
- constructor({ assetInfo, onClick, coordinates2d, playerIds, effects, node, id }) {
86
+ constructor({ assetInfo, onClick, coordinates2d, playerIds, effects, node, id, onHover, offHover }) {
85
87
  if (!assetInfo && !node) {
86
88
  throw new Error("Asset node requires assetInfo");
87
89
  }
@@ -94,7 +96,9 @@ class Asset extends BaseNode {
94
96
  effects,
95
97
  node,
96
98
  subtype: SUBTYPES.ASSET,
97
- id
99
+ id,
100
+ onHover,
101
+ offHover
98
102
  });
99
103
  }
100
104
 
@@ -1,5 +1,5 @@
1
1
  class InternalGameNode {
2
- constructor(color, onClick, coordinates2d, border, fill, text, asset, playerIds = [], effects = null, input = null, subType = null, id = null) {
2
+ constructor(color, onClick, coordinates2d, border, fill, text, asset, playerIds = [], effects = null, input = null, subType = null, id = null, onHover, offHover) {
3
3
  this.id = id ? Number(id) : Math.floor(Math.random() * 999999999999);
4
4
  this.children = new Array();
5
5
  this.color = color;
@@ -17,11 +17,12 @@ class InternalGameNode {
17
17
  }
18
18
  this.playerIds = playerIds || [];
19
19
  this.subType = subType;
20
+ this.onHover = onHover;
21
+ this.offHover = offHover;
20
22
  }
21
23
 
22
24
  addChild(node) {
23
25
  this.children.push(node);
24
- this.onStateChange();
25
26
  }
26
27
 
27
28
  addChildren(...nodes) {
@@ -37,8 +38,6 @@ class InternalGameNode {
37
38
  clearInterval(this._animation);
38
39
  }
39
40
  this.children.splice(removeIndex, 1);
40
- // hack to invoke update listener
41
- this.id = this.id;
42
41
  }
43
42
  }
44
43
 
@@ -39,10 +39,13 @@ test("squisher listener", () => {
39
39
  assert(timesInvoked === 0);
40
40
 
41
41
  root.node.color = COLORS.BLACK;
42
+ root.node.onStateChange();
42
43
 
43
44
  assert(timesInvoked === 1);
44
45
 
45
46
  root.node.color = COLORS.GREEN;
47
+ root.node.onStateChange();
48
+
46
49
  assert(timesInvoked === 2);
47
50
  });
48
51
 
package/test/main.test.js CHANGED
@@ -3,7 +3,7 @@ const { GameNode } = require('../src/GameNode');
3
3
  const { COLORS } = require("../src/Colors");
4
4
  const Shapes = require('../src/Shapes');
5
5
  const ShapeUtils = require('../src/util/shapes');
6
- const { verifyArrayEquality } = require('./utils');
6
+ const { verifyArrayEquality, rectNode, FakeGame } = require('./utils');
7
7
  const Squisher = require('../src/Squisher');
8
8
  const Game = require('../src/Game');
9
9
 
@@ -361,7 +361,7 @@ test("big big text", () => {
361
361
  test("allocate a bunch of nodes", () => {
362
362
  const initialMemUsage = process.memoryUsage();
363
363
 
364
- const nodeCount = Math.pow(10, 2);
364
+ const nodeCount = Math.pow(10, 3);
365
365
 
366
366
  const root = new GameNode.Text({
367
367
  textInfo: {
@@ -404,10 +404,47 @@ test("allocate a bunch of nodes", () => {
404
404
 
405
405
  root.addChild(gameNode);
406
406
  root.removeChild(gameNode.node.id);
407
- gameNode.free();
408
407
  }
409
408
 
410
409
  const postMemUsage = process.memoryUsage();
411
410
  assert(postMemUsage.heapTotal - initialMemUsage.heapTotal <= (.05 * initialMemUsage.heapTotal));
412
411
  });
413
412
 
413
+ test("Simple shape with updates", () => {
414
+ const base = rectNode({ x: 0, y: 0, width: 100, height: 100, fill: COLORS.RED });
415
+
416
+ const game = new FakeGame([
417
+ {
418
+ root: base
419
+ }
420
+ ]);
421
+
422
+ const squisher = new Squisher({ game });
423
+
424
+ let updateCount = 0;
425
+ // add listener so squisher cares about update
426
+ squisher.addListener(() => {
427
+ updateCount++;
428
+ });
429
+
430
+ const initialState = Array.from(squisher.state);
431
+ assert(initialState.length === 1);
432
+
433
+ verifyArrayEquality(unsquish(initialState[0]).node.fill, COLORS.RED);
434
+
435
+ base.update({
436
+ fill: COLORS.BLUE
437
+ });
438
+
439
+ const state2 = Array.from(squisher.state);
440
+ verifyArrayEquality(unsquish(state2[0]).node.fill, COLORS.BLUE);
441
+
442
+ base.update({
443
+ fill: COLORS.GREEN
444
+ });
445
+
446
+ const state3 = Array.from(squisher.state);
447
+ verifyArrayEquality(unsquish(state3[0]).node.fill, COLORS.GREEN);
448
+ });
449
+
450
+
@@ -1,16 +0,0 @@
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
- };
@@ -1,156 +0,0 @@
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
- };
@@ -1,142 +0,0 @@
1
- import { pointDef, boardInfo } from './defs';
2
-
3
- const placeTerrain = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
4
- if (x < 0 || x >= boardWidth) {
5
- return;
6
- } else if (y < 0 || y >= boardHeight) {
7
- return;
8
- } else if ( x === keyPoint.x && y === keyPoint.y) {
9
- return;
10
- }
11
-
12
- if (!board[x][y].filled) {
13
- if (board[x][y].north) {
14
- board[x][y - 1].south = false;
15
- }
16
- if (board[x][y].south) {
17
- board[x][y + 1].north = false;
18
- }
19
- if (board[x][y].east) {
20
- board[x + 1][y].west = false;
21
- }
22
- if (board[x][y].west) {
23
- board[x - 1][y].east = false;
24
- }
25
- board[x][y].filled = true;
26
- board[x][y].north = false;
27
- board[x][y].south = false;
28
- board[x][y].east = false;
29
- board[x][y].west = false;
30
- }
31
- }
32
-
33
- const square = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
34
- const option = Math.floor(Math.random() * 4);
35
- placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
36
- if (option === 0) { // start point is top left
37
- placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
38
- placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
39
- placeTerrain(board, { x: x + 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
40
- } else if (option === 1) { // start point is top right
41
- placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
42
- placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
43
- placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
44
- } else if (option === 2) { // start point is bottom left
45
- placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
46
- placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
47
- placeTerrain(board, { x: x + 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
48
- } else { // start point is bottom right
49
- placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
50
- placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
51
- placeTerrain(board, { x: x - 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
52
- }
53
- };
54
-
55
- const line = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
56
- const option = Math.floor(Math.random() * 4);
57
- placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
58
- if (option === 0) { // start point is top
59
- placeTerrain(board, { x, y: y + 1}, boardWidth, boardHeight, keyPoint);
60
- placeTerrain(board, { x, y: y + 2 }, boardWidth, boardHeight, keyPoint);
61
- placeTerrain(board, { x, y: y + 3 }, boardWidth, boardHeight, keyPoint);
62
- } else if (option === 1) { // start point is left
63
- placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
64
- placeTerrain(board, { x: x + 2, y }, boardWidth, boardHeight, keyPoint);
65
- placeTerrain(board, { x: x + 3, y }, boardWidth, boardHeight, keyPoint);
66
- } else if (option === 2) { // start point is right
67
- placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
68
- placeTerrain(board, { x: x - 2, y }, boardWidth, boardHeight, keyPoint);
69
- placeTerrain(board, { x: x - 3, y }, boardWidth, boardHeight, keyPoint);
70
- } else { // start point is bottom
71
- placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
72
- placeTerrain(board, { x, y: y - 2 }, boardWidth, boardHeight, keyPoint);
73
- placeTerrain(board, { x, y: y - 3 }, boardWidth, boardHeight, keyPoint);
74
- }
75
- };
76
-
77
- const bentLine = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
78
- const option = Math.floor(Math.random() * 4);
79
- placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
80
- if (option === 0) { // start point is top
81
- placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
82
- placeTerrain(board, { x, y: y + 2 }, boardWidth, boardHeight, keyPoint);
83
- placeTerrain(board, { x: x + 1, y: y + 2 }, boardWidth, boardHeight, keyPoint);
84
- } else if (option === 1) { // start point is left
85
- placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
86
- placeTerrain(board, { x: x + 2, y }, boardWidth, boardHeight, keyPoint);
87
- placeTerrain(board, { x: x + 2, y: y - 1 }, boardWidth, boardHeight, keyPoint);
88
- } else if (option === 2) { // start point is right
89
- placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
90
- placeTerrain(board, { x: x - 2, y }, boardWidth, boardHeight, keyPoint);
91
- placeTerrain(board, { x: x - 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
92
- } else { // start point is bottom
93
- placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
94
- placeTerrain(board, { x, y: y - 2 }, boardWidth, boardHeight, keyPoint);
95
- placeTerrain(board, { x: x - 1, y: y - 2 }, boardWidth, boardHeight, keyPoint);
96
- }
97
- };
98
-
99
- const zShape = (board: boardInfo[][], { x, y }: pointDef, boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
100
- const option = Math.floor(Math.random() * 4);
101
- placeTerrain(board, { x, y }, boardWidth, boardHeight, keyPoint);
102
- if (option === 0) { // start point is top
103
- placeTerrain(board, { x, y: y + 1 }, boardWidth, boardHeight, keyPoint);
104
- placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
105
- placeTerrain(board, { x: x - 1, y: y + 2 }, boardWidth, boardHeight, keyPoint);
106
- } else if (option === 1) { // start point is left
107
- placeTerrain(board, { x: x + 1, y }, boardWidth, boardHeight, keyPoint);
108
- placeTerrain(board, { x: x + 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
109
- placeTerrain(board, { x: x + 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
110
- } else if (option === 2) { // start point is right
111
- placeTerrain(board, { x: x - 1, y }, boardWidth, boardHeight, keyPoint);
112
- placeTerrain(board, { x: x - 1, y: y + 1 }, boardWidth, boardHeight, keyPoint);
113
- placeTerrain(board, { x: x - 2, y: y + 1 }, boardWidth, boardHeight, keyPoint);
114
- } else { // start point is bottom
115
- placeTerrain(board, { x, y: y - 1 }, boardWidth, boardHeight, keyPoint);
116
- placeTerrain(board, { x: x - 1, y: y - 1 }, boardWidth, boardHeight, keyPoint);
117
- placeTerrain(board, { x: x - 1, y: y - 2 }, boardWidth, boardHeight, keyPoint);
118
- }
119
- };
120
-
121
- const terrainFunctions = [
122
- square,
123
- line,
124
- bentLine,
125
- zShape
126
- ];
127
-
128
- export const getRandomTerrain = () => {
129
- return terrainFunctions[Math.floor(Math.random() * terrainFunctions.length)];
130
- };
131
-
132
- export const getRandomStartPoint = (boardWidth: number, boardHeight: number, keyPoint: pointDef) => {
133
- const startPoint = {
134
- x: Math.floor(Math.random() * boardWidth),
135
- y: Math.floor(Math.random() * boardHeight)
136
- };
137
- while (startPoint.x === keyPoint.x && startPoint.y === keyPoint.y) {
138
- startPoint.x = Math.floor(Math.random() * boardWidth);
139
- startPoint.y = Math.floor(Math.random() * boardHeight);
140
- }
141
- return startPoint;
142
- };
package/src/util/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export const hypLength = (x: number, y: number) => Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
2
-
3
- // get the first 2 digits after the decimal
4
- export const getFractional = (number: number) => {
5
- return Math.round(100 * (number - Math.floor(number)));
6
- };
@@ -1,27 +0,0 @@
1
- const listenable = function(obj, onChange) {
2
- const handler = {
3
- get(target, property, receiver) {
4
- return Reflect.get(target, property, receiver);
5
- },
6
- defineProperty(target, property, descriptor) {
7
- const change = Reflect.defineProperty(target, property, descriptor);
8
- onChange && onChange();
9
- return change;
10
- },
11
- deleteProperty(target, property) {
12
- const change = Reflect.deleteProperty(target, property);
13
- onChange && onChange();
14
- return change;
15
- }
16
- };
17
-
18
- const revocable = Proxy.revocable(obj, handler);
19
-
20
- obj.free = () => {
21
- revocable.revoke();
22
- }
23
-
24
- return revocable.proxy;
25
- };
26
-
27
- module.exports = listenable;
@@ -1,20 +0,0 @@
1
- import { gameNodeDef, internalGameNodeDef } from '../sharedDefs';
2
- export default (obj: internalGameNodeDef, onChange: () => void) => {
3
- const handler = {
4
- get(target: Object, property: PropertyKey, receiver: any) {
5
- return Reflect.get(target, property, receiver);
6
- },
7
- defineProperty(target: Object, property: PropertyKey, descriptor: PropertyDescriptor) {
8
- const change = Reflect.defineProperty(target, property, descriptor);
9
- onChange && onChange();
10
- return change;
11
- },
12
- deleteProperty(target: Object, property: PropertyKey) {
13
- const change = Reflect.deleteProperty(target, property);
14
- onChange && onChange();
15
- return change;
16
- }
17
- };
18
-
19
- return new Proxy(obj, handler);
20
- };
@@ -1,18 +0,0 @@
1
- export const rectangle = (startX: number, startY: number, width: number, height: number) => {
2
- return [
3
- [startX, startY],
4
- [startX + width, startY],
5
- [startX + width, startY + height],
6
- [startX, startY + height],
7
- [startX, startY],
8
- ];
9
- };
10
-
11
- export const triangle = (x1: number, y1: number, x2: number, y2: number, x3: number, y3: number) => {
12
- return [
13
- [x1, y1],
14
- [x2, y2],
15
- [x3, y3],
16
- [x1, y1]
17
- ];
18
- };
package/tsconfig.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "lib": ["es2019"],
4
- "target": "es2015",
5
- "module": "commonjs",
6
- "declaration": true,
7
- "noEmitOnError": true,
8
- "noImplicitAny": true,
9
- "experimentalDecorators": true,
10
- "sourceMap": false,
11
- "inlineSourceMap": true,
12
- "outDir": "./dist"
13
- },
14
- "exclude": [
15
- "dist",
16
- "test",
17
- "node_modules"
18
- ],
19
- "include": [
20
- "src/**/*"
21
- ]
22
- }