From 4f80bead01d6d6badbd34cfef9ff9eead53669eb Mon Sep 17 00:00:00 2001 From: skedwards88 Date: Mon, 13 May 2024 06:52:37 -0700 Subject: [PATCH] eliminate shapes that span fill width or height of grid --- src/logic/getColumnIndex.js | 9 ++ src/logic/getColumnIndex.test.js | 56 +++++++++++ src/logic/getLettersAndShapes.js | 12 ++- src/logic/getRowIndex.js | 9 ++ src/logic/getRowIndex.test.js | 56 +++++++++++ src/logic/omitShapesThatExceedSize.js | 22 +++++ src/logic/omitShapesThatExceedSize.test.js | 103 +++++++++++++++++++++ 7 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 src/logic/getColumnIndex.js create mode 100644 src/logic/getColumnIndex.test.js create mode 100644 src/logic/getRowIndex.js create mode 100644 src/logic/getRowIndex.test.js create mode 100644 src/logic/omitShapesThatExceedSize.js create mode 100644 src/logic/omitShapesThatExceedSize.test.js 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); + }); +});