Skip to content

Commit e87776c

Browse files
committed
Bump version to 0.1.4
1 parent f41f3ee commit e87776c

File tree

14 files changed

+450
-45
lines changed

14 files changed

+450
-45
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11

22
# llcacodec Changelog
33

4+
## April 10: Version 0.1.4
5+
6+
Bump to version 0.1.4
7+
48
## April 10: Version 0.1.3
59

610
Add documentation to src/core/set2D.ts

DEV_DOCUMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
# llcacodec 0.1.3 Developer Documentation
2+
# llcacodec 0.1.4 Developer Documentation
33

44
## Unsupported File Formats
55

DOCUMENTATION.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
# llcacodec 0.1.3
2+
# llcacodec 0.1.4
33

44
llcacodec is a library written in Typescript to decode and encode
55
[Life-Like Cellular Automata](https://conwaylife.com/wiki/Life-like_cellular_automaton)
@@ -25,7 +25,7 @@ llcacodec should be present inside of this DOCUMENTATION.md file
2525

2626
## Table of Contents
2727

28-
- [llcacodec 0.1.3](#llcacodec-013)
28+
- [llcacodec 0.1.4](#llcacodec-014)
2929
- [Table of Contents](#table-of-contents)
3030
- [Quickstart](#quickstart)
3131
- [Quickstart - Files](#quickstart---files)
@@ -528,5 +528,5 @@ fetchLifeLikeFileData("my/path")
528528

529529
---
530530

531-
Written for llcacodec version 0.1.3
531+
Written for llcacodec version 0.1.4
532532
Written by [cobyj33](https://www.github.com/cobyj33)

dist/api.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
"use strict";
2+
/**
3+
* @file api.ts
4+
* @description The public API for the llcacodec library
5+
* @author Jacoby Johnson
6+
* @version 0.1.4
7+
* @date April 9th, 2023
8+
* @license MIT
9+
*/
210
Object.defineProperty(exports, "__esModule", { value: true });
311
exports.writeLifeString = exports.getLifeStringFormat = exports.isLifeStringFormat = exports.readLifeString = exports.getLifeRuleFormat = exports.isValidLifeRule = exports.makeLifeRule = exports.readLifeRule = exports.CONWAY_LIFE_RULE_DATA = void 0;
412
const life106_1 = require("./formats/file/life106");
@@ -26,6 +34,18 @@ function readLifeString(data, format = "") {
2634
}
2735
}
2836
exports.readLifeString = readLifeString;
37+
/**
38+
* Assert whether a life string conforms to a given format
39+
*
40+
* Note that just because isLifeStringFormat returns true does NOT mean that readLifeString will not throw an error when called with the given life string.
41+
* isLifeStringFormat does not confirm if the passed data is valid. It only tries to detect if the given life string
42+
* conforms to a format by searching for required headers and sequences in the string.
43+
*
44+
* @param data The life string to test
45+
* @param format The format to test conformity against the given life string with.
46+
* Can either be "plaintext", "life 1.05", "life 1.06", or "plaintext"
47+
* @returns Whether the given life string conforms with the given format
48+
*/
2949
function isLifeStringFormat(data, format) {
3050
switch (format) {
3151
case "life 1.06": return (0, life106_1.isLife106String)(data);
@@ -35,11 +55,22 @@ function isLifeStringFormat(data, format) {
3555
}
3656
}
3757
exports.isLifeStringFormat = isLifeStringFormat;
58+
/** Detect the format of a life string
59+
*
60+
* Note that just because getLifeStringFormat detects a file format does NOT mean that readLifeString will not throw an error
61+
* when called with the given life string. getLifeStringFormat does not confirm if the passed data is valid.
62+
* It only tries to detect the given life string's format by searching for required headers and sequences in the string.
63+
*
64+
* @param data The life string to get the format of
65+
* @returns Either "life 1.06", "life 1.05", "rle", or "plaintext" on the finding
66+
* of a successful format, and an empty string when no format could be found.
67+
*/
3868
function getLifeStringFormat(data) {
3969
// Note how the tests are ordered. They are ordered from the most simple to identify
4070
// to the least simple to identify. Life 1.06 and Life 1.05 can simply be identified by
4171
// if their file begins with the appropriate header data. isRLEString is identified by the existence of
42-
// a
72+
// a header, although it may not be at the beginning of the file. Finally, Plaintext is identified by simply
73+
// checking if the file parses correctly
4374
if ((0, life106_1.isLife106String)(data)) {
4475
return "life 1.06";
4576
}
@@ -55,6 +86,39 @@ function getLifeStringFormat(data) {
5586
return "";
5687
}
5788
exports.getLifeStringFormat = getLifeStringFormat;
89+
/**
90+
* Write a Life String, keeping the passed data in a portable string format
91+
*
92+
* writeLifeString can write in either one of these formats:
93+
*
94+
* [Life 1.06](https://conwaylife.com/wiki/Life_1.06):
95+
* represented by the presence of { format: "life 1.06" } in the given structured data
96+
*
97+
* [Plaintext](https://conwaylife.com/wiki/Plaintext):
98+
* represented by the presence of { format: "plaintext" } in the given structured data
99+
*
100+
* [Run-Length Encoded (RLE)](https://conwaylife.com/wiki/Run_Length_Encoded):
101+
* represented by the presence of { format: "rle" } in the given structured data
102+
*
103+
* Given all of these formats, rle will likely be the most compressed and desired
104+
* format the large majority of the time. Generally, it is much better to represent
105+
* any semi-large or large pattern, but it is less straight-forward for humans to read
106+
* if that is desired.
107+
*
108+
* Plaintext will be a little larger, but should generally not be used for patterns with a bounding box with a width
109+
* greater than 80 cells. Plaintext, however, could be desired for smaller patterns because of
110+
* the human-readability of the pattern's diagram.
111+
*
112+
* Life 1.06 is also viable for smaller patterns. However, since Life 1.06 lists every single
113+
* coordinate without any compression, Life 1.06 could make for larger files as well.
114+
*
115+
* The particulars of each data format is out of the scope of this little docstring,
116+
* and can be found in DOCUMENTATION.md in the llcacodec repository. This documentation
117+
* could also be found online [here](https://www.github.com/cobyj33/llcacodec/blob/master/DOCUMENTATION.md)
118+
*
119+
* @param data The structured data to use when creating the life string
120+
* @returns A life string containing the passed in compiled data.
121+
*/
58122
function writeLifeString(data) {
59123
switch (data.format) {
60124
case "life 1.06": return (0, life106_1.writeLife106String)(data);

dist/core/set2D.js

Lines changed: 128 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,60 @@
11
"use strict";
2+
/**
3+
* @author Jacoby Johnson
4+
* @file src/common/Set2D.ts
5+
* @package jsutil
6+
* @github https://www.github.com/cobyj33/jsutil
7+
* @description A Set implementation to hold a unique set of 2D vector
8+
* values that can easily be queried for existence and iterated over
9+
* @version 0.1.0
10+
* @date 2023-4-09
11+
*/
212
Object.defineProperty(exports, "__esModule", { value: true });
313
exports.Set2D = void 0;
14+
/**
15+
* A Set implementation to quickly and reliably hold a unique set of 2D vector
16+
* values that can easily be queried for existence and iterated over
17+
*/
418
class Set2D {
519
constructor(values = []) {
20+
/**
21+
* @brief This map internally holds all of the data to query into the Set2D.
22+
*
23+
* @summary It consists of a key which represents the first component of a 2-dimensional vector,
24+
* with a set of number values which represent any second component values associated with the given first component
25+
*/
626
this.map = new Map();
727
this._length = 0;
8-
if (Array.isArray(values)) {
9-
values.forEach(value => this.add(value[0], value[1]));
28+
values.forEach(value => this.add(value[0], value[1]));
29+
}
30+
/**
31+
* Clear all data within this Set2D instance.
32+
*
33+
* @param total An optional parameter on whether to totally clear the allocated internal data
34+
* structures used in the Set2D or not. Defaults to false. Note that whether this "total" parameter
35+
* is false or true does not affect this function's purpose: This function will always remove all 2D
36+
* vectors present in this Set2D instance. However, users can choose whether to reallocate storage containers
37+
* within the Set2D instance with the "total" parameter
38+
*/
39+
clear(total = false) {
40+
if (total) {
41+
this.map = new Map();
1042
}
1143
else {
12-
values.forEach(([first, second]) => this.add(first, second));
44+
[...this.map.values()].forEach(set => set.clear());
1345
}
14-
}
15-
fullClear() {
16-
this.map = new Map();
17-
this._length = 0;
18-
}
19-
clear() {
20-
[...this.map.values()].forEach(set => set.clear());
2146
this._length = 0;
2247
}
48+
/**
49+
* @brief The number of unique 2D vectors stored in this Set2D instance
50+
* This number cannot be set by the user. It is instead changed by the
51+
* Set2D class internally among additions and removals.
52+
*/
2353
get length() { return this._length; }
24-
static fromNumberMatrix(values) {
25-
const set = new Set2D();
26-
for (let row = 0; row < values.length; row++) {
27-
for (let col = 0; col < values[row].length; col++) {
28-
if (values[row][col] === 1) {
29-
set.add(row, col);
30-
}
31-
}
32-
}
33-
return set;
34-
}
54+
/**
55+
* @returns An array of 2D tuples in the form {[number, number]} of the unique
56+
* 2D vectors present in this Set2D instance
57+
*/
3558
getTuples() {
3659
const arr = new Array(this.length);
3760
let i = 0;
@@ -41,9 +64,27 @@ class Set2D {
4164
});
4265
return arr;
4366
}
67+
/**
68+
* Perform a callback function for every vector present in t
69+
*
70+
* @note The ordering of the calls to the forEach function should not be assumed and is not
71+
* guaranteed to stay the same between different versions of jsutil.
72+
*
73+
* @param callbackfn The callback function to be performed on each 2D vector in this Set2D instance. The
74+
* callback function will be passed a 2D vector in the form of {[number, number]}.
75+
*/
4476
forEach(callbackfn) {
4577
this.map.forEach((set, first) => set.forEach(second => callbackfn([first, second])));
4678
}
79+
/**
80+
* Add a 2D vector to this Set2D instance
81+
*
82+
* If the given 2D vector is already present in this Set2D instance
83+
* the vector will NOT be added to the Set2D and the Set2D instance will not change
84+
*
85+
* @param first The first component of the 2D vector to add
86+
* @param second The second component of the 2D vector to add
87+
*/
4788
add(first, second) {
4889
var _a, _b;
4990
if (((_a = this.map.get(first)) === null || _a === void 0 ? void 0 : _a.has(second)) === false) {
@@ -55,43 +96,105 @@ class Set2D {
5596
this._length += 1;
5697
}
5798
}
99+
/**
100+
* Remove a 2D vector to this Set2D instance
101+
*
102+
* If the given 2D vector is not present in this Set2D instance, then
103+
* the Set2D instance will not change and this function will do nothing
104+
*
105+
* @param first The first component of the 2D vector to remove
106+
* @param second The second component of the 2D vector to remove
107+
*/
58108
remove(first, second) {
59109
let set;
60110
if (set = this.map.get(first)) {
61111
if (set.has(second)) {
62112
set.delete(second);
63113
this._length -= 1;
64-
// if (set.size === 0) {
65-
// this.map.delete(first)
66-
// }
114+
if (set.size === 0) {
115+
this.map.delete(first);
116+
}
67117
}
68118
}
69119
}
120+
/**
121+
* Check whether this Set2D instance contains a given 2D vector.
122+
*
123+
* @param first The first component of the 2D Vector to check
124+
* @param second The second component of the 2D Vector to check
125+
* @returns Whether the given 2D vector is present in this Set2D instance
126+
*/
70127
has(first, second) {
71128
var _a;
72129
return ((_a = this.map.get(first)) === null || _a === void 0 ? void 0 : _a.has(second)) || false;
73130
}
131+
/**
132+
* Check whether this Set2D instance contains all of the given 2D vectors in a list.
133+
*
134+
* @param tuples The 2D vectors to check for existence in this Set2D instance
135+
* @returns Whether all given 2D vectos are present in this Set2D instance
136+
*/
74137
hasAll(tuples) {
75138
return tuples.every(tuple => this.has(tuple[0], tuple[1]));
76139
}
140+
/**
141+
* Check whether this Set2D instance contains all and ONLY all of the given 2D vectors in a list.
142+
*
143+
* @param tuples The 2D vectors to check for existence in this Set2D instance
144+
* @returns Whether all given 2D vectos are present in this Set2D instance,
145+
*/
77146
hasAllExact(tuples) {
78147
return tuples.length === this.length && this.hasAll(tuples);
79148
}
149+
/**
150+
* Combine this Set2D with other Set2D's and/or 2D vector arrays
151+
*
152+
* Similar to the concat function in Javascript Arrays
153+
*
154+
* Note that this Set2D instance will NOT be modified by the "combine" function. In order to push 2D vector data and
155+
* modify this Set2D instance in-place, use the "push" function.
156+
*
157+
* @param others Variable arguments. Other Set2D's or 2D vector arrays
158+
* @returns A new Set2D instance created by combining 2D vectors present in this Set2D instance,
159+
* given Set2D instances, and given 2D vector arrays
160+
*/
80161
combine(...others) {
81162
const set = new Set2D();
82163
set.push(this, ...others);
83164
return set;
84165
}
166+
/**
167+
* Push 2D vector values into this Set2D instance
168+
*
169+
* Similar to the push function in Javascript Arrays
170+
*
171+
* Note that this Set2D instance will be modified by pushing data. In order to create a new Set2D instance
172+
* with combined data without modifying this Set2D instance in-place, use the "combine" function.
173+
*
174+
* @param others Variable argument parameter, where other Set2D's or arrays of 2D vectors can be pushed into this Set2D instance.
175+
* Each given vector is added to this Set2D instance.
176+
*/
85177
push(...others) {
86-
others.forEach(other => other.forEach(tuple => this.add(tuple[0], tuple[1])));
178+
others.forEach(other => Array.isArray(other) ? this.add(other[0], other[1]) : other.forEach(tuple => this.add(tuple[0], tuple[1])));
87179
}
180+
/**
181+
* Iterator integration for "for ... of " syntax
182+
*/
88183
*[Symbol.iterator]() {
89184
for (const pair of this.map) {
90185
for (const second of pair[1]) {
91186
yield [pair[0], second];
92187
}
93188
}
94189
}
190+
/**
191+
* Check whether 2 Set2D instances are equal
192+
*
193+
* 2 Set2D instances are considered "equal" if they have the same stored vectors present within
194+
*
195+
* @param other The other Set2D instance to check equality with
196+
* @returns Whether the two Set2D instances are equal according to the stated conditions.
197+
*/
95198
equals(other) {
96199
if (this.length !== other.length) {
97200
return false;

dist/formats/file/life105.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function readLife105CellBlock(data) {
4848
return {
4949
x: x,
5050
y: y,
51-
width: matrix[0].length,
51+
width: matrix.length !== 0 ? matrix[0].length : 0,
5252
height: matrix.length,
5353
pattern: matrix,
5454
liveCoordinates: liveCoordinates

dist/formats/file/plaintext.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function readPlaintextDiagramToMatrix(str) {
156156
if (!isPlaintextDiagram(str)) {
157157
throw new Error(`[llcacodec::readPlaintextDiagramToXY] attempted to read invalid plaintext diagram ${str}`);
158158
}
159-
const lines = (0, util_1.trimTrailing)(str, "\n").split("\n");
159+
const lines = str.trim().replace("\r", "").split("\n");
160160
const width = Math.max(...lines.map(line => line.length));
161161
return lines.map(line => {
162162
const newLine = new Array(width);
@@ -170,15 +170,19 @@ function readPlaintextDiagramToMatrix(str) {
170170
else if (VALID_DEAD_CELL_CHARACTERS.some(ch => ch === line[i])) {
171171
newLine[i] = 0;
172172
}
173-
else if (line[i] !== " ") {
174-
throw new Error();
173+
else if (line[i] !== " " && line[i] !== "\r") {
174+
throw new Error(`[llcacodec::readPlaintextDiagramToMatrix Found invalid character (UTF-8 code: ${line[i].charCodeAt(0)})`);
175175
}
176176
}
177177
return newLine;
178178
});
179179
}
180180
exports.readPlaintextDiagramToMatrix = readPlaintextDiagramToMatrix;
181181
function isPlaintextDiagram(line) {
182-
return line.split("").every(char => VALID_DEAD_CELL_CHARACTERS.some(ch => ch === char) || VALID_LIVE_CELL_CHARACTERS.some(ch => ch === char) || char === " " || char === "\n");
182+
return line.split("").every(char => VALID_DEAD_CELL_CHARACTERS.some(ch => ch === char) ||
183+
VALID_LIVE_CELL_CHARACTERS.some(ch => ch === char) ||
184+
char === " "
185+
|| char === "\n"
186+
|| char === "\r");
183187
}
184188
exports.isPlaintextDiagram = isPlaintextDiagram;

0 commit comments

Comments
 (0)