@@ -85,7 +86,7 @@ function Game({dispatchGameState, gameState, isDaily}) {
+
{gameState.letters.map((letter, index) => (
- {boxes}
+ {boxes}
{word}
);
diff --git a/src/logic/gameInit.js b/src/logic/gameInit.js
index 098e8a9..2d87de6 100644
--- a/src/logic/gameInit.js
+++ b/src/logic/gameInit.js
@@ -67,13 +67,15 @@ export function gameInit({
return {...savedState, playedIndexes: [], result: ""};
}
- const gridSize = 4;
-
difficultyLevel = isDaily ? getDifficultyLevelForDay() : difficultyLevel || 3;
const [minWordLength, maxWordLength] =
getShapeSizeForDifficulty(difficultyLevel);
+ // if the min word length is >=5, set the grid size to 5; otherwise, set it to 4
+ // This helps prevent shapes that have a low number of possible placements
+ const gridSize = minWordLength >= 5 ? 5 : 4;
+
const [letters, shapes, officialSolutions] = getGame({
gridSize,
minWordLength,
diff --git a/src/logic/getColumnIndex.js b/src/logic/getColumnIndex.js
new file mode 100644
index 0000000..3dae6df
--- /dev/null
+++ b/src/logic/getColumnIndex.js
@@ -0,0 +1,9 @@
+// Gets the column index of a given index in a grid, assuming a square grid
+export function getColumnIndex(index, gridSize) {
+ // error if index exceeds the grid size
+ if (index >= gridSize * gridSize || index < 0) {
+ throw new Error("Index is not within grid size");
+ }
+
+ return index % gridSize;
+}
diff --git a/src/logic/getColumnIndex.test.js b/src/logic/getColumnIndex.test.js
new file mode 100644
index 0000000..12baa26
--- /dev/null
+++ b/src/logic/getColumnIndex.test.js
@@ -0,0 +1,56 @@
+import {getColumnIndex} from "./getColumnIndex";
+
+describe("getColumnIndex", () => {
+ test("returns the column index of a given index (grid size 4)", () => {
+ const indexes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+ const gridSize = 4;
+ const expectedColumns = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3];
+ const result = indexes.map((index) => getColumnIndex(index, gridSize));
+ expect(result).toEqual(expectedColumns);
+ });
+
+ test("returns the column index of a given index (grid size 5)", () => {
+ const indexes = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24,
+ ];
+ const gridSize = 5;
+ const expectedColumns = [
+ 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4,
+ ];
+ const result = indexes.map((index) => getColumnIndex(index, gridSize));
+ expect(result).toEqual(expectedColumns);
+ });
+
+ test("returns the column index of a given index (grid size 1)", () => {
+ const index = 0;
+ const gridSize = 1;
+ const expectedColumn = 0;
+ const result = getColumnIndex(index, gridSize);
+ expect(result).toEqual(expectedColumn);
+ });
+
+ test("errors if index exceeds gridSize", () => {
+ const index = 4;
+ const gridSize = 2;
+ expect(() => getColumnIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+
+ test("errors if index is negative", () => {
+ const index = -1;
+ const gridSize = 2;
+ expect(() => getColumnIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+
+ test("errors if gridSize is 0", () => {
+ const index = 0;
+ const gridSize = 0;
+ expect(() => getColumnIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+});
diff --git a/src/logic/getLettersAndShapes.js b/src/logic/getLettersAndShapes.js
index 298635a..de1ceaf 100644
--- a/src/logic/getLettersAndShapes.js
+++ b/src/logic/getLettersAndShapes.js
@@ -4,6 +4,7 @@ import {trie} from "./trie";
import {shuffleArray} from "@skedwards88/word_logic";
import {omitDuplicateWordsAcrossShapes} from "./omitDuplicateWordsAcrossShapes";
import {centerIndexes} from "./centerIndexes";
+import {omitShapesThatExceedSize} from "./omitShapesThatExceedSize";
export function getLettersAndShapes({
gridSize,
@@ -23,7 +24,16 @@ export function getLettersAndShapes({
trie: trie,
});
- const shuffledWordIndexes = shuffleArray(wordIndexes, pseudoRandomGenerator);
+ // Remove wordIndexes that exceed a shape width or height of gridSize - 1
+ const wordIndexesOfAppropriateSize = omitShapesThatExceedSize({
+ wordIndexes,
+ gridSize,
+ });
+
+ const shuffledWordIndexes = shuffleArray(
+ wordIndexesOfAppropriateSize,
+ pseudoRandomGenerator,
+ );
// Figure out what shape each word makes
// by centering the word indexes in the grid
diff --git a/src/logic/getRowIndex.js b/src/logic/getRowIndex.js
new file mode 100644
index 0000000..5b0f150
--- /dev/null
+++ b/src/logic/getRowIndex.js
@@ -0,0 +1,9 @@
+// Gets the row index of a given index in a grid, assuming a square grid
+export function getRowIndex(index, gridSize) {
+ // error if index exceeds the grid size
+ if (index >= gridSize * gridSize || index < 0) {
+ throw new Error("Index is not within grid size");
+ }
+
+ return Math.floor(index / gridSize);
+}
diff --git a/src/logic/getRowIndex.test.js b/src/logic/getRowIndex.test.js
new file mode 100644
index 0000000..5401f96
--- /dev/null
+++ b/src/logic/getRowIndex.test.js
@@ -0,0 +1,56 @@
+import {getRowIndex} from "./getRowIndex";
+
+describe("getRowIndex", () => {
+ test("returns the row index of a given index (grid size 4)", () => {
+ const indexes = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+ const gridSize = 4;
+ const expectedRows = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3];
+ const result = indexes.map((index) => getRowIndex(index, gridSize));
+ expect(result).toEqual(expectedRows);
+ });
+
+ test("returns the row index of a given index (grid size 5)", () => {
+ const indexes = [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24,
+ ];
+ const gridSize = 5;
+ const expectedRows = [
+ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ ];
+ const result = indexes.map((index) => getRowIndex(index, gridSize));
+ expect(result).toEqual(expectedRows);
+ });
+
+ test("returns the row index of a given index (grid size 1)", () => {
+ const index = 0;
+ const gridSize = 1;
+ const expectedRow = 0;
+ const result = getRowIndex(index, gridSize);
+ expect(result).toEqual(expectedRow);
+ });
+
+ test("errors if index exceeds gridSize", () => {
+ const index = 4;
+ const gridSize = 2;
+ expect(() => getRowIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+
+ test("errors if index is negative", () => {
+ const index = -1;
+ const gridSize = 2;
+ expect(() => getRowIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+
+ test("errors if gridSize is 0", () => {
+ const index = 0;
+ const gridSize = 0;
+ expect(() => getRowIndex(index, gridSize)).toThrow(
+ "Index is not within grid size",
+ );
+ });
+});
diff --git a/src/logic/omitShapesThatExceedSize.js b/src/logic/omitShapesThatExceedSize.js
new file mode 100644
index 0000000..de1bb9f
--- /dev/null
+++ b/src/logic/omitShapesThatExceedSize.js
@@ -0,0 +1,22 @@
+import {getColumnIndex} from "./getColumnIndex";
+import {getRowIndex} from "./getRowIndex";
+
+// Remove wordIndexes that exceed a shape width or height of gridSize - 1
+export function omitShapesThatExceedSize({wordIndexes, gridSize}) {
+ const filteredWordIndexes = wordIndexes.filter((indexes) => {
+ const rowIndexes = indexes.map((index) => getRowIndex(index, gridSize));
+ const columnIndexes = indexes.map((index) =>
+ getColumnIndex(index, gridSize),
+ );
+ const maxRow = Math.max(...rowIndexes);
+ const minRow = Math.min(...rowIndexes);
+ const maxColumn = Math.max(...columnIndexes);
+ const minColumn = Math.min(...columnIndexes);
+
+ return (
+ maxRow - minRow < gridSize - 1 && maxColumn - minColumn < gridSize - 1
+ );
+ });
+
+ return filteredWordIndexes;
+}
diff --git a/src/logic/omitShapesThatExceedSize.test.js b/src/logic/omitShapesThatExceedSize.test.js
new file mode 100644
index 0000000..b923e7e
--- /dev/null
+++ b/src/logic/omitShapesThatExceedSize.test.js
@@ -0,0 +1,103 @@
+import {omitShapesThatExceedSize} from "./omitShapesThatExceedSize";
+
+describe("omitShapesThatExceedSize", () => {
+ test("removes wordIndexes that exceed a shape width or height of gridSize - 1 (grid size 5)", () => {
+ const wordIndexes = [
+ [0, 1, 2, 3], // width 4, height 1
+ [2, 3, 4, 0, 1], // width 5, height 1
+ [15, 18, 19, 20, 16, 17], // width 5, height 2
+ [10, 11, 12, 13, 14, 15, 16], // width 5, height 2
+ [0, 1, 2, 3, 5, 6, 7, 8], // width 4, height 2
+ [15, 10, 5, 0], // width 1, height 4
+ [2, 7, 12, 17, 22], // width 1, height 5
+ [0, 5, 10, 15, 20, 2], // width 2, height 5
+ [3, 7, 11, 15], // width 4, height 4
+ [0, 6, 12, 18, 24], // width 5, height 5
+ ];
+ const gridSize = 5;
+ const expectedWordIndexes = [
+ [0, 1, 2, 3], // width 4, height 1
+ [0, 1, 2, 3, 5, 6, 7, 8], // width 4, height 2
+ [15, 10, 5, 0], // width 1, height 4
+ [3, 7, 11, 15], // width 4, height 4
+ ];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+
+ test("removes wordIndexes that exceed a shape width or height of gridSize - 1 (grid size 4)", () => {
+ const wordIndexes = [
+ [1, 2, 3], // width 3, height 1
+ [0, 1, 2, 3], // width 4, height 1
+ [2, 3, 7, 1], // width 3, height 2
+ [2, 3, 4, 0, 1], // width 4, height 2
+ [7, 8, 9, 10, 11, 12], // width 4, height 3
+ [8, 4, 9, 10, 12], // width 3, height 3
+ [12, 8, 4, 0], // width 1, height 4
+ [12, 8, 4], // width 1, height 3
+ [0, 4, 8, 12, 5], // width 2, height 4
+ [0, 4, 8, 5], // width 2, height 3
+ [3, 6, 9, 12], // width 4, height 4
+ ];
+ const gridSize = 4;
+ const expectedWordIndexes = [
+ [1, 2, 3], // width 3, height 1
+ [2, 3, 7, 1], // width 3, height 2
+ [8, 4, 9, 10, 12], // width 3, height 3
+ [12, 8, 4], // width 1, height 3
+ [0, 4, 8, 5], // width 2, height 3
+ ];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+
+ test("works with empty wordIndexes input", () => {
+ const wordIndexes = [];
+ const gridSize = 4;
+ const expectedWordIndexes = [];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+
+ test("works with singleton wordIndexes input", () => {
+ const wordIndexes = [[0, 1, 2, 3]];
+ const gridSize = 4;
+ const expectedWordIndexes = [];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+
+ test("does not remove any indexes if all are within size", () => {
+ const wordIndexes = [
+ [1, 2, 3], // width 3, height 1
+ [2, 3, 7, 1], // width 3, height 2
+ [8, 4, 9, 10, 12], // width 3, height 3
+ [12, 8, 4], // width 1, height 3
+ [0, 4, 8, 5], // width 2, height 3
+ ];
+ const gridSize = 4;
+ const expectedWordIndexes = [
+ [1, 2, 3], // width 3, height 1
+ [2, 3, 7, 1], // width 3, height 2
+ [8, 4, 9, 10, 12], // width 3, height 3
+ [12, 8, 4], // width 1, height 3
+ [0, 4, 8, 5], // width 2, height 3
+ ];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+
+ test("removes all input if all exceed size", () => {
+ const wordIndexes = [
+ [0, 1, 2, 3], // width 4, height 1
+ [2, 3, 4, 0, 1], // width 4, height 2
+ [7, 8, 9, 10, 11, 12], // width 4, height 3
+ [12, 8, 4, 0], // width 1, height 4
+ [3, 6, 9, 12], // width 4, height 4
+ ];
+ const gridSize = 4;
+ const expectedWordIndexes = [];
+ const result = omitShapesThatExceedSize({wordIndexes, gridSize});
+ expect(result).toEqual(expectedWordIndexes);
+ });
+});
diff --git a/src/styles/App.css b/src/styles/App.css
index 6d2b2ba..78469cb 100644
--- a/src/styles/App.css
+++ b/src/styles/App.css
@@ -1,7 +1,8 @@
html {
- --board-box-diam: min(10vmax, 20vmin);
+ --board-box-diam-4: min(10vmax, 20vmin);
+ --board-box-diam-5: min(8vmax, 16vmin);
--shape-box-diam: min(5vmin, 3vmax);
- --default-font-size: calc(var(--board-box-diam) * 0.4);
+ --default-font-size: calc(var(--board-box-diam-4) * 0.4);
--dark-color: rgb(55 54 71);
--light-color: rgb(239 239 239);
@@ -108,14 +109,22 @@ button:disabled {
background-color: var(--dark-color);
touch-action: none;
justify-content: center;
- grid-template-columns: repeat(4, var(--board-box-diam));
- grid-template-rows: repeat(4, var(--board-box-diam));
justify-self: center;
align-items: center;
justify-items: center;
align-self: center;
}
+#board.size4 {
+ grid-template-columns: repeat(4, var(--board-box-diam-4));
+ grid-template-rows: repeat(4, var(--board-box-diam-4));
+}
+
+#board.size5 {
+ grid-template-columns: repeat(5, var(--board-box-diam-5));
+ grid-template-rows: repeat(5, var(--board-box-diam-5));
+}
+
.letterBox {
width: 95%;
height: 95%;
@@ -128,7 +137,6 @@ button:disabled {
background-color: transparent;
width: 60%;
height: 60%;
- font-size: calc(var(--default-font-size) * 1.2);
color: var(--dark-color);
display: flex;
align-items: center;
@@ -144,6 +152,14 @@ button:disabled {
color: var(--light-color);
}
+#board.size4 > .letter {
+ font-size: calc(var(--board-box-diam-4) * 0.5);
+}
+
+#board.size5 > .letter {
+ font-size: calc(var(--board-box-diam-4) * 0.5);
+}
+
#shapes {
display: grid;
grid-area: shapes;
@@ -161,9 +177,17 @@ button:disabled {
.shape {
display: grid;
+ margin: calc(var(--shape-box-diam) * 0.4);
+}
+
+.shape.size4 {
grid-template-columns: repeat(4, var(--shape-box-diam));
grid-template-rows: repeat(4, var(--shape-box-diam));
- margin: calc(var(--shape-box-diam) * 0.4);
+}
+
+.shape.size5 {
+ grid-template-columns: repeat(5, var(--shape-box-diam));
+ grid-template-rows: repeat(5, var(--shape-box-diam));
}
.shapeBox.filled {