Skip to content

Commit

Permalink
Finished generateSolutions script
Browse files Browse the repository at this point in the history
  • Loading branch information
John Davison committed Jul 8, 2023
1 parent f0fa871 commit ec45baf
Show file tree
Hide file tree
Showing 14 changed files with 453 additions and 289 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"name": "Next.js: debug scripts",
"type": "node-terminal",
"request": "launch",
"command": "node ./scripts/generateSolutions.js"
"command": "pnpm generateSolutions"
},

]
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@
"test": "vitest",
"coverage": "vitest run --coverage",
"seed": "pnpm ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts",
"generateSolutionsTsNode": "pnpm ts-node scripts/generateSolutions.ts",
"generateSolutions": "pnpm tsc -p tsconfig.scripts.json && node ./scripts/generateSolutions.js",
"generateSolutions": "pnpm ts-node --project tsconfig.scripts.json scripts/generateSolutions.ts",
"migrate": "pnpm prisma migrate dev"
},
"prisma": {
Expand Down
Binary file modified prisma/dev.db
Binary file not shown.
44 changes: 22 additions & 22 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,28 +47,28 @@ async function main() {
},
},
});
const level2Res = await prisma.level.create({
data: {
difficulty: "EASY",
solutionId: 1,
solutionPieces: {
connect: [
{
id: 5,
},
{
id: 6,
},
{
id: 7,
},
{
id: 8,
},
],
},
},
});
// const level2Res = await prisma.level.create({
// data: {
// difficulty: "EASY",
// solutionId: 1,
// solutionPieces: {
// connect: [
// {
// id: 5,
// },
// {
// id: 6,
// },
// {
// id: 7,
// },
// {
// id: 8,
// },
// ],
// },
// },
// });

// console.log();
}
Expand Down
82 changes: 82 additions & 0 deletions scripts/__tests__/getNextAvailableCell.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { test, expect, describe } from "vitest";
import { getNextAvailableCell } from "../generateSolutions";

