Skip to content

Commit

Permalink
Merge pull request #503 from legnes/enum-ref-warnings
Browse files Browse the repository at this point in the history
Add some publish warnings
  • Loading branch information
mdirolf authored Apr 30, 2024
2 parents 309e593 + 59ab349 commit b65cbc4
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 16 deletions.
4 changes: 2 additions & 2 deletions app/components/ClueList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
useRef,
} from 'react';
import { EntryBase, GridBase, valAt } from '../lib/gridBase';
import { Direction, Position } from '../lib/types';
import { Position, directionString } from '../lib/types';
import { CluedEntry } from '../lib/viewableGrid';
import { PuzzleAction } from '../reducers/commonActions';
import { ClickedEntryAction } from '../reducers/gridReducer';
Expand Down Expand Up @@ -88,7 +88,7 @@ const ClueListItem = memo(function ClueListItem({
<div className={styles.label}>
{props.entry.labelNumber}
<span className={styles.direction}>
{props.entry.direction === Direction.Across ? 'A' : 'D'}
{directionString(props.entry.direction)}
</span>
</div>
<div className={styles.clueText}>
Expand Down
4 changes: 2 additions & 2 deletions app/components/ClueMode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { sizeTag } from '../lib/sizeTag';
import { COVER_PIC } from '../lib/style';
import { Timestamp } from '../lib/timestamp';
import {
Direction,
dbCluesToClueTArray,
directionString,
removeClueSpecials,
} from '../lib/types';
import { isMetaSolution } from '../lib/utils';
Expand Down Expand Up @@ -86,7 +86,7 @@ const ClueRow = (props: {
<tr>
<td className={styles.fixedCell}>
{props.entry.labelNumber}
{props.entry.direction === Direction.Down ? 'D' : 'A'}
{directionString(props.entry.direction)}
</td>
<td className={styles.fixedCell}>
<label
Expand Down
4 changes: 2 additions & 2 deletions app/components/ClueReference.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useContext } from 'react';
import { valAt } from '../lib/gridBase';
import { Direction, getClueText } from '../lib/types';
import { Direction, directionString, getClueText } from '../lib/types';
import { DownsOnlyContext } from './DownsOnlyContext';
import { GridContext } from './GridContext';
import { ShowRefsContext } from './ShowRefsContext';
Expand Down Expand Up @@ -36,7 +36,7 @@ export const ClueReference = (props: ClueReferenceProps): JSX.Element => {
<>
<b className="marginRight0-5em whiteSpaceNowrap">
{props.labelNumber}
{props.direction === Direction.Across ? 'A' : 'D'}
{directionString(props.direction)}
</b>
{downsOnly && props.direction === Direction.Across
? '-'
Expand Down
3 changes: 2 additions & 1 deletion app/components/Puzzle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import {
CheatUnit,
Direction,
KeyK,
directionString,
fromKeyString,
fromKeyboardEvent,
getClueText,
Expand Down Expand Up @@ -211,7 +212,7 @@ const AboveTheGridClue = memo(function AboveTheGridClue({
<div className={styles.agcWrap}>
<div className={styles.agcLabel}>
{entry.labelNumber}
{entry.direction === Direction.Across ? 'A' : 'D'}
{directionString(entry.direction)}
</div>
<div data-conceal={shouldConceal} className={styles.agcClue}>
<ClueText entry={entry} hast={hast} />
Expand Down
19 changes: 19 additions & 0 deletions app/lib/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,22 @@ export const parseClueReferences = (text: string): ClueReferenceData[] => {
}
return refs;
};

export function parseClueEnumeration(str: string): string | null {
str = str.trim();
let res = '';
for (let i = str.length - 1, depth = 0; i >= 0; i--) {
const char = str[i];
if (char === ')') {
depth++;
} else if (char === '(') {
depth--;
}
if (depth > 0 || (depth === 0 && char === '(')) {
res = char + res;
} else {
break;
}
}
return /\d/.test(res) || res === '()' ? res : null;
}
4 changes: 4 additions & 0 deletions app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export enum Direction {
Down,
}

export function directionString(dir: Direction): string {
return dir === Direction.Across ? 'A' : 'D';
}

export interface WorkerMessage {
type: string;
}
Expand Down
8 changes: 8 additions & 0 deletions app/lib/viewableGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
PosAndDir,
Position,
Symmetry,
directionString,
isSamePosition,
} from './types';

Expand Down Expand Up @@ -49,6 +50,13 @@ export interface CluedGrid extends ViewableGrid<CluedEntry> {
clues: ClueT[];
}

export function entryString(entry: {
labelNumber: number;
direction: Direction;
}): string {
return `${entry.labelNumber}${directionString(entry.direction)}`;
}

function getSortedEntries<Entry extends EntryBase>(entries: Entry[]) {
return [...entries]
.sort((a, b) => {
Expand Down
60 changes: 52 additions & 8 deletions app/reducers/builderReducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ import {
moveRight,
moveUp,
} from '../lib/viewableGrid';
import { hasSelection, postEdit, validateGrid } from './builderUtils';
import {
getWarningStats,
hasSelection,
postEdit,
validateGrid,
} from './builderUtils';
import { PuzzleAction, isKeypressAction } from './commonActions';
import {
GridInterfaceState,
Expand Down Expand Up @@ -747,13 +752,6 @@ function _builderReducer(
if (!state.gridIsComplete) {
errors.push('All squares in the grid must be filled in');
}
if (state.repeats.size > 0) {
warnings.push(
'Some words are repeated (' +
Array.from(state.repeats).sort().join(', ') +
')'
);
}
if (!state.title) {
errors.push('Puzzle must have a title set');
}
Expand Down Expand Up @@ -795,6 +793,52 @@ function _builderReducer(
);
}

if (state.repeats.size > 0) {
warnings.push(
'Some words are repeated (' +
Array.from(state.repeats).sort().join(', ') +
')'
);
}
const stats = getWarningStats(state);
if (stats.shortWords.size > 0) {
warnings.push(
'Some words are only two letters long (' +
Array.from(stats.shortWords).sort().join(', ') +
')'
);
}
if (stats.hasUnches) {
warnings.push(
'Some letters are unchecked and the puzzle is not tagged as cryptic'
);
}
if (stats.unmatchedRefs.size > 0) {
warnings.push(
`Some clues reference entries that don't exist: (${Array.from(
stats.unmatchedRefs
)
.sort()
.join(', ')})`
);
}
if (stats.missingEnums) {
warnings.push(
`Some clues are missing enumerations: (${Array.from(stats.missingEnums)
.sort()
.join(', ')})`
);
}
if (stats.wrongEnums) {
warnings.push(
`Some clues have enumerations that don't match the answer length: (${Array.from(
stats.wrongEnums
)
.sort()
.join(', ')})`
);
}

if (errors.length) {
return { ...state, publishErrors: errors, publishWarnings: warnings };
}
Expand Down
56 changes: 55 additions & 1 deletion app/reducers/builderUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { entryWord } from '../lib/gridBase';
import { entryWord, hasUnches } from '../lib/gridBase';
import { parseClueEnumeration, parseClueReferences } from '../lib/parse';
import { emptySelection, hasMultipleCells } from '../lib/selection';
import { entryString } from '../lib/viewableGrid';
import type { BuilderState } from './builderReducer';
import type { GridInterfaceState } from './gridReducer';

Expand Down Expand Up @@ -49,6 +51,58 @@ export function validateGrid(state: BuilderState) {
};
}

export function getWarningStats(state: BuilderState) {
const shortWords = new Set<string>();

for (const [i, entry] of state.grid.entries.entries()) {
if (entry.cells.length <= 2) {
shortWords.add(entryWord(state.grid, i));
}
}

const unmatchedRefs = new Set<string>();
const missingEnums = new Set<string>();
const wrongEnums = new Set<string>();

const digitsRegex = /(\d+)/g;

const gridEntries = new Set<string>();
state.grid.entries.forEach((entry) => {
gridEntries.add(entryString(entry));
});

for (const [word, clues] of Object.entries(state.clues)) {
for (const clue of clues) {
const refs = parseClueReferences(clue);
if (!refs.every((ref) => gridEntries.has(entryString(ref)))) {
unmatchedRefs.add(word);
}

const clueEnum = parseClueEnumeration(clue);
if (clueEnum == null) {
missingEnums.add(word);
} else {
const length = clueEnum.match(digitsRegex)?.reduce((sum, str) => {
const val = +str;
return isNaN(val) ? sum : sum + val;
}, 0);
if (length == null || length !== word.length) {
wrongEnums.add(word);
}
}
}
}
const enumsExpected = missingEnums.size < Object.keys(state.clues).length / 2;

return {
shortWords,
hasUnches: hasUnches(state.grid) && !state.userTags.includes('cryptic'),
unmatchedRefs,
missingEnums: enumsExpected ? missingEnums : null,
wrongEnums: enumsExpected ? wrongEnums : null,
};
}

export function hasSelection<T extends GridInterfaceState>(state: T): boolean {
return isBuilderState(state) && hasMultipleCells(state.selection);
}
Expand Down

0 comments on commit b65cbc4

Please sign in to comment.