Skip to content

Commit

Permalink
add tests, split out logic
Browse files Browse the repository at this point in the history
  • Loading branch information
skedwards88 committed Apr 16, 2024
1 parent dc70739 commit 797ad37
Show file tree
Hide file tree
Showing 5 changed files with 441 additions and 17 deletions.
2 changes: 0 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,3 @@ omit "soaker" "ROAMER","Loo","Joe"
add "Jeer"

disfavor words that end in single s

don't allow dup words as plural
42 changes: 42 additions & 0 deletions src/logic/findIncompatibleShapeIdIfAny.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
function sharedWordsQ(words1, words2) {
for (const word of words1) {
if (words2.has(word)) {
return true;
}
}
return false;
}

export function findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
shapeId1,
shapeId2,
) {
// If the shapes share a word, remove the shape that has more words
if (sharedWordsQ(wordsForShape1, wordsForShape2)) {
if (wordsForShape1.size <= wordsForShape2.size) {
return shapeId2;
} else {
return shapeId1;
}
}

// If the singular and plural form of a word are both in the game,
// remove the shape that has the plural form.
// This isn't perfect because it doesn't account for irregular plurals
// and because it doesn't account for words that end in "S" that aren't plural,
// but it should catch most cases that are likely to be present in the game.
for (const word of wordsForShape1) {
if (wordsForShape2.has(word + "S")) {
return shapeId2;
}
}
for (const word of wordsForShape2) {
if (wordsForShape1.has(word + "S")) {
return shapeId1;
}
}

// Otherwise, the shapes are compatible. Return undefined.
}
84 changes: 84 additions & 0 deletions src/logic/findIncompatibleShapeIdIfAny.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {findIncompatibleShapeIdIfAny} from "./findIncompatibleShapeIdIfAny";

test("If two shapes share a word, the ID for the shape with the most words is returned", () => {
const wordsForShape1 = new Set(["CAT", "DOG"]);
const wordsForShape2 = new Set(["COW", "HORSE", "DOG"]);

expect(
findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
"1-1-1",
"2-2-2",
),
).toBe("2-2-2");

expect(
findIncompatibleShapeIdIfAny(
wordsForShape2,
wordsForShape1,
"1-1-1",
"2-2-2",
),
).toBe("1-1-1");
});

test("If two shapes share a word, and the shapes are the same length, the ID for the second shape is returned", () => {
const wordsForShape1 = new Set(["CAT", "DOG", "ANIMAL"]);
const wordsForShape2 = new Set(["COW", "HORSE", "DOG"]);

expect(
findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
"1-1-1",
"2-2-2",
),
).toBe("2-2-2");

expect(
findIncompatibleShapeIdIfAny(
wordsForShape2,
wordsForShape1,
"1-1-1",
"2-2-2",
),
).toBe("2-2-2");
});

test("If two shapes share a word with 'S' appended to one, the ID for the shape with the 'S' appended to the word is returned", () => {
const wordsForShape1 = new Set(["CAT", "DOGS"]);
const wordsForShape2 = new Set(["COW", "HORSE", "DOG"]);

expect(
findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
"1-1-1",
"2-2-2",
),
).toBe("1-1-1");

expect(
findIncompatibleShapeIdIfAny(
wordsForShape2,
wordsForShape1,
"1-1-1",
"2-2-2",
),
).toBe("2-2-2");
});

test("If two shapes don't share a word, no ID is returned", () => {
const wordsForShape1 = new Set(["CAT", "DOGS"]);
const wordsForShape2 = new Set(["COW", "HORSE", "DOGGIE"]);

expect(
findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
"1-1-1",
"2-2-2",
),
).toBe(undefined);
});
46 changes: 31 additions & 15 deletions src/logic/omitDuplicateWordsAcrossShapes.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,56 @@
import {indexesToWord} from "./indexesToWord";
import {findIncompatibleShapeIdIfAny} from "./findIncompatibleShapeIdIfAny";

function getWordsForShape(indexListsForShape, letters) {
return new Set(
indexListsForShape.map((wordIndexes) =>
indexesToWord(wordIndexes, letters),
),
);
}

export function omitDuplicateWordsAcrossShapes({shapeLookup, letters}) {
// The same word can be present in multiple shapes,
// but it feels weird to find the same word twice.
// It also feels weird to have the single and plural form of a word.
// This function eliminates those cases.

const allShapeIDs = Object.keys(shapeLookup);
let potentialShapeIDs = new Set(allShapeIDs);

// Compare each shape to each other shape
for (let index1 = 0; index1 < allShapeIDs.length - 1; index1++) {
// Get the ID of the first shape. (The ID is just the indexes of the normalized shape, joined by a hyphen.)
const shapeId1 = allShapeIDs[index1];

// Skip this round if this shape has already been removed
if (!potentialShapeIDs.has(shapeId1)) {
continue;
}
const words1 = new Set(
shapeLookup[shapeId1].map((wordIndexes) =>
indexesToWord(wordIndexes, letters),
),
);

// Get the words that can solve the first shape
const wordsForShape1 = getWordsForShape(shapeLookup[shapeId1], letters);

// Compare this shape to each later shape
for (let index2 = index1 + 1; index2 < allShapeIDs.length; index2++) {
const shapeId2 = allShapeIDs[index2];

// Skip this round if this shape has already been removed
if (!potentialShapeIDs.has(shapeId2)) {
continue;
}
const words2 = new Set(
shapeLookup[shapeId2].map((wordIndexes) =>
indexesToWord(wordIndexes, letters),
),

// Get the words that can solve the second shape
const wordsForShape2 = getWordsForShape(shapeLookup[shapeId2], letters);

const shapeIdToRemove = findIncompatibleShapeIdIfAny(
wordsForShape1,
wordsForShape2,
shapeId1,
shapeId2,
);
// If the shapes share a word, remove the shape that has more words
const uniqueValues = new Set([...words1, ...words2]);
if (uniqueValues.size < words1.size + words2.size) {
words1.size < words2.size
? potentialShapeIDs.delete(shapeId2)
: potentialShapeIDs.delete(shapeId1);
if (shapeIdToRemove) {
potentialShapeIDs.delete(shapeIdToRemove);
}
}
}
Expand Down
Loading

0 comments on commit 797ad37

Please sign in to comment.