describe("nextAvailableCell", () => {
test("empty grid", () => {
const grid: number[][] = [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

const nextAvailableCell = getNextAvailableCell(grid);
expect(nextAvailableCell).toEqual([0, 0]);
});

test("completed grid", () => {
const grid: number[][] = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

const nextAvailableCell = getNextAvailableCell(grid);
expect(nextAvailableCell).toEqual(false);
});

test("placed piece ", () => {
const grid: number[][] = [
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

const nextAvailableCell = getNextAvailableCell(grid);
expect(nextAvailableCell).toEqual([3, 0]);
});

test("placed piece with gaps", () => {
const grid: number[][] = [
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

const nextAvailableCell = getNextAvailableCell(grid);
expect(nextAvailableCell).toEqual([0, 0]);
});

test("placed piece and starting offset", () => {
const grid: number[][] = [
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

const nextAvailableCell = getNextAvailableCell(grid, [1, 0]);
expect(nextAvailableCell).toEqual([3, 0]);
});

test("placed pieces and starting offset", () => {
const grid: number[][] = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
];

const nextAvailableCell = getNextAvailableCell(grid, [1, 0]);
expect(nextAvailableCell).toEqual([4, 1]);
});
});
162 changes: 124 additions & 38 deletions scripts/generateSolutions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import {
getIsPiecePlaceable,
getPieceOverCells,
getRotatedAndFlippedShape,
nestedCopy,
} from "../src/app/play/[id]/sharedUtils";
import { PrismaClient, SolutionPiece } from "@prisma/client";
import { pieceOrientations } from "./pieceOrientations";
import { connect } from "http2";

type GeneratedSolutionPiece = Omit<
SolutionPiece,
Expand All @@ -20,15 +22,17 @@ const getPieces = async () => {
return await prisma.piece.findMany();
};

const getNextAvailableCell = (
grid: [number][],
export const getNextAvailableCell = (
grid: number[][],
starting: [number, number] = [0, 0]
): [number, number] | false => {
let y = starting[1];
let x = starting[0];
for (y; y <= grid.length; y++) {
for (x; x <= grid[0].length; x++) {
if (grid[x][y] === 0) {
let y = starting[1];
// console.log({ x, y });

for (y; y < grid.length; y++) {
for (x; x < grid[0].length; x++) {
if (grid[y][x] === 0) {
return [x, y];
}
}
Expand All @@ -50,47 +54,69 @@ const generate = async () => {
placePiece({
allPieces,
remainingPieces,
boardStateGrid,
boardGrid: boardStateGrid,
solutionPieces: [],
});
};

const placePiece = ({
const placePiece = async ({
allPieces,
remainingPieces,
boardStateGrid,
boardGrid,
solutionPieces,
previousAvailableCell,
}: {
allPieces: { id: number; shape: [number, number][] }[];
remainingPieces: number[];
boardStateGrid: [number][];
boardGrid: number[][];
solutionPieces: GeneratedSolutionPiece[];
previousAvailableCell?: [number, number];
}) => {
const nextAvailableCell = getNextAvailableCell(boardStateGrid);
console.log({ nextAvailableCell, remainingPieces, hi: "hi" });
printBoard(boardStateGrid);
const nextAvailableCell = getNextAvailableCell(
boardGrid,
previousAvailableCell
);
// console.log({ nextAvailableCell, remainingPieces });
// printBoard(boardGrid);

if (!nextAvailableCell) {
// found a solution
solutions.push(solutionPieces);
console.log("found a solition", { solutionPieces, solutions });
// solutions.push(solutionPieces);
console.count("Found a solution!");
// console.log({ nextAvailableCell, remainingPieces });
// printBoard(boardGrid);
await prisma.solution.create({
data: {
solutionPieces: {
create: solutionPieces.map(
({ rotation, isFlippedX, isFlippedY, placedInCells, pieceId }) => ({
rotation,
isFlippedX,
isFlippedY,
placedInCells: JSON.stringify(placedInCells),

piece: {
connect: { id: pieceId },
},
})
),
},
},
});
return;
}

remainingPieces.forEach((pieceId) => {
const piece = allPieces.find((piece) => piece.id === pieceId);
// console.log({ pieceId, piece });

if (!piece) {
throw new Error(`Piece ${pieceId} not found`);
}

pieceOrientations[pieceId].forEach((pieceOrientation) => {

// console.log("Current piece: ", pieceId);
// printShape(piece.shape);

// check if piece fits
// if it does, add to boardState
// else, generate next piece
pieceOrientations[pieceId].forEach((pieceOrientation) => {
const flippedShape = getRotatedAndFlippedShape(
piece.shape,
pieceOrientation.rotation,
Expand All @@ -101,9 +127,9 @@ const placePiece = ({
const pieceOverCells = getPieceOverCells(nextAvailableCell, flippedShape);

if (pieceOverCells) {
if (getIsPiecePlaceable(pieceOverCells, boardStateGrid)) {
if (getIsPiecePlaceable(pieceOverCells, boardGrid)) {
const updatedSolutionPieces = [
...solutionPieces,
...nestedCopy(solutionPieces),
{
pieceId: piece.id,
rotation: pieceOrientation.rotation,
Expand All @@ -114,34 +140,94 @@ const placePiece = ({
];

// add piece to boardState
const updatedBoardStateGrid = addPieceToBoard(
boardStateGrid,
pieceOverCells
);
const updatedBoardGrid = addPieceToBoard(boardGrid, pieceOverCells);

// remove piece from remainingPieces
const updatedRemainingPieces = remainingPieces.filter(
(id) => piece.id !== id
);

// place next piece
placePiece({
allPieces,
remainingPieces: updatedRemainingPieces,
boardStateGrid: updatedBoardStateGrid,
solutionPieces: updatedSolutionPieces,
});
if (!hasUnfillableCells(updatedBoardGrid)) {
// console.log("Piece placed", {
// id: piece.id,
// pieceOrientation,
// pieceOverCells,
// });
// console.log("Piece placed");
// printShape(flippedShape);
// printBoard(updatedBoardGrid);

// place next piece
placePiece({
allPieces,
remainingPieces: updatedRemainingPieces,
boardGrid: updatedBoardGrid,
solutionPieces: updatedSolutionPieces,
previousAvailableCell: [...nextAvailableCell],
});
}
}
console.log("piece not placeable", {pieceId, pieceOrientation});
}
});
// console.log("Piece not placeable: ", pieceId);
});
};

const printBoard = (boardStateGrid: [number][]) => {
const printBoard = (boardStateGrid: number[][]) => {
console.log("boardStateGrid:");
boardStateGrid.forEach((row) => {
console.log(row);
console.log(row.join(" "));
});
console.log(" ");
};

const printShape = (shape: number[][]) => {
console.log("Piece shape:");
shape.forEach((row) => {
console.log(row.join(" "));
});
}
console.log(" ");
};

/**
* Check if there are any cells that are not filled in but are surrounded by filled in cells
*/
const hasUnfillableCells = (grid: number[][]) => {
return grid.some((row, y) =>
row.some((cell, x) => {
return (
cell === 0 &&
getSurroundingCells(grid, x, y).every(([x, y]) => grid[y][x] === 1)
);
})
);
};

/**
* Get the grid position of the cells that are above below and to the left and right of the cell
*/

const getSurroundingCells = (
grid: number[][],
x: number,
y: number
): [number, number][] => {
const surroundingCells: [number, number][] = [];

if (x > 0) {
surroundingCells.push([x - 1, y]);
}
if (y > 0) {
surroundingCells.push([x, y - 1]);
}
if (x < grid[0].length - 1) {
surroundingCells.push([x + 1, y]);
}
if (y < grid.length - 1) {
surroundingCells.push([x, y + 1]);
}

return surroundingCells;
};

generate();
Loading

0 comments on commit ec45baf

Please sign in to comment.