Skip to content

Commit

Permalink
Implement dual-color galaxies/lotuses
Browse files Browse the repository at this point in the history
  • Loading branch information
hlysine committed Jan 25, 2025
1 parent 0afbdfe commit 1eb00ce
Show file tree
Hide file tree
Showing 10 changed files with 311 additions and 99 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Added drum samples (kick, snare, hihat, crash, tom, rim) to music grids
- Fixed symbol colors on hidden tiles
- Added a config in the Hidden symbol to reveal location
- Added support for dual-color galaxies and lotuses

# 24/1/2025

Expand Down
4 changes: 4 additions & 0 deletions packages/logic-core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 0.2.2

- Added support for dual-color galaxies and lotuses

# 0.2.1

- Added a config in Hidden symbol to reveal location
Expand Down
5 changes: 4 additions & 1 deletion packages/logic-core/assets/logic-core.global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2011,7 +2011,6 @@ declare global {
get explanation(): string;
get configs(): readonly AnyConfig[] | null;
createExampleGrid(): GridData;
private getColor;
private deltaCoordinate;
validateSymbol(grid: GridData): State;
copyWith({ x, y }: { x?: number; y?: number }): this;
Expand All @@ -2020,6 +2019,8 @@ declare global {
export declare abstract class DirectionLinkerBTModule extends BTModule {
instr: DirectionLinkerSymbol;
constructor(instr: DirectionLinkerSymbol);
private initialPositions;
private getInitialPositions;
checkGlobal(grid: BTGridData): CheckResult | false;
protected abstract movePos(
grid: BTGridData,
Expand Down Expand Up @@ -2127,6 +2128,8 @@ declare global {
x: number,
y: number
): Position$1 | null;
private getTileSafe;
checkGlobal(grid: BTGridData): false | CheckResult;
}
export declare class MinesweeperSymbol extends NumberSymbol {
private static readonly CONFIGS;
Expand Down
2 changes: 1 addition & 1 deletion packages/logic-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@logic-pad/core",
"version": "0.2.1",
"version": "0.2.2",
"type": "module",
"files": [
"dist",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default class BacktrackSolver extends Solver {

worker.addEventListener('error', (e: ErrorEvent) => {
alert(`Error while solving!\n${e.message}`);
fail(new Error(e.message));
fail(e as unknown as Error);
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Position } from '../../../primitives.js';
import DirectionLinkerSymbol from '../../../symbols/directionLinkerSymbol.js';
import BTModule, {
BTColor,
BTGridData,
BTTile,
CheckResult,
IntArray2D,
Rating,
createOneTileResult,
} from '../data.js';

export default abstract class DirectionLinkerBTModule extends BTModule {
Expand All @@ -17,43 +17,123 @@ export default abstract class DirectionLinkerBTModule extends BTModule {
this.instr = instr;
}

public checkGlobal(grid: BTGridData): CheckResult | false {
const thisX = Math.floor(this.instr.x);
const thisY = Math.floor(this.instr.y);
private initialPositions: Position[] | null = null;

const tile = grid.getTile(thisX, thisY);
private getInitialPositions(): Position[] {
if (this.instr.x % 1 !== 0 && this.instr.y % 1 !== 0)
return [
{ x: Math.floor(this.instr.x), y: Math.floor(this.instr.y) },
{ x: Math.ceil(this.instr.x), y: Math.ceil(this.instr.y) },
{ x: Math.floor(this.instr.x), y: Math.ceil(this.instr.y) },
{ x: Math.ceil(this.instr.x), y: Math.floor(this.instr.y) },
];
else if (this.instr.x % 1 !== 0)
return [
{ x: Math.floor(this.instr.x), y: this.instr.y },
{ x: Math.ceil(this.instr.x), y: this.instr.y },
];
else if (this.instr.y % 1 !== 0)
return [
{ x: this.instr.x, y: Math.floor(this.instr.y) },
{ x: this.instr.x, y: Math.ceil(this.instr.y) },
];
else return [{ x: this.instr.x, y: this.instr.y }];
}

if (tile === BTTile.Empty)
return createOneTileResult(grid, { x: thisX, y: thisY });
public checkGlobal(grid: BTGridData): CheckResult | false {
if (this.initialPositions === null)
this.initialPositions = this.getInitialPositions();

const tilesNeedCheck = IntArray2D.create(grid.width, grid.height);
const ratings: Rating[] = [];

const queue: Position[] = [{ x: thisX, y: thisY }];
let checkable = false;
for (const pos of this.initialPositions) {
const tile = grid.isInBound(pos.x, pos.y)
? grid.getTile(pos.x, pos.y)
: BTTile.NonExist;
if (tile === BTTile.Empty) {
const oppoPos = this.movePos(grid, pos.x, pos.y);
if (
oppoPos === null ||
grid.getTile(oppoPos.x, oppoPos.y) === BTTile.NonExist
)
return false;
else {
tilesNeedCheck.set(pos.x, pos.y, 1);
ratings.push({ pos, score: 1 });
}
} else if (tile === BTTile.NonExist) {
const oppoPos = this.movePos(grid, pos.x, pos.y);
if (
oppoPos !== null &&
grid.getTile(oppoPos.x, oppoPos.y) !== BTTile.NonExist
) {
return false;
}
} else {
const oppoPos = this.movePos(grid, pos.x, pos.y);
if (oppoPos !== null) {
const oppoTile = grid.getTile(oppoPos.x, oppoPos.y);
if (oppoTile === BTTile.Empty) {
tilesNeedCheck.set(pos.x, pos.y, 1);
ratings.push({ pos, score: 1 });
} else if (oppoTile === BTTile.NonExist) return false;
else checkable = true;
} else {
return false;
}
}
}

if (!checkable) {
return { tilesNeedCheck, ratings };
}

const queue: { pos: Position; color: BTColor; oppoColor: BTColor }[] =
this.initialPositions
.filter(
pos =>
grid.isInBound(pos.x, pos.y) &&
grid.getTile(pos.x, pos.y) !== BTTile.NonExist
)
.map(pos => {
const oppoPos = this.movePos(grid, pos.x, pos.y)!;
return {
pos,
color: grid.getTile(pos.x, pos.y) as BTColor,
oppoColor: grid.getTile(oppoPos.x, oppoPos.y) as BTColor,
};
});
const visited = IntArray2D.create(grid.width, grid.height);

// Visit all connected tiles
while (queue.length > 0) {
const curPos = queue.pop()!;

if (visited.get(curPos.x, curPos.y)) continue;
visited.set(curPos.x, curPos.y, 1);
if (visited.get(curPos.pos.x, curPos.pos.y)) continue;
visited.set(curPos.pos.x, curPos.pos.y, 1);

const oppoPos = this.movePos(grid, curPos.x, curPos.y);
const oppoPos = this.movePos(grid, curPos.pos.x, curPos.pos.y);
if (oppoPos === null) return false;

const oppoTile = grid.getTile(oppoPos.x, oppoPos.y);
if (!(oppoTile === BTTile.Empty || oppoTile === tile)) return false;
if (!(oppoTile === BTTile.Empty || oppoTile === curPos.oppoColor))
return false;

for (const edge of grid.getEdges(curPos)) {
for (const edge of grid.getEdges(curPos.pos)) {
if (visited.get(edge.x, edge.y)) continue;

const edgeTile = grid.getTile(edge.x, edge.y);
if (edgeTile === BTTile.Empty) {
tilesNeedCheck.set(edge.x, edge.y, 1);
ratings.push({ pos: edge, score: 1 });
} else if (edgeTile === tile) {
queue.push(edge);
} else if (edgeTile === curPos.color) {
queue.push({
pos: edge,
color: curPos.color,
oppoColor: curPos.oppoColor,
});
}
}
}
Expand Down
46 changes: 45 additions & 1 deletion packages/logic-core/src/data/solver/backtrack/symbols/lotus.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Orientation, Position } from '../../../primitives.js';
import LotusSymbol from '../../../symbols/lotusSymbol.js';
import { BTGridData } from '../data.js';
import { BTGridData, BTTile, CheckResult } from '../data.js';
import DirectionLinkerBTModule from './directionLinker.js';

export default class LotusBTModule extends DirectionLinkerBTModule {
Expand Down Expand Up @@ -40,4 +40,48 @@ export default class LotusBTModule extends DirectionLinkerBTModule {

return grid.isInBound(pos.x, pos.y) ? pos : null;
}

private getTileSafe(grid: BTGridData, x: number, y: number): BTTile {
return grid.isInBound(x, y) ? grid.getTile(x, y) : BTTile.NonExist;
}

public checkGlobal(grid: BTGridData): false | CheckResult {
if (
this.instr.orientation === Orientation.DownLeft ||
this.instr.orientation === Orientation.DownRight ||
this.instr.orientation === Orientation.UpLeft ||
this.instr.orientation === Orientation.UpRight
) {
if (this.instr.x % 1 === 0 || this.instr.y % 1 === 0)
if (this.instr.x % 1 !== 0 || this.instr.y % 1 !== 0) {
if (
this.getTileSafe(
grid,
Math.floor(this.instr.x),
Math.floor(this.instr.y)
) === BTTile.NonExist &&
this.getTileSafe(
grid,
Math.ceil(this.instr.x),
Math.ceil(this.instr.y)
) === BTTile.NonExist &&
this.getTileSafe(
grid,
Math.floor(this.instr.x),
Math.ceil(this.instr.y)
) === BTTile.NonExist &&
this.getTileSafe(
grid,
Math.ceil(this.instr.x),
Math.floor(this.instr.y)
) === BTTile.NonExist
) {
return { tilesNeedCheck: null, ratings: null };
} else {
return false;
}
}
}
return super.checkGlobal(grid);
}
}
Loading

0 comments on commit 1eb00ce

Please sign in to comment.