Skip to content

Commit 581fd8e

Browse files
committed
Initiation \!o-o\!
0 parents  commit 581fd8e

File tree

11 files changed

+273
-0
lines changed

11 files changed

+273
-0
lines changed

.gitignore

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
8+
# testing
9+
/coverage
10+
11+
# production
12+
/build
13+
/dist
14+
sensitive/*
15+
16+
# misc
17+
.DS_Store
18+
.env.local
19+
.env.development.local
20+
.env.test.local
21+
.env.production.local
22+
23+
npm-debug.log*
24+
yarn-debug.log*
25+
yarn-error.log*
26+
27+
# ctags for indexing
28+
/tags
29+
tags.lock
30+
tags.temp
31+
package-lock.json

babel.config.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module.exports = {
2+
env: {
3+
test: {
4+
plugins: ["@babel/plugin-transform-modules-commonjs"]
5+
}
6+
},
7+
presets: [
8+
['@babel/preset-env', {targets: {node: 'current'}}],
9+
'@babel/preset-typescript',
10+
],
11+
};

core/util.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
3+
/**
4+
* Test if a matrix is rectangular or not
5+
*
6+
* A matrix is considered rectangular if it's height is not 0, and all rows have the same amount of columns
7+
*
8+
* @param matrix A matrix of a data type
9+
* @returns If the matrix is rectangular or not
10+
*/
11+
export function isRectangularMatrix<T>(matrix: T[][]): boolean {
12+
if (matrix.length === 0) return true;
13+
const width = matrix[0].length;
14+
15+
for (let row = 0; row < matrix.length; row++) {
16+
if (matrix[row].length !== width) return false;
17+
}
18+
return true;
19+
}

formats/core.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export {}
2+
3+
const LifeFileExtensions = [".lif", ".life"] as const;

formats/life105.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Life 1.05 File Format Spec: https://conwaylife.com/wiki/Life_1.05
2+
3+
const LIFE_105_HEADER = "#Life 1.05" as const
4+
const MAX_DESCRIPTION_LINE_COUNT = 22 as const
5+
const LIFE_105_MAX_LINE_LENGTH = 80 as const
6+
7+
const Life105FileExtensions = [".lif", ".life"] as const;
8+
9+
type LifeRuleData = { birth: number[], survival: number[] }
10+
11+
interface Life105Config {
12+
descriptions: string | string[],
13+
rule: string | LifeRuleData | [number[], number[]] | "N#"
14+
}
15+
16+
export function writeLife105File(pattern: (0 | 1)[][], config: Life105Config): string {
17+
return ""
18+
}
19+
20+
export function readLife105File(): (0 | 1)[][] {
21+
return []
22+
}
23+
24+
export function hello(): number {
25+
return 5;
26+
}

formats/rule.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
3+
4+
export function isValidLifeString(lifeString: string, errorOutput?: (error: string) => any) {
5+
const error = getLifeStringError(lifeString);
6+
if (error.length > 0) {
7+
errorOutput?.(error)
8+
return false;
9+
}
10+
return true;
11+
}
12+
13+
export function getLifeStringError(lifeString: string): string {
14+
const sides = lifeString.split("/");
15+
if (sides.length !== 2) {
16+
return "Error: Not able to split string into birth and survival counts, format must include a forward slash B<NUMS>/S<NUMS> "
17+
} else if (sides[0].charAt(0) !== "B" || sides[1].charAt(0) !== "S") {
18+
return "Error: B and S are backwards, please switch to B<NUMS>/S<NUMS> "
19+
} else if (sides[0].substring(1).split('').some((char: string) => isNaN(Number.parseInt(char))) || sides[1].substring(1).split('').some((char: string) => isNaN(Number.parseInt(char)))) {
20+
return "Error: Must include numbers after B and after /S B<NUMS>/S<NUMS> "
21+
} else if (new Set<string>(sides[0].substring(1).split('')).size !== sides[0].length - 1 || new Set<string>(sides[1].substring(1).split('')).size !== sides[1].length - 1) {
22+
return "Error: Replicate number on one side of B<NUMS>/S<NUMS> "
23+
}
24+
25+
return "";
26+
}
27+
28+
function getCanMakeLifeStringError(survivalNums: number[], birthNums: number[]): string {
29+
if (survivalNums.some(num => num < 0 || num > 8)) {
30+
return "Survival neighborhood rules must be between 0 and 8";
31+
}
32+
if (birthNums.some(num => num < 0 || num > 8)) {
33+
return "Birth neighborhood rules must be between 0 and 8";
34+
}
35+
if (survivalNums.length > 8) {
36+
return "Can only have 8 maximum survival rules";
37+
}
38+
if (birthNums.length > 8) {
39+
return "Can only have 8 maximum birth rules";
40+
}
41+
if (survivalNums.length !== new Set<number>(survivalNums).size) {
42+
return "Not all survival rules are unique";
43+
}
44+
if (birthNums.length !== new Set<number>(birthNums).size) {
45+
return "Not all birth rules are unique";
46+
}
47+
48+
49+
return "";
50+
}
51+
52+
function canMakeLifeString(survivalNums: number[], birthNums: number[]): boolean {
53+
return getCanMakeLifeStringError(survivalNums, birthNums) === ""
54+
}
55+
56+
export function createLifeString(birthNums: number[], survivalNums: number[]): string {
57+
if (!canMakeLifeString(survivalNums, birthNums)) {
58+
throw new Error(`Cannot make new life string from ${survivalNums} and ${birthNums}: ${getCanMakeLifeStringError(survivalNums, birthNums)}`);
59+
}
60+
61+
return "B".concat( birthNums.join("") ).concat('/S').concat( survivalNums.join("") );
62+
}
63+
64+
export function parseLifeLikeString(lifeString: string): LifeRuleData {
65+
let lifeData: LifeRuleData = {birth: [], survival: []};
66+
if (!isValidLifeString(lifeString)) {
67+
return lifeData;
68+
}
69+
70+
const [ birth, survival ] = lifeString.split("/");
71+
72+
for (let i = 1; i < birth.length; i++) {
73+
const num: number = Number.parseInt(birth.charAt(i));
74+
lifeData.birth.push(num);
75+
}
76+
77+
78+
for (let i = 1; i < survival.length; i++) {
79+
const num: number = Number.parseInt(survival.charAt(i));
80+
lifeData.survival.push(num);
81+
}
82+
83+
return lifeData;
84+
}

index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { hello } from "./formats/life105"
2+
3+
console.log("run", hello())

jest.config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import {Config} from 'jest';
2+
3+
const config: Config = {
4+
verbose: true,
5+
transform: {
6+
"^.+\\.[t|j]sx?$": "babel-jest"
7+
}
8+
};
9+
10+
export default config;

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"devDependencies": {
3+
"@babel/core": "^7.21.0",
4+
"@babel/plugin-transform-modules-commonjs": "^7.21.2",
5+
"@babel/preset-env": "^7.20.2",
6+
"@babel/preset-typescript": "^7.21.0",
7+
"@types/jest": "^29.4.0",
8+
"babel-jest": "^29.4.3",
9+
"jest": "^29.4.3",
10+
"ts-node": "^10.9.1"
11+
},
12+
"scripts": {
13+
"test": "jest"
14+
}
15+
}

tests/life105.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { hello } from "../formats/life105";
2+
3+
it("hello", () => {
4+
expect(hello()).toBe(5)
5+
})

0 commit comments

Comments
 (0)