@bitblit/ratchet-maze 6.0.146-alpha → 6.0.148-alpha
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/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitblit/ratchet-maze",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.148-alpha",
|
|
4
4
|
"description": "My silly tools for mazes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
|
+
"src/**",
|
|
7
8
|
"lib/**",
|
|
8
9
|
"bin/**"
|
|
9
10
|
],
|
|
@@ -43,6 +44,6 @@
|
|
|
43
44
|
},
|
|
44
45
|
"license": "Apache-2.0",
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@bitblit/ratchet-common": "6.0.
|
|
47
|
+
"@bitblit/ratchet-common": "6.0.148-alpha"
|
|
47
48
|
}
|
|
48
49
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BuildInformation } from '@bitblit/ratchet-common/build/build-information';
|
|
2
|
+
|
|
3
|
+
export class RatchetInfo {
|
|
4
|
+
// Prevent instantiation
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
6
|
+
private constructor() {
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public static buildInformation(): BuildInformation {
|
|
10
|
+
const val: BuildInformation = {
|
|
11
|
+
version: 'LOCAL-SNAPSHOT',
|
|
12
|
+
hash: 'LOCAL-HASH',
|
|
13
|
+
branch: 'LOCAL-BRANCH',
|
|
14
|
+
tag: 'LOCAL-TAG',
|
|
15
|
+
timeBuiltISO: 'LOCAL-TIME-ISO',
|
|
16
|
+
notes: 'LOCAL-NOTES',
|
|
17
|
+
};
|
|
18
|
+
return val;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { RectangularMaze } from "./rectangular-maze.js";
|
|
2
|
+
|
|
3
|
+
// 0,1,2,3...width-1
|
|
4
|
+
// width,
|
|
5
|
+
|
|
6
|
+
export class BinaryGenerator {
|
|
7
|
+
|
|
8
|
+
public static generate(width: number, height: number): RectangularMaze {
|
|
9
|
+
const rval: RectangularMaze = new RectangularMaze(width, height);
|
|
10
|
+
for (let y = 0; y < height; y++) {
|
|
11
|
+
for (let x = 0; x < width; x++) {
|
|
12
|
+
if (y === 0) {
|
|
13
|
+
if (x < width - 1) {
|
|
14
|
+
rval.addPassage({ x, y }, { x: x + 1, y: y });
|
|
15
|
+
}
|
|
16
|
+
} else if (x === width - 1) {
|
|
17
|
+
rval.addPassage({ x, y }, { x: x, y: y - 1 });
|
|
18
|
+
} else {
|
|
19
|
+
const flip: number = Math.random();
|
|
20
|
+
if (flip <= .5) {
|
|
21
|
+
rval.addPassage({ x, y }, { x: x + 1, y: y });
|
|
22
|
+
} else {
|
|
23
|
+
rval.addPassage({ x, y }, { x: x, y: y - 1 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return rval;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { RectangularMaze } from "./rectangular-maze.js";
|
|
2
|
+
import { RequireRatchet } from "@bitblit/ratchet-common/lang/require-ratchet";
|
|
3
|
+
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { Direction } from "./direction.js";
|
|
6
|
+
import { StringRatchet } from "@bitblit/ratchet-common/lang/string-ratchet";
|
|
7
|
+
import { BinaryGenerator } from "./binary-generator";
|
|
8
|
+
|
|
9
|
+
// 0,1,2,3...width-1
|
|
10
|
+
// width,
|
|
11
|
+
|
|
12
|
+
export class DrawingUtil {
|
|
13
|
+
|
|
14
|
+
public static formatBitmapToString(bmap: string[][], lineEnd: string = '\n'): string {
|
|
15
|
+
const rval: string = bmap.map(s=>s.join(' ')).join(lineEnd);
|
|
16
|
+
return rval;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static rectangularMazeToBitmap(maze: RectangularMaze): string[][] {
|
|
20
|
+
RequireRatchet.notNullOrUndefined(maze);
|
|
21
|
+
RequireRatchet.true(maze.width>0);
|
|
22
|
+
RequireRatchet.true(maze.height>0);
|
|
23
|
+
|
|
24
|
+
const rval: string[][]=[];
|
|
25
|
+
|
|
26
|
+
for (let y=0;y<maze.height;y++) {
|
|
27
|
+
const curRow: string[] = [];
|
|
28
|
+
for (let x=0;x<maze.width;x++) {
|
|
29
|
+
if (maze.isDisabled({x:x, y:y})) {
|
|
30
|
+
curRow.push('X');
|
|
31
|
+
} else {
|
|
32
|
+
let num: number = 0;
|
|
33
|
+
num += maze.hasWall({x,y}, Direction.North) ? 1 : 0;
|
|
34
|
+
num += maze.hasWall({x,y}, Direction.East) ? 2 : 0;
|
|
35
|
+
num += maze.hasWall({x,y}, Direction.South) ? 4 : 0;
|
|
36
|
+
num += maze.hasWall({x,y}, Direction.West) ? 8 : 0;
|
|
37
|
+
curRow.push(StringRatchet.HEXITS[num]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
rval.push(curRow);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return rval;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
public static rectangularMazeToSvg(maze: RectangularMaze, opts?: RectangularMazeDrawOptions): string {
|
|
48
|
+
RequireRatchet.notNullOrUndefined(maze);
|
|
49
|
+
RequireRatchet.true(maze.width>0);
|
|
50
|
+
RequireRatchet.true(maze.height>0);
|
|
51
|
+
|
|
52
|
+
const widthPx: number = maze.width * opts.cellSize;
|
|
53
|
+
const heightPx: number = maze.height * opts.cellSize;
|
|
54
|
+
|
|
55
|
+
let rval: string =
|
|
56
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${widthPx} ${heightPx}"><g> \n`
|
|
57
|
+
// `<text x="10" y="10" font-family="Style_Script, cursive" font-size="2">Test</text>`;
|
|
58
|
+
|
|
59
|
+
for (let y=0;y<maze.height;y++) {
|
|
60
|
+
for (let x=0;x<maze.width;x++) {
|
|
61
|
+
// Upper left corner
|
|
62
|
+
const uX: number = x * opts.cellSize;
|
|
63
|
+
const uY: number = y * opts.cellSize;
|
|
64
|
+
if (maze.isDisabled({x:x, y:y})) {
|
|
65
|
+
|
|
66
|
+
//curRow.push('X');
|
|
67
|
+
} else {
|
|
68
|
+
if (maze.hasWall({x,y}, Direction.North)) {
|
|
69
|
+
rval+=`<polyline stroke="#000080" stroke-width="1" fill="none" points="${uX},${uY} ${uX+opts.cellSize} ${uY}" />\n`;
|
|
70
|
+
}
|
|
71
|
+
if (maze.hasWall({x,y}, Direction.East)) {
|
|
72
|
+
rval+=`<polyline stroke="#000080" stroke-width="1" fill="none" points="${uX+opts.cellSize},${uY} ${uX+opts.cellSize} ${uY+opts.cellSize}" />\n`;
|
|
73
|
+
}
|
|
74
|
+
if (maze.hasWall({x,y}, Direction.South)) {
|
|
75
|
+
rval+=`<polyline stroke="#000080" stroke-width="1" fill="none" points="${uX},${uY+opts.cellSize} ${uX+opts.cellSize} ${uY+opts.cellSize}" />\n`;
|
|
76
|
+
}
|
|
77
|
+
if (maze.hasWall({x,y}, Direction.West)) {
|
|
78
|
+
rval+=`<polyline stroke="#000080" stroke-width="1" fill="none" points="${uX},${uY+opts.cellSize} ${uX} ${uY}" />\n`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
rval+='</g></svg>';
|
|
85
|
+
|
|
86
|
+
return rval;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface RectangularMazeDrawOptions {
|
|
92
|
+
backgroundColor: string;
|
|
93
|
+
wallColor: string;
|
|
94
|
+
disabledColor: string;
|
|
95
|
+
cellSize: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// DELETE ME
|
|
99
|
+
//x.addPassage(0,1);
|
|
100
|
+
//x.addPassage(1,3);
|
|
101
|
+
//const bmap: string[][]=DrawingUtil.rectangularMazeToBitmap(x);
|
|
102
|
+
//console.log(DrawingUtil.formatBitmapToString(bmap));
|
|
103
|
+
//const x: RectangularMaze = new RectangularMaze(10,10);
|
|
104
|
+
//const x: RectangularMaze = BinaryGenerator.generate(20,20);// new RectangularMaze(2,2);
|
|
105
|
+
//const out: string = DrawingUtil.rectangularMazeToSvg(x, {backgroundColor: '#FFF', wallColor: '#000', disabledColor: '#333', cellSize: 10});
|
|
106
|
+
//fs.writeFileSync('test.svg', out);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Coordinate } from "./coordinate.js";
|
|
2
|
+
import { RequireRatchet } from "@bitblit/ratchet-common/lang/require-ratchet";
|
|
3
|
+
import { Direction } from "./direction.js";
|
|
4
|
+
|
|
5
|
+
// 0,1,2,3...width-1
|
|
6
|
+
// width,
|
|
7
|
+
|
|
8
|
+
export class RectangularMaze {
|
|
9
|
+
|
|
10
|
+
private disabledIdx: Set<number> = new Set<number>();
|
|
11
|
+
private passages: Map<number, Set<number>> = new Map<number, Set<number>>();
|
|
12
|
+
|
|
13
|
+
constructor(private _width: number, private _height: number) {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
public isDisabled(idx: number | Coordinate): boolean {
|
|
17
|
+
if (typeof idx === 'number') {
|
|
18
|
+
return this.disabledIdx.has(idx);
|
|
19
|
+
} else {
|
|
20
|
+
return this.disabledIdx.has(this.coordinateToIndex(idx));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public neighborCoordinate(idx:number | Coordinate, direction: Direction): Coordinate {
|
|
25
|
+
let rval: Coordinate = structuredClone((typeof idx==='number') ? this.indexToCoordinate(idx) : idx);
|
|
26
|
+
|
|
27
|
+
switch (direction) {
|
|
28
|
+
case Direction.North : rval.y--;break;
|
|
29
|
+
case Direction.South : rval.y++;break;
|
|
30
|
+
case Direction.East : rval.x++;break;
|
|
31
|
+
case Direction.West : rval.x--;break;
|
|
32
|
+
default: throw new Error('Cannot happen - invalid direction');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (rval.x<0 || rval.y<0 || rval.x >=this.width || rval.y>=this.height) {
|
|
36
|
+
rval = null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return rval;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
public neighborIdx(idx:number, direction: Direction): number {
|
|
44
|
+
const n = this.neighborCoordinate(idx, direction);
|
|
45
|
+
return n===null ? null : this.coordinateToIndex(n);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public hasWall(idx: number | Coordinate, direction: Direction): boolean {
|
|
49
|
+
const idx1: number = (typeof(idx)==='number') ? idx : this.coordinateToIndex(idx);
|
|
50
|
+
const idx2: number = this.neighborIdx(idx1, direction);
|
|
51
|
+
return !this.hasPassage(idx1, idx2);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public get width(): number {
|
|
55
|
+
return this._width;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public get height(): number {
|
|
59
|
+
return this._height;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public coordinateToIndex(coord: Coordinate): number {
|
|
63
|
+
RequireRatchet.true(coord.x < this.width);
|
|
64
|
+
RequireRatchet.true(coord.y < this.height);
|
|
65
|
+
return (coord.y * this.width) + coord.x;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public indexToCoordinate(idx: number): Coordinate {
|
|
69
|
+
const row: number = Math.floor(idx/this.width);
|
|
70
|
+
const rval: Coordinate = {
|
|
71
|
+
x: idx - (row*this.width),
|
|
72
|
+
y: row
|
|
73
|
+
};
|
|
74
|
+
return rval;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public get maxIdx(): number {
|
|
78
|
+
return this.coordinateToIndex({x: this.width-1, y: this.height-1});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public validIdx(idx: number): boolean {
|
|
82
|
+
const crd: Coordinate = this.indexToCoordinate(idx);
|
|
83
|
+
return crd.x < this.width && crd.y < this.height;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public get allIdxWithPassages(): Set<number> {
|
|
87
|
+
const rval: Set<number> = new Set<number>();
|
|
88
|
+
Array.from(this.passages.values()).forEach(vals=>{
|
|
89
|
+
Array.from(vals).forEach(s=>rval.add(s));
|
|
90
|
+
})
|
|
91
|
+
return rval;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public disable(idx: number): void {
|
|
95
|
+
RequireRatchet.true(this.validIdx(idx));
|
|
96
|
+
RequireRatchet.true(!this.passages.has(idx));
|
|
97
|
+
RequireRatchet.true(!this.allIdxWithPassages.has(idx));
|
|
98
|
+
this.disabledIdx.add(idx);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public hasPassage(idx1: number, idx2: number): boolean {
|
|
102
|
+
let rval: boolean = false;
|
|
103
|
+
if (idx1!==null && idx2!==null) {
|
|
104
|
+
const vals: Set<number> = this.passages.get(Math.min(idx1, idx2));
|
|
105
|
+
rval = vals && vals.has(Math.max(idx1, idx2));
|
|
106
|
+
}
|
|
107
|
+
return rval;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
public addPassage(idxC1: number | Coordinate, idxC2: number | Coordinate) : void {
|
|
112
|
+
RequireRatchet.notNullOrUndefined(idxC1);
|
|
113
|
+
RequireRatchet.notNullOrUndefined(idxC2);
|
|
114
|
+
const idx1: number = (typeof idxC1 ==='number') ? idxC1 : this.coordinateToIndex(idxC1);
|
|
115
|
+
const idx2: number = (typeof idxC2 ==='number') ? idxC2 : this.coordinateToIndex(idxC2);
|
|
116
|
+
|
|
117
|
+
RequireRatchet.true(this.validIdx(idx1));
|
|
118
|
+
RequireRatchet.true(this.validIdx(idx2));
|
|
119
|
+
RequireRatchet.true(!this.disabledIdx.has(idx1));
|
|
120
|
+
RequireRatchet.true(!this.disabledIdx.has(idx2));
|
|
121
|
+
|
|
122
|
+
const cur: Set<number> = this.passages.get(Math.min(idx1, idx2)) ?? new Set<number>;
|
|
123
|
+
cur.add(Math.max(idx1,idx2));
|
|
124
|
+
this.passages.set(Math.min(idx1,idx2), cur);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public removePassage(idx1: number, idx2: number) : void {
|
|
128
|
+
RequireRatchet.true(this.validIdx(idx1));
|
|
129
|
+
RequireRatchet.true(this.validIdx(idx2));
|
|
130
|
+
|
|
131
|
+
const cur: Set<number> = this.passages.get(Math.min(idx1, idx2)) ?? new Set<number>;
|
|
132
|
+
cur.delete(Math.max(idx1, idx2));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|