From 1144cc6e68e4f05c9461e32ef29d991a6b961a09 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Tue, 9 Mar 2021 20:05:43 -0500 Subject: [PATCH 01/35] initial refactor renameData function - replace if-else chain with switch statement - wrap lodash._mapKeys in function - add empty function as placeholder for shorthand renaming - apply vs code "format document" - add "shorthand" option to naming types in settings --- src/js/pages/Settings.jsx | 1 + src/js/utils/index.js | 59 +++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/js/pages/Settings.jsx b/src/js/pages/Settings.jsx index d39a465..2edb78d 100644 --- a/src/js/pages/Settings.jsx +++ b/src/js/pages/Settings.jsx @@ -71,6 +71,7 @@ const Settings = ({ activeGame, setActiveGame, dataDisplaySettings, setDataDispl > Official Common + Shorthand Inputs diff --git a/src/js/utils/index.js b/src/js/utils/index.js index 667278d..3533d35 100644 --- a/src/js/utils/index.js +++ b/src/js/utils/index.js @@ -1,22 +1,33 @@ import { mapKeys, isEqual } from 'lodash'; +/** + * Renames the moves in the character frame data to reflect the user's desired naming convention + * @param {string} rawFrameData The frame data for the current character + * @param {string} moveNameType The naming convention + * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed + * @returns The frame data JSON object with renamed moves + */ export function renameData(rawFrameData, moveNameType, inputNotationType) { - - let renameKey = ""; - if (moveNameType === "official") { - renameKey = "moveName" - } else if (moveNameType === "common") { - renameKey = "cmnName"; - } else if (moveNameType === "inputs" && inputNotationType) { - renameKey = inputNotationType; + const bulkRenameFrameData = (rawData, renameKey) => { + return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); } - const renamedFrameData = mapKeys(rawFrameData, - (moveData, moveKey) => - moveData[renameKey] ? moveData[renameKey] : moveKey - ); + const shorthandRename = (rawData) => { + + } - return renamedFrameData; + switch (moveNameType) { + case "official": + return bulkRenameFrameData(rawFrameData, "moveName"); + case "common": + return bulkRenameFrameData(rawFrameData, "cmnName"); + case "shorthand": + return shorthandRename(rawFrameData); + case "inputs": + return bulkRenameFrameData(rawFrameData, inputNotationType); + default: + break; + } } @@ -27,18 +38,18 @@ function vTriggerMerge(rawFrameData, vtState) { const vtMergedData = { ...rawFrameData.normal, ...rawFrameData[vtState] } - + Object.keys(rawFrameData[vtState]).forEach(vtMove => { - let changedValues = []; - Object.keys(rawFrameData[vtState][vtMove]).forEach(detail => { - if (!rawFrameData.normal[vtMove]) { - vtMergedData[vtMove]["uniqueInVt"] = true; - } else if (rawFrameData.normal[vtMove] && !isEqual(rawFrameData.normal[vtMove][detail], rawFrameData[vtState][vtMove][detail])) { - changedValues = [ ...changedValues, detail ] - } - }) - vtMergedData[vtMove] = { ...vtMergedData[vtMove], changedValues } + let changedValues = []; + Object.keys(rawFrameData[vtState][vtMove]).forEach(detail => { + if (!rawFrameData.normal[vtMove]) { + vtMergedData[vtMove]["uniqueInVt"] = true; + } else if (rawFrameData.normal[vtMove] && !isEqual(rawFrameData.normal[vtMove][detail], rawFrameData[vtState][vtMove][detail])) { + changedValues = [...changedValues, detail] } + }) + vtMergedData[vtMove] = { ...vtMergedData[vtMove], changedValues } + } ) // based on https://stackoverflow.com/a/39442287 @@ -47,7 +58,7 @@ function vTriggerMerge(rawFrameData, vtState) { .sort(function (a, b) { return a[1].i - b[1].i }) - .reduce((_sortedObj, [k,v]) => ({ + .reduce((_sortedObj, [k, v]) => ({ ..._sortedObj, [k]: v }), {}) From 6590a22373c51d7179e7a7bc6a8e9537883bf2b5 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 10 Mar 2021 11:27:49 -0500 Subject: [PATCH 02/35] mostly pseudocode for move name formatting --- src/js/utils/index.js | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/js/utils/index.js b/src/js/utils/index.js index 3533d35..44be58f 100644 --- a/src/js/utils/index.js +++ b/src/js/utils/index.js @@ -1,4 +1,4 @@ -import { mapKeys, isEqual } from 'lodash'; +import { mapKeys, isEqual, has } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -9,11 +9,50 @@ import { mapKeys, isEqual } from 'lodash'; */ export function renameData(rawFrameData, moveNameType, inputNotationType) { const bulkRenameFrameData = (rawData, renameKey) => { - return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + let debugReturn = mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + + return debugReturn; + } + + const formatMoveName = (moveData) => { + + let splitMoveName = moveData.plnCmd.split(/[\\s+]+/); + + /* + split the plain command input into direction, button, and if it's present, aerial state + (the regex supplied will safely split the plain command if it's just a grounded direction and input) + if the length of the resulting array is greater than 2, we're dealing with an airborne move + return "j" for jump prepended to [direction].[input] + else + return [direction].[input] + */ + } const shorthandRename = (rawData) => { + // let foo = rawData; + + // for (const fdKey of Object.keys(foo)) { + // console.log(`Key: ${fdKey} ---- Value: ${foo[fdKey].moveType}`); + + // if (foo[fdKey].moveType === "normal") { + + // } + // } + + // return foo; + + let rename = mapKeys(rawData, (moveValue, moveKey) => { + if (moveValue.moveType === "normal") { + if (has(moveValue, "cmnName")) { + return moveValue.cmnName; + } else { + return moveKey; + } + } + }); + return rename; } switch (moveNameType) { From 5e73fd53934115e029b708599f293b96d4fff86e Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Thu, 11 Mar 2021 16:01:36 -0500 Subject: [PATCH 03/35] finish initial impl. of shorthand rename func --- src/js/utils/index.ts | 61 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 44be58f..c781925 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,4 +1,4 @@ -import { mapKeys, isEqual, has } from 'lodash'; +import { mapKeys, isEqual } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -14,54 +14,43 @@ export function renameData(rawFrameData, moveNameType, inputNotationType) { return debugReturn; } - const formatMoveName = (moveData) => { - - let splitMoveName = moveData.plnCmd.split(/[\\s+]+/); - - /* - split the plain command input into direction, button, and if it's present, aerial state - (the regex supplied will safely split the plain command if it's just a grounded direction and input) - if the length of the resulting array is greater than 2, we're dealing with an airborne move - return "j" for jump prepended to [direction].[input] - else - return [direction].[input] - */ - - } - - const shorthandRename = (rawData) => { - // let foo = rawData; - - // for (const fdKey of Object.keys(foo)) { - // console.log(`Key: ${fdKey} ---- Value: ${foo[fdKey].moveType}`); - - // if (foo[fdKey].moveType === "normal") { - - // } - // } - - // return foo; - + const renameFrameDataToShorthand = (rawData) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { - if (moveValue.moveType === "normal") { - if (has(moveValue, "cmnName")) { - return moveValue.cmnName; - } else { - return moveKey; - } + if (moveValue.moveType === "normal" && !moveValue.movesList) { + return formatMoveName(moveValue); + } else { + return moveKey; } }); return rename; } + const formatMoveName = (moveData) => { + let truncatedMoveName: string = ""; + const numberToDirectionMap: Map = new Map([ + ["2", "cr."], + ["5", "st."], + ["8", "nj."] + ]); + + if (moveData.numCmd.includes("j")) { + truncatedMoveName = moveData.numCmd; + } else { + let [direction, input]: string[] = moveData.numCmd.split(/(\d)/).filter((x: string) => x !== ""); + truncatedMoveName = `${numberToDirectionMap.get(direction)}${input}`; + } + + return truncatedMoveName; + } + switch (moveNameType) { case "official": return bulkRenameFrameData(rawFrameData, "moveName"); case "common": return bulkRenameFrameData(rawFrameData, "cmnName"); case "shorthand": - return shorthandRename(rawFrameData); + return renameFrameDataToShorthand(rawFrameData); case "inputs": return bulkRenameFrameData(rawFrameData, inputNotationType); default: From e471cd34fb07156ccf2078788bb0b82aef7c743c Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 12:49:07 -0500 Subject: [PATCH 04/35] renameData function signature initial changes d4rk and i discussed the actual implementation of the updated function into the app and his vision was to have an extra entirely separate setting for longform and shorthand notation, so i've adjusted the function a bit to prepare for him adding a redux store for that setting and some various other changes to support it --- src/js/utils/index.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index e8e317a..a83836a 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -5,13 +5,19 @@ import { mapKeys, isEqual } from 'lodash'; * @param {string} rawFrameData The frame data for the current character * @param {string} moveNameType The naming convention * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed + * @param {string} notationDisplayType The move name size convention ("longform" & "shorthand") * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, moveNameType, inputNotationType) { - const bulkRenameFrameData = (rawData, renameKey) => { - let debugReturn = mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); - - return debugReturn; +export function renameData(rawFrameData, moveNameType, inputNotationType, notationDisplayType) { + const renameFrameData = (rawData, renameKey, notationDisplay) => { + switch (notationDisplay) { + case "longform": + return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + case "shorthand": + return renameFrameDataToShorthand(rawData); + default: + break; + } } const renameFrameDataToShorthand = (rawData) => { @@ -46,13 +52,11 @@ export function renameData(rawFrameData, moveNameType, inputNotationType) { switch (moveNameType) { case "official": - return bulkRenameFrameData(rawFrameData, "moveName"); + return renameFrameData(rawFrameData, "moveName", notationDisplayType); case "common": - return bulkRenameFrameData(rawFrameData, "cmnName"); - case "shorthand": - return renameFrameDataToShorthand(rawFrameData); + return renameFrameData(rawFrameData, "cmnName", notationDisplayType); case "inputs": - return bulkRenameFrameData(rawFrameData, inputNotationType); + return renameFrameData(rawFrameData, inputNotationType, notationDisplayType); default: break; } From 6474d91ec2706c4cd35c7fb5e88a19e78842b5a9 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 14:23:25 -0500 Subject: [PATCH 05/35] finish implementation of abbreviation func - helpCreateFrameDataJSON now takes in the display state as a parameter - clarification: renamed stateToSet -> vTriggerStateToSet - removed select option I added to 'Move Name Type' dropdown in settings, was using for testing and I swept it up into the commit - uncommented d4rk's notation widget for shorthand/longform naming - renameData func now accepts the display state as an arg, updated interior of function to leverage it - updated function docstring for renameData to reflect changes --- src/js/actions/index.ts | 6 +++--- src/js/pages/Settings.tsx | 7 ++----- src/js/utils/index.ts | 27 +++++++++++---------------- 3 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/js/actions/index.ts b/src/js/actions/index.ts index 94ec927..8796034 100644 --- a/src/js/actions/index.ts +++ b/src/js/actions/index.ts @@ -90,15 +90,15 @@ export const setAutoSetSpecificCols = (autoSetColsOn: Boolean) => ({ export const setPlayer = (playerId: PlayerId, charName: PlayerData["name"]) => { return function(dispatch, getState) { const { frameDataState, dataDisplaySettingsState, selectedCharactersState, activeGameState }: RootState = getState(); - const stateToSet: VtState = + const vTriggerStateToSet: VtState = activeGameState !== "SFV" ? "normal" : selectedCharactersState[playerId].vtState const playerData: PlayerData = { name: charName, - frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState.moveNameType, dataDisplaySettingsState.inputNotationType, dataDisplaySettingsState.normalNotationType, stateToSet), + frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState, vTriggerStateToSet), stats: frameDataState[charName].stats, - vtState: stateToSet, + vtState: vTriggerStateToSet, } dispatch({ type: 'SET_PLAYER', diff --git a/src/js/pages/Settings.tsx b/src/js/pages/Settings.tsx index 4e5ada3..d9af38f 100644 --- a/src/js/pages/Settings.tsx +++ b/src/js/pages/Settings.tsx @@ -81,7 +81,6 @@ const Settings = () => { > Official Common - Shorthand Inputs @@ -109,8 +108,7 @@ const Settings = () => { - {/* @Jon Uncomment this! */} - {/* +

Normal Notation

Choose long or short normal names

@@ -131,8 +129,7 @@ const Settings = () => { Full Word Shorthand -
*/} - +
{/* APP OPTIONS */} App Settings diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 9ae697f..4245579 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -5,15 +5,13 @@ import { VtState } from '../types'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention * @param {string} rawFrameData The frame data for the current character - * @param {string} moveNameType The naming convention - * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed - * @param {string} notationDisplayType The move name size convention ("longform" & "shorthand") + * @param {DataDisplaySettingsReducerState} displayState The Redux state containing various move text render settings * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, moveNameType, inputNotationType, notationDisplayType) { +export function renameData(rawFrameData, displayState: DataDisplaySettingsReducerState) { const renameFrameData = (rawData, renameKey, notationDisplay) => { switch (notationDisplay) { - case "longform": + case "fullWord": return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); case "shorthand": return renameFrameDataToShorthand(rawData); @@ -52,15 +50,15 @@ export function renameData(rawFrameData, moveNameType, inputNotationType, notati return truncatedMoveName; } - switch (moveNameType) { + switch (displayState.moveNameType) { case "official": - return renameFrameData(rawFrameData, "moveName", notationDisplayType); + return renameFrameData(rawFrameData, "moveName", displayState.normalNotationType); case "common": - return renameFrameData(rawFrameData, "cmnName", notationDisplayType); + return renameFrameData(rawFrameData, "cmnName", displayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, inputNotationType, notationDisplayType); + return renameFrameData(rawFrameData, displayState.inputNotationType, displayState.normalNotationType); default: - break; + return rawFrameData; } } @@ -102,12 +100,9 @@ function vTriggerMerge(rawFrameData, vtState) { } // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet -export function helpCreateFrameDataJSON(rawFrameData, moveNameType, inputNotationType, normalNotationType, vtState) { +export function helpCreateFrameDataJSON(rawFrameData, displayState: DataDisplaySettingsReducerState, vtState: VtState) { - const dataToRename = vtState === "normal" - ? rawFrameData.normal - : vTriggerMerge(rawFrameData, vtState); - - return moveNameType === "official" ? dataToRename : renameData(dataToRename, moveNameType, inputNotationType); + const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); + return (displayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, displayState); } \ No newline at end of file From 018a2aad1b38ba2124e64aa7a2fdc5a2e8d9fc3c Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 18:13:03 -0500 Subject: [PATCH 06/35] change variable for clarity based on feedback - 'change displayState' to 'dataDisplayState' --- src/js/utils/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 4245579..578c337 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -5,10 +5,10 @@ import { VtState } from '../types'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention * @param {string} rawFrameData The frame data for the current character - * @param {DataDisplaySettingsReducerState} displayState The Redux state containing various move text render settings + * @param {DataDisplaySettingsReducerState} dataDisplayState The Redux state containing various move text render settings * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, displayState: DataDisplaySettingsReducerState) { +export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState) { const renameFrameData = (rawData, renameKey, notationDisplay) => { switch (notationDisplay) { case "fullWord": @@ -50,13 +50,13 @@ export function renameData(rawFrameData, displayState: DataDisplaySettingsReduce return truncatedMoveName; } - switch (displayState.moveNameType) { + switch (dataDisplayState.moveNameType) { case "official": - return renameFrameData(rawFrameData, "moveName", displayState.normalNotationType); + return renameFrameData(rawFrameData, "moveName", dataDisplayState.normalNotationType); case "common": - return renameFrameData(rawFrameData, "cmnName", displayState.normalNotationType); + return renameFrameData(rawFrameData, "cmnName", dataDisplayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, displayState.inputNotationType, displayState.normalNotationType); + return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, dataDisplayState.normalNotationType); default: return rawFrameData; } @@ -100,9 +100,9 @@ function vTriggerMerge(rawFrameData, vtState) { } // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet -export function helpCreateFrameDataJSON(rawFrameData, displayState: DataDisplaySettingsReducerState, vtState: VtState) { +export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState) { const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); - return (displayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, displayState); + return (dataDisplayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, dataDisplayState); } \ No newline at end of file From 46dfd130cb74940656197eda8bfe3a44f687ff8f Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 17 Mar 2021 14:24:47 -0400 Subject: [PATCH 07/35] apply PR feedback - ensured that input name types won't be converted to shorthand - remove ternary operator in return statement in helpCreateFrameDataJSON --- src/js/utils/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 578c337..754b54c 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -56,7 +56,7 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe case "common": return renameFrameData(rawFrameData, "cmnName", dataDisplayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, dataDisplayState.normalNotationType); + return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, "fullWord"); default: return rawFrameData; } @@ -104,5 +104,5 @@ export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisp const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); - return (dataDisplayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, dataDisplayState); + return renameData(dataToRename, dataDisplayState); } \ No newline at end of file From 310b69b604fb982a9d6507bcaab5f9c841f30246 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Fri, 19 Mar 2021 16:30:50 -0400 Subject: [PATCH 08/35] PR changes: - fix bug where common inputs with shorthand were leaving specials "untranslated" to common input - refactor formatMoveName to work with moveName property instead of numCmd property due to consistency changes in the way numeric inputs were represented --- src/js/utils/index.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 754b54c..0b9bb11 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -14,18 +14,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe case "fullWord": return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); case "shorthand": - return renameFrameDataToShorthand(rawData); + return renameFrameDataToShorthand(rawData, renameKey); default: break; } } - const renameFrameDataToShorthand = (rawData) => { + const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { if (moveValue.moveType === "normal" && !moveValue.movesList) { return formatMoveName(moveValue); } else { - return moveKey; + return moveValue[nameTypeKey]; } }); @@ -34,18 +34,17 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const formatMoveName = (moveData) => { let truncatedMoveName: string = ""; - const numberToDirectionMap: Map = new Map([ - ["2", "cr."], - ["5", "st."], - ["8", "nj."] + const wordToAbbreviationMap: Map = new Map([ + ["stand", "st."], + ["crouch", "cr."], + ["jump", "j."], + ["neutral", "nj."] ]); - if (moveData.numCmd.includes("j")) { - truncatedMoveName = moveData.numCmd; - } else { - let [direction, input]: string[] = moveData.numCmd.split(/(\d)/).filter((x: string) => x !== ""); - truncatedMoveName = `${numberToDirectionMap.get(direction)}${input}`; - } + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; return truncatedMoveName; } @@ -101,7 +100,7 @@ function vTriggerMerge(rawFrameData, vtState) { // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState) { - + const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); return renameData(dataToRename, dataDisplayState); From 404d48237622816ab3aadd97d022f8413538ca52 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 20 Mar 2021 13:52:44 -0400 Subject: [PATCH 09/35] start handling moves with modifying parentheses --- src/js/utils/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 0b9bb11..0ab8d5a 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -41,10 +41,23 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; + if (!moveData.moveName.includes('(')) { + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; + } else { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ + let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + } return truncatedMoveName; } From ecf50f2f12e3bdeb21efee6a273a9bb8213991a3 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Tue, 4 May 2021 11:13:39 -0400 Subject: [PATCH 10/35] rough work for implementing cross-game shorthand --- src/js/utils/index.ts | 63 ++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 0ab8d5a..ac0557f 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -22,7 +22,7 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { - if (moveValue.moveType === "normal" && !moveValue.movesList) { + if ((moveValue.moveType === "normal" || moveValue.moveType === "held normal") && !moveValue.movesList) { return formatMoveName(moveValue); } else { return moveValue[nameTypeKey]; @@ -32,6 +32,24 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return rename; } + const skipFormattingMove = (moveData) => { + const TARGET_COMBO: string = "(TC)"; + const COMMAND_NORMAL: string[] = ["b", "f", ">", "(air)"]; + const MOVE_NAME: string = moveData.moveName; + + if (MOVE_NAME.includes(TARGET_COMBO)) { + return true; + } + + // Other languages have a cleaner way of representing this: if any of the values in the + // array are in the string, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + return true; + } + + return false; + } + const formatMoveName = (moveData) => { let truncatedMoveName: string = ""; const wordToAbbreviationMap: Map = new Map([ @@ -41,24 +59,31 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); - if (!moveData.moveName.includes('(')) { - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; - } else { - /* - Regex documentation: - Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result - The outermost parentheses start the capture group of characters we DO want to capture - The character combo of \( means that we want to find an actual opening parenthesis - [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" - Then we want to find the closing parenthesis with \) - The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression - */ - let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); - } - + if (!skipFormattingMove(moveData)) { + if (!moveData.moveName.includes('(')) { + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; + } else { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ + let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); + let modifierParens: string[] = splitMoveFromExtraParens.slice(1); + let abbr: string = wordToAbbreviationMap.get(splitMove[0].toLowerCase()); + let input: string = splitMove[splitMove.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; + } + } + return truncatedMoveName; } From 47abae5e9ad9596090eee706dc3cf37d0cf720a2 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 26 May 2021 23:18:03 -0400 Subject: [PATCH 11/35] begin refactoring code to address changes in data - movesList will now also contain the type of normal it is, i.e., this will hold 'held normal' if it's normal that changes when the button is held - applying some logic changes to skipFormattingMove to function in line with the changes to the frame data - menat was breaking the implementation, i had to create a function for encapsulating the input extraction --- src/js/utils/index.ts | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index ac0557f..720e038 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -21,9 +21,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe } const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { + const HELD_NORMAL: string = "Held Normal"; let rename = mapKeys(rawData, (moveValue, moveKey) => { - if ((moveValue.moveType === "normal" || moveValue.moveType === "held normal") && !moveValue.movesList) { - return formatMoveName(moveValue); + if ((moveValue.moveType === "normal")) { + if (moveValue.movesList) { + if (moveValue.movesList === HELD_NORMAL) { + return formatMoveName(moveValue); + } else { + return moveValue[nameTypeKey]; + } + } else { + return formatMoveName(moveValue); + } } else { return moveValue[nameTypeKey]; } @@ -33,25 +42,32 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe } const skipFormattingMove = (moveData) => { - const TARGET_COMBO: string = "(TC)"; - const COMMAND_NORMAL: string[] = ["b", "f", ">", "(air)"]; + const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; + const COMMAND_NORMAL: string[] = ["4", "6"]; + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)"] const MOVE_NAME: string = moveData.moveName; - if (MOVE_NAME.includes(TARGET_COMBO)) { + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } // Other languages have a cleaner way of representing this: if any of the values in the - // array are in the string, just return the move name since it's a command normal - if (COMMAND_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + // designated array is in the numCmd, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { + return true; + } + + // If the above check doesn't find anything, check for some other common indicators; if + // nothing comes back here, we're good and don't need to skip formatting + if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { return true; } return false; } - const formatMoveName = (moveData) => { - let truncatedMoveName: string = ""; + const formatMoveName = (moveData) => { + const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], @@ -59,12 +75,36 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); + let truncatedMoveName: string = ""; + + const extractInput = (splitMove: string[]) => { + let input: string[] = []; + + // Menat orb needs special attention + if (splitMove.some(x => x.toLocaleLowerCase().includes("orb"))) { + let orb: string = splitMove.find(x => x === "orb"); + let button: string = splitMove.find(x => strengths.some(y => y === x)); + + input.push(button.toUpperCase()); + input.push(orb); + } else { + input.push(splitMove[splitMove.length - 1].toUpperCase()); + } + + return input; + } + if (!skipFormattingMove(moveData)) { if (!moveData.moveName.includes('(')) { let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; + let input: string[] = extractInput(splitMoveName); + + if (input.length > 1) { + truncatedMoveName = `${abbr}${input[0]} ${input[1]}`; + } else { + truncatedMoveName = `${abbr}${input[0]}`; + } } else { /* Regex documentation: @@ -82,8 +122,8 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe let input: string = splitMove[splitMove.length - 1].toUpperCase(); truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; } - } - + } + return truncatedMoveName; } From ae485891d715182f65477559e832bcb537e0d546 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sun, 6 Jun 2021 00:18:17 -0400 Subject: [PATCH 12/35] hopefully last stages of shorthand for sf5 - implement 'default to official name' logic for shorthand rename - add edge case catching for Rashid to skipFormattingMove - add edge case catching for Young Zeku and Menat to formatMoveName --- src/js/utils/index.ts | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 720e038..fe354a4 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -23,18 +23,31 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { const HELD_NORMAL: string = "Held Normal"; let rename = mapKeys(rawData, (moveValue, moveKey) => { + const DEFAULT_MOVE_NAME = () => { + if (moveValue[nameTypeKey]) { + return moveValue[nameTypeKey]; + } else { + return moveKey; + } + } + if ((moveValue.moveType === "normal")) { if (moveValue.movesList) { if (moveValue.movesList === HELD_NORMAL) { return formatMoveName(moveValue); } else { - return moveValue[nameTypeKey]; + return DEFAULT_MOVE_NAME(); } } else { - return formatMoveName(moveValue); + let formatted: string = formatMoveName(moveValue); + if (formatted !== "") { + return formatted; + } else { + return DEFAULT_MOVE_NAME(); + } } } else { - return moveValue[nameTypeKey]; + return DEFAULT_MOVE_NAME(); } }); @@ -44,7 +57,8 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const skipFormattingMove = (moveData) => { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; const COMMAND_NORMAL: string[] = ["4", "6"]; - const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)"] + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; + const RASHID_WIND: string = "(wind)"; const MOVE_NAME: string = moveData.moveName; if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { @@ -63,11 +77,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return true; } + // Rashid should be the only one (for now) to trigger this condition for his mixers + if (MOVE_NAME.includes(RASHID_WIND)) { + return true; + } + return false; } const formatMoveName = (moveData) => { const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + const Y_ZEKU_LATE_HIT: string = "late"; + const MENAT_ORB: string = "orb"; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], @@ -81,12 +102,20 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe let input: string[] = []; // Menat orb needs special attention - if (splitMove.some(x => x.toLocaleLowerCase().includes("orb"))) { + if (splitMove.some(x => x.toLocaleLowerCase().includes(MENAT_ORB))) { let orb: string = splitMove.find(x => x === "orb"); let button: string = splitMove.find(x => strengths.some(y => y === x)); input.push(button.toUpperCase()); input.push(orb); + // Young Zeku's crouch HK also need special attention since it has a "late hit" + // state that gives it different properties and is listed as such in FAT. + } else if (splitMove.some(x => x.toLocaleLowerCase().includes(Y_ZEKU_LATE_HIT))) { + // Index 3 contains "late", index 4 contains "hit" + let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; + + input.push(splitMove[1].toUpperCase()); + input.push(lateHit); } else { input.push(splitMove[splitMove.length - 1].toUpperCase()); } From 50ad3d5bc7392a60f675fdf7bd89aa55630521ca Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 24 Jul 2021 21:46:03 -0400 Subject: [PATCH 13/35] Additional changes to handle SF4 edge cases --- src/js/utils/index.ts | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index fe354a4..3925e82 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -56,11 +56,17 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const skipFormattingMove = (moveData) => { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; - const COMMAND_NORMAL: string[] = ["4", "6"]; + const COMMAND_NORMAL: string[] = ["3", "4", "6"]; const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; const RASHID_WIND: string = "(wind)"; const MOVE_NAME: string = moveData.moveName; + if (!moveData.numCmd) { + if (!moveData.plnCmd) { + return true; + } + } + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } @@ -89,28 +95,44 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; const Y_ZEKU_LATE_HIT: string = "late"; const MENAT_ORB: string = "orb"; + const USF4_CODY_KNIFE_NORMAL: string = "knife"; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], ["jump", "j."], - ["neutral", "nj."] + ["neutral", "nj."], + ["close", "c."], + ["far", "f."], + ["downback", "db."], + ["back", "b."] ]); let truncatedMoveName: string = ""; const extractInput = (splitMove: string[]) => { - let input: string[] = []; + let input: string[] = []; + let uniqueAspect: string = ""; + let isMenatOrbNormal: boolean = splitMove.some(x => x.toLowerCase().includes(MENAT_ORB)); + let isUSF4CodyKnifeNormal: boolean = splitMove.some(x => x.toLowerCase().includes(USF4_CODY_KNIFE_NORMAL)); - // Menat orb needs special attention - if (splitMove.some(x => x.toLocaleLowerCase().includes(MENAT_ORB))) { - let orb: string = splitMove.find(x => x === "orb"); + // Menat orb and USF4 Cody with knife need special attention + if (isMenatOrbNormal || isUSF4CodyKnifeNormal) { + + if (isMenatOrbNormal) { + uniqueAspect = MENAT_ORB.charAt(0).toUpperCase() + MENAT_ORB.slice(1); + } + + if (isUSF4CodyKnifeNormal) { + uniqueAspect = USF4_CODY_KNIFE_NORMAL.charAt(0).toUpperCase() + USF4_CODY_KNIFE_NORMAL.slice(1); + } + let button: string = splitMove.find(x => strengths.some(y => y === x)); input.push(button.toUpperCase()); - input.push(orb); + input.push(uniqueAspect); // Young Zeku's crouch HK also need special attention since it has a "late hit" // state that gives it different properties and is listed as such in FAT. - } else if (splitMove.some(x => x.toLocaleLowerCase().includes(Y_ZEKU_LATE_HIT))) { + } else if (splitMove.some(x => x.toLowerCase().includes(Y_ZEKU_LATE_HIT))) { // Index 3 contains "late", index 4 contains "hit" let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; From dd7d954d8d88741136b936fb1fdcdc9942eae259 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 16 Oct 2021 15:32:37 -0400 Subject: [PATCH 14/35] begin development of rules engine - logic in index.ts is getting too dense, filled with edge cases - created MoveFormatter class to hold all rules and perform shorthand formatting per rule - created BaseFormatRule class and example derived class --- src/js/utils/MoveFormatter.ts | 26 +++++++++ src/js/utils/format_rules/BaseFormatRule.ts | 58 +++++++++++++++++++ .../utils/format_rules/MenatSF5FormatRule.ts | 19 ++++++ 3 files changed, 103 insertions(+) create mode 100644 src/js/utils/MoveFormatter.ts create mode 100644 src/js/utils/format_rules/BaseFormatRule.ts create mode 100644 src/js/utils/format_rules/MenatSF5FormatRule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts new file mode 100644 index 0000000..c4b9b61 --- /dev/null +++ b/src/js/utils/MoveFormatter.ts @@ -0,0 +1,26 @@ +import BaseFormatRule from "./format_rules/BaseFormatRule"; +import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; + +export default class MoveFormatter { + rules: BaseFormatRule[]; + + constructor() { + this.rules = [ + new MenatSF5FormatRule() + ]; + } + + formatToShorthand(move: string): string { + let shorthand: string = ""; + + this.rules.forEach(rule => { + shorthand = rule.formatMove(move); + + if (shorthand !== "") { + return shorthand; + } + }); + + return null; + } +} \ No newline at end of file diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts new file mode 100644 index 0000000..ae7bcca --- /dev/null +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -0,0 +1,58 @@ +export default abstract class BaseFormatRule { + characterMoveRule: string; + strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + stanceToAbbreviationMap: Map = new Map([ + ["stand", "st."], + ["crouch", "cr."], + ["jump", "j."], + ["neutral", "nj."], + ["close", "c."], + ["far", "f."], + ["downback", "db."], + ["back", "b."] + ]); + + constructor(rule: string) { + this.characterMoveRule = rule; + } + + formatMove(move: string): string { + let moveName: string = ""; + + if (!move.includes(this.characterMoveRule)) { + return moveName; + } + + if (move.includes('(')) { + return this.formatMoveWithParenthesis(move); + } + + let stanceAbbreviation: string = this.getStanceToAbbreviation(move); + let moveInput: string[] = this.extractInput(move); + + if (moveInput.length > 1) { + moveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; + } else { + moveName = `${stanceAbbreviation}${moveInput[0]}`; + } + + return moveName; + } + + private getStanceToAbbreviation(move: string): string { + let stance = move.toLowerCase().split(' ')[0]; + return this.stanceToAbbreviationMap.get(stance); + } + + private formatMoveWithParenthesis(move: string) { + let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); + let modifierParens: string[] = splitMoveFromExtraParens.slice(1); + let stanceAbbreviation: string = this.stanceToAbbreviationMap.get(splitMove[0].toLowerCase()); + let input: string = splitMove[splitMove.length - 1].toUpperCase(); + + return `${stanceAbbreviation}${input} ${modifierParens.join(' ')}`; + } + + protected abstract extractInput(move: string): string[]; +} \ No newline at end of file diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts new file mode 100644 index 0000000..5d85698 --- /dev/null +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -0,0 +1,19 @@ +import BaseFormatRule from "./BaseFormatRule"; + +export default class MenatSF5FormatRule extends BaseFormatRule { + private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); + + constructor() { + super("orb"); + } + + protected extractInput(move: string): string[] { + let input: string[]; + let moveInput: string = move.split(' ').find(input => this.strengths.some(inputStr => inputStr === input)); + + input.push(moveInput.toUpperCase()); + input.push(this.orbLabel); + + return input; + } +} \ No newline at end of file From 79b84f728fb19a173c68f2622bf23f3657106893 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sun, 17 Oct 2021 12:54:52 -0400 Subject: [PATCH 15/35] add rule for young zeku, refine formatter - added the rule for young zeku - update logic for breaking out of format function in base class - add rule to MoveFormatter --- src/js/utils/MoveFormatter.ts | 4 ++- src/js/utils/format_rules/BaseFormatRule.ts | 32 ++++++++++++++++++- .../format_rules/YoungZekuSF5FormatRule.ts | 19 +++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/js/utils/format_rules/YoungZekuSF5FormatRule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index c4b9b61..28c3b0f 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -1,12 +1,14 @@ import BaseFormatRule from "./format_rules/BaseFormatRule"; import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; +import YoungZekuSF5FormatRule from "./format_rules/YoungZekuSF5FormatRule"; export default class MoveFormatter { rules: BaseFormatRule[]; constructor() { this.rules = [ - new MenatSF5FormatRule() + new MenatSF5FormatRule(), + new YoungZekuSF5FormatRule() ]; } diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index ae7bcca..98d468c 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -16,13 +16,23 @@ export default abstract class BaseFormatRule { this.characterMoveRule = rule; } + /** + * Given a move in its full-length form (i.e., Stand HP), returns the + * move in the common shorthand form of "abbreviated stance.abbreviated input", + * (i.e., st.HP) + * @param move The text representing the move as a string + * @returns A string containing the abbreviated move + */ formatMove(move: string): string { let moveName: string = ""; - if (!move.includes(this.characterMoveRule)) { + // If the move doesn't match the rule, we should break out of the method + // so the next rule can take over + if (!move.toLowerCase().includes(this.characterMoveRule.toLowerCase())) { return moveName; } + // If the move contains something like (Hold), use the regex format method if (move.includes('(')) { return this.formatMoveWithParenthesis(move); } @@ -39,11 +49,25 @@ export default abstract class BaseFormatRule { return moveName; } + /** + * This method abstracts getting the stance part of the move + * and retrieving its abbreviation from the abbreviation map + * @param move The move provided to the call to formatMove + * @returns The stance of the move in its abbreviated form + */ private getStanceToAbbreviation(move: string): string { let stance = move.toLowerCase().split(' ')[0]; return this.stanceToAbbreviationMap.get(stance); } + /** + * Given a move in its full-length form but with a trailing word + * surrounded by parenthesis (i.e., Stand HP (Hold)), returns the + * move in the common shorthand form of "abbr stance.abbr input (parenContent)" + * i.e., st.HP (Hold) + * @param move The move provided to the call to formatMove + * @returns A string containing the abbreviated move + */ private formatMoveWithParenthesis(move: string) { let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); @@ -54,5 +78,11 @@ export default abstract class BaseFormatRule { return `${stanceAbbreviation}${input} ${modifierParens.join(' ')}`; } + /** + * Given a move, extract the input from it. This method's logic will vary + * for some characters, but the default case simply retrieves the currently + * used input from the end of the string. + * @param move The move provided to the call to formatMove + */ protected abstract extractInput(move: string): string[]; } \ No newline at end of file diff --git a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts new file mode 100644 index 0000000..a2263f1 --- /dev/null +++ b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts @@ -0,0 +1,19 @@ +import BaseFormatRule from "./BaseFormatRule"; + +export default class YoungZekuSF5FormatRule extends BaseFormatRule { + constructor() { + super("late"); + } + + protected extractInput(move: string): string[] { + let input: string[]; + let splitMove = move.toLowerCase().split(' '); + let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; + + input.push(splitMove[1].toUpperCase()) + input.push(lateHit); + + return input; + } + +} \ No newline at end of file From 8d26ae237faa2e2c48f3960ba31097467bae9e9e Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Mon, 3 Jan 2022 19:27:22 -0500 Subject: [PATCH 16/35] Extract formatting logic into rules engine --- src/js/utils/MoveFormatter.ts | 67 ++++++++-- src/js/utils/format_rules/BaseFormatRule.ts | 41 +++--- .../utils/format_rules/MenatSF5FormatRule.ts | 8 +- .../format_rules/YoungZekuSF5FormatRule.ts | 8 +- .../utils/format_rules/codyusf4formatrule.ts | 20 +++ .../utils/format_rules/defaultformatrule.ts | 17 +++ .../format_rules/dhalsimusf4formatrule.ts | 9 ++ src/js/utils/index.ts | 122 +----------------- 8 files changed, 141 insertions(+), 151 deletions(-) create mode 100644 src/js/utils/format_rules/codyusf4formatrule.ts create mode 100644 src/js/utils/format_rules/defaultformatrule.ts create mode 100644 src/js/utils/format_rules/dhalsimusf4formatrule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index 28c3b0f..03abb99 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -1,6 +1,8 @@ -import BaseFormatRule from "./format_rules/BaseFormatRule"; -import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; -import YoungZekuSF5FormatRule from "./format_rules/YoungZekuSF5FormatRule"; +import BaseFormatRule from "./format_rules/baseformatrule"; +import CodyUSF4FormatRule from "./format_rules/codyusf4formatrule"; +import DefaultFormatRule from "./format_rules/defaultformatrule"; +import MenatSF5FormatRule from "./format_rules/menatsf5formatrule"; +import YoungZekuSF5FormatRule from "./format_rules/youngzekusf5formatrule"; export default class MoveFormatter { rules: BaseFormatRule[]; @@ -8,21 +10,64 @@ export default class MoveFormatter { constructor() { this.rules = [ new MenatSF5FormatRule(), - new YoungZekuSF5FormatRule() + new YoungZekuSF5FormatRule(), + new CodyUSF4FormatRule(), + new DefaultFormatRule() ]; } - formatToShorthand(move: string): string { - let shorthand: string = ""; + formatToShorthand(moveData): string { + let shorthand: string; - this.rules.forEach(rule => { - shorthand = rule.formatMove(move); + for (const rule of this.rules) { + if (this.skipFormattingMove(moveData)) { + return ""; + } + + shorthand = rule.formatMove(moveData); - if (shorthand !== "") { + if (shorthand) { return shorthand; } - }); + } + + return ""; + } - return null; + private skipFormattingMove(moveData): boolean { + const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; + const COMMAND_NORMAL: string[] = ["3", "6"]; + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; + const RASHID_WIND: string = "(wind)"; + const MOVE_NAME: string = moveData.moveName; + + if (!moveData.numCmd) { + if (!moveData.plnCmd) { + return true; + } + } + + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { + return true; + } + + // Other languages have a cleaner way of representing this: if any of the values in the + // designated array is in the numCmd, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { + return true; + } + + // If the above check doesn't find anything, check for some other common indicators; if + // nothing comes back here, we're good and don't need to skip formatting + if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + return true; + } + + // Rashid should be the only one (for now) to trigger this condition for his mixers + if (MOVE_NAME.includes(RASHID_WIND)) { + return true; + } + + return false; } } \ No newline at end of file diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index 98d468c..02708b0 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -12,6 +12,9 @@ export default abstract class BaseFormatRule { ["back", "b."] ]); + /** + * @param rule A word, digit, or character to use as criteria for formatting + */ constructor(rule: string) { this.characterMoveRule = rule; } @@ -19,39 +22,38 @@ export default abstract class BaseFormatRule { /** * Given a move in its full-length form (i.e., Stand HP), returns the * move in the common shorthand form of "abbreviated stance.abbreviated input", - * (i.e., st.HP) + * i.e. st.HP * @param move The text representing the move as a string * @returns A string containing the abbreviated move */ - formatMove(move: string): string { - let moveName: string = ""; + formatMove(moveData): string { + let formattedMoveName: string = ""; // If the move doesn't match the rule, we should break out of the method // so the next rule can take over - if (!move.toLowerCase().includes(this.characterMoveRule.toLowerCase())) { - return moveName; + if (!moveData.moveName.toLowerCase().includes(this.characterMoveRule.toLowerCase()) && this.characterMoveRule !== "") { + return formattedMoveName; } // If the move contains something like (Hold), use the regex format method - if (move.includes('(')) { - return this.formatMoveWithParenthesis(move); + if (moveData.moveName.includes('(')) { + return this.formatMoveWithParenthesis(moveData.moveName); } - let stanceAbbreviation: string = this.getStanceToAbbreviation(move); - let moveInput: string[] = this.extractInput(move); + let stanceAbbreviation: string = this.getStanceToAbbreviation(moveData.moveName); + let moveInput: string[] = this.extractInput(moveData); if (moveInput.length > 1) { - moveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; + formattedMoveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; } else { - moveName = `${stanceAbbreviation}${moveInput[0]}`; + formattedMoveName = `${stanceAbbreviation}${moveInput[0]}`; } - return moveName; + return formattedMoveName; } /** - * This method abstracts getting the stance part of the move - * and retrieving its abbreviation from the abbreviation map + * Given a stance in full form (i.e., stand), returns the abbreviated version * @param move The move provided to the call to formatMove * @returns The stance of the move in its abbreviated form */ @@ -69,6 +71,15 @@ export default abstract class BaseFormatRule { * @returns A string containing the abbreviated move */ private formatMoveWithParenthesis(move: string) { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); let modifierParens: string[] = splitMoveFromExtraParens.slice(1); @@ -84,5 +95,5 @@ export default abstract class BaseFormatRule { * used input from the end of the string. * @param move The move provided to the call to formatMove */ - protected abstract extractInput(move: string): string[]; + protected abstract extractInput(moveData): string[]; } \ No newline at end of file diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts index 5d85698..b72e68a 100644 --- a/src/js/utils/format_rules/MenatSF5FormatRule.ts +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./BaseFormatRule"; +import BaseFormatRule from "./baseformatrule"; export default class MenatSF5FormatRule extends BaseFormatRule { private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); @@ -7,9 +7,9 @@ export default class MenatSF5FormatRule extends BaseFormatRule { super("orb"); } - protected extractInput(move: string): string[] { - let input: string[]; - let moveInput: string = move.split(' ').find(input => this.strengths.some(inputStr => inputStr === input)); + protected extractInput(moveData): string[] { + let input: string[] = []; + let moveInput: string = moveData.moveName.split(' ').find(x => this.strengths.some(y => y === x.toLowerCase())); input.push(moveInput.toUpperCase()); input.push(this.orbLabel); diff --git a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts index a2263f1..99fca37 100644 --- a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts +++ b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts @@ -1,13 +1,13 @@ -import BaseFormatRule from "./BaseFormatRule"; +import BaseFormatRule from "./baseformatrule"; export default class YoungZekuSF5FormatRule extends BaseFormatRule { constructor() { super("late"); } - protected extractInput(move: string): string[] { - let input: string[]; - let splitMove = move.toLowerCase().split(' '); + protected extractInput(moveData): string[] { + let input: string[] = []; + let splitMove = moveData.moveName.toLowerCase().split(' '); let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; input.push(splitMove[1].toUpperCase()) diff --git a/src/js/utils/format_rules/codyusf4formatrule.ts b/src/js/utils/format_rules/codyusf4formatrule.ts new file mode 100644 index 0000000..e7418e3 --- /dev/null +++ b/src/js/utils/format_rules/codyusf4formatrule.ts @@ -0,0 +1,20 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class CodyUSF4FormatRule extends BaseFormatRule { + private knifeLabel: string = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); + + constructor() { + super("knife"); + } + + protected extractInput(moveData): string[] { + let input: string[] = []; + let moveInput: string = moveData.moveName.split(' ').find(x => this.strengths.some(y => y === x.toLowerCase())); + + input.push(moveInput.toUpperCase()); + input.push(this.knifeLabel); + + return input; + } + +} \ No newline at end of file diff --git a/src/js/utils/format_rules/defaultformatrule.ts b/src/js/utils/format_rules/defaultformatrule.ts new file mode 100644 index 0000000..57bfd58 --- /dev/null +++ b/src/js/utils/format_rules/defaultformatrule.ts @@ -0,0 +1,17 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class DefaultFormatRule extends BaseFormatRule { + constructor() { + super(""); + } + + protected extractInput(moveData): string[] { + let input: string[] = []; + let splitMove = moveData.moveName.split(' '); + + input.push(splitMove[splitMove.length - 1].toUpperCase()); + + return input; + } + +} \ No newline at end of file diff --git a/src/js/utils/format_rules/dhalsimusf4formatrule.ts b/src/js/utils/format_rules/dhalsimusf4formatrule.ts new file mode 100644 index 0000000..4572119 --- /dev/null +++ b/src/js/utils/format_rules/dhalsimusf4formatrule.ts @@ -0,0 +1,9 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class DhalsimUSF4FormatRule extends BaseFormatRule { + + protected extractInput(moveData: any): string[] { + throw new Error("Method not implemented."); + } + +} \ No newline at end of file diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 16ce8d7..cfb809a 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,4 +1,7 @@ import { mapKeys, isEqual } from 'lodash'; +import { DataDisplaySettingsReducerState } from '../reducers/datadisplaysettings'; +import { VtState } from '../types'; +import MoveFormatter from './moveformatter'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -52,126 +55,11 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return rename; } - const skipFormattingMove = (moveData) => { - const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; - const COMMAND_NORMAL: string[] = ["3", "4", "6"]; - const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; - const RASHID_WIND: string = "(wind)"; - const MOVE_NAME: string = moveData.moveName; - - if (!moveData.numCmd) { - if (!moveData.plnCmd) { - return true; - } - } - - if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { - return true; - } - - // Other languages have a cleaner way of representing this: if any of the values in the - // designated array is in the numCmd, just return the move name since it's a command normal - if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { - return true; - } - - // If the above check doesn't find anything, check for some other common indicators; if - // nothing comes back here, we're good and don't need to skip formatting - if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { - return true; - } - - // Rashid should be the only one (for now) to trigger this condition for his mixers - if (MOVE_NAME.includes(RASHID_WIND)) { - return true; - } - - return false; - } - const formatMoveName = (moveData) => { - const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; - const Y_ZEKU_LATE_HIT: string = "late"; - const MENAT_ORB: string = "orb"; - const USF4_CODY_KNIFE_NORMAL: string = "knife"; - const wordToAbbreviationMap: Map = new Map([ - ["stand", "st."], - ["crouch", "cr."], - ["jump", "j."], - ["neutral", "nj."], - ["close", "c."], - ["far", "f."], - ["downback", "db."], - ["back", "b."] - ]); - let truncatedMoveName: string = ""; + let moveFormatter = new MoveFormatter(); - const extractInput = (splitMove: string[]) => { - let input: string[] = []; - let uniqueAspect: string = ""; - let isMenatOrbNormal: boolean = splitMove.some(x => x.toLowerCase().includes(MENAT_ORB)); - let isUSF4CodyKnifeNormal: boolean = splitMove.some(x => x.toLowerCase().includes(USF4_CODY_KNIFE_NORMAL)); - - // Menat orb and USF4 Cody with knife need special attention - if (isMenatOrbNormal || isUSF4CodyKnifeNormal) { - - if (isMenatOrbNormal) { - uniqueAspect = MENAT_ORB.charAt(0).toUpperCase() + MENAT_ORB.slice(1); - } - - if (isUSF4CodyKnifeNormal) { - uniqueAspect = USF4_CODY_KNIFE_NORMAL.charAt(0).toUpperCase() + USF4_CODY_KNIFE_NORMAL.slice(1); - } - - let button: string = splitMove.find(x => strengths.some(y => y === x)); - - input.push(button.toUpperCase()); - input.push(uniqueAspect); - // Young Zeku's crouch HK also need special attention since it has a "late hit" - // state that gives it different properties and is listed as such in FAT. - } else if (splitMove.some(x => x.toLowerCase().includes(Y_ZEKU_LATE_HIT))) { - // Index 3 contains "late", index 4 contains "hit" - let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; - - input.push(splitMove[1].toUpperCase()); - input.push(lateHit); - } else { - input.push(splitMove[splitMove.length - 1].toUpperCase()); - } - - return input; - } - - if (!skipFormattingMove(moveData)) { - if (!moveData.moveName.includes('(')) { - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string[] = extractInput(splitMoveName); - - if (input.length > 1) { - truncatedMoveName = `${abbr}${input[0]} ${input[1]}`; - } else { - truncatedMoveName = `${abbr}${input[0]}`; - } - } else { - /* - Regex documentation: - Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result - The outermost parentheses start the capture group of characters we DO want to capture - The character combo of \( means that we want to find an actual opening parenthesis - [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" - Then we want to find the closing parenthesis with \) - The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression - */ - let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); - let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); - let modifierParens: string[] = splitMoveFromExtraParens.slice(1); - let abbr: string = wordToAbbreviationMap.get(splitMove[0].toLowerCase()); - let input: string = splitMove[splitMove.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; - } - } + truncatedMoveName = moveFormatter.formatToShorthand(moveData); return truncatedMoveName; } From 48b3ea093b63317d562973e29f548bbc3af4dc41 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Fri, 18 Feb 2022 22:56:03 -0500 Subject: [PATCH 17/35] Add final tweaks & rules for SF4 and Third Strike --- src/js/utils/MoveFormatter.ts | 35 ++++++++++++++++--- src/js/utils/format_rules/BaseFormatRule.ts | 12 ++++--- .../utils/format_rules/MenatSF5FormatRule.ts | 1 + .../utils/format_rules/codyusf4formatrule.ts | 1 + .../utils/format_rules/defaultformatrule.ts | 2 +- .../format_rules/dhalsimusf4formatrule.ts | 9 ----- 6 files changed, 40 insertions(+), 20 deletions(-) delete mode 100644 src/js/utils/format_rules/dhalsimusf4formatrule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index 03abb99..fc9a9d4 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -5,7 +5,7 @@ import MenatSF5FormatRule from "./format_rules/menatsf5formatrule"; import YoungZekuSF5FormatRule from "./format_rules/youngzekusf5formatrule"; export default class MoveFormatter { - rules: BaseFormatRule[]; + private rules: BaseFormatRule[]; constructor() { this.rules = [ @@ -34,11 +34,24 @@ export default class MoveFormatter { return ""; } + /** + * Skips character moves that meet various criteria in order to focus on applying + * formatting to normals. + * @remarks Some move types like command normals and others will sometimes get caught by the + * formatter engine because of their attributes in their JSON object. + * @param moveData The current move and its attributes as a JSON object + * @returns true if the move should not have formatting applied to it, false otherwise + */ private skipFormattingMove(moveData): boolean { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; const COMMAND_NORMAL: string[] = ["3", "6"]; const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; const RASHID_WIND: string = "(wind)"; + const IGNORED_THIRD_STRIKE_MOVES: string[] = [ + "Kakushuu Rakukyaku" /* Chun-li b.MK (Hold) */, + "Inazuma Kakato Wari (Long)" /* Ken b.MK (Hold) */, + "Elbow Cannon" /* Necro db.HP */ + ]; const MOVE_NAME: string = moveData.moveName; if (!moveData.numCmd) { @@ -46,15 +59,15 @@ export default class MoveFormatter { return true; } } - + + // Do not attempt to apply formatting to target combos if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } - // Other languages have a cleaner way of representing this: if any of the values in the - // designated array is in the numCmd, just return the move name since it's a command normal + // Do not attempt to apply formatting to command normals if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { - return true; + return true; } // If the above check doesn't find anything, check for some other common indicators; if @@ -67,6 +80,18 @@ export default class MoveFormatter { if (MOVE_NAME.includes(RASHID_WIND)) { return true; } + + // For USF4, if the move motion is "back" but the move name doesn't include back, skip it + if (moveData.moveMotion === "B" && !MOVE_NAME.includes("Back")) { + return true; + } + + // There are three awkward moves that get caught by the formatting engine + // for the 3S data. If the 3S data is ever cleaned up, this could be removed + // or refactored + if (IGNORED_THIRD_STRIKE_MOVES.some(indicator => MOVE_NAME === indicator)) { + return true; + } return false; } diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index 02708b0..47c47c8 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -1,11 +1,11 @@ export default abstract class BaseFormatRule { - characterMoveRule: string; - strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; - stanceToAbbreviationMap: Map = new Map([ + protected characterMoveRule: string; + protected strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + protected stanceToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], ["jump", "j."], - ["neutral", "nj."], + ["neutral jump", "nj."], ["close", "c."], ["far", "f."], ["downback", "db."], @@ -58,7 +58,9 @@ export default abstract class BaseFormatRule { * @returns The stance of the move in its abbreviated form */ private getStanceToAbbreviation(move: string): string { - let stance = move.toLowerCase().split(' ')[0]; + let splitMove: string[] = move.trim().toLowerCase().split(' '); + let stance: string = splitMove[0]; + return this.stanceToAbbreviationMap.get(stance); } diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts index b72e68a..71a8075 100644 --- a/src/js/utils/format_rules/MenatSF5FormatRule.ts +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -1,6 +1,7 @@ import BaseFormatRule from "./baseformatrule"; export default class MenatSF5FormatRule extends BaseFormatRule { + // Sentence-casing for the "Orb" label private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); constructor() { diff --git a/src/js/utils/format_rules/codyusf4formatrule.ts b/src/js/utils/format_rules/codyusf4formatrule.ts index e7418e3..48b4e35 100644 --- a/src/js/utils/format_rules/codyusf4formatrule.ts +++ b/src/js/utils/format_rules/codyusf4formatrule.ts @@ -1,6 +1,7 @@ import BaseFormatRule from "./baseformatrule"; export default class CodyUSF4FormatRule extends BaseFormatRule { + // Sentence-casing for the "Knife" label private knifeLabel: string = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); constructor() { diff --git a/src/js/utils/format_rules/defaultformatrule.ts b/src/js/utils/format_rules/defaultformatrule.ts index 57bfd58..b404180 100644 --- a/src/js/utils/format_rules/defaultformatrule.ts +++ b/src/js/utils/format_rules/defaultformatrule.ts @@ -7,7 +7,7 @@ export default class DefaultFormatRule extends BaseFormatRule { protected extractInput(moveData): string[] { let input: string[] = []; - let splitMove = moveData.moveName.split(' '); + let splitMove = moveData.moveName.trim().split(' '); input.push(splitMove[splitMove.length - 1].toUpperCase()); diff --git a/src/js/utils/format_rules/dhalsimusf4formatrule.ts b/src/js/utils/format_rules/dhalsimusf4formatrule.ts deleted file mode 100644 index 4572119..0000000 --- a/src/js/utils/format_rules/dhalsimusf4formatrule.ts +++ /dev/null @@ -1,9 +0,0 @@ -import BaseFormatRule from "./baseformatrule"; - -export default class DhalsimUSF4FormatRule extends BaseFormatRule { - - protected extractInput(moveData: any): string[] { - throw new Error("Method not implemented."); - } - -} \ No newline at end of file From 6b90b070038f1b9919efe0b3145365d449e4f57a Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Tue, 9 Mar 2021 20:05:43 -0500 Subject: [PATCH 18/35] initial refactor renameData function - replace if-else chain with switch statement - wrap lodash._mapKeys in function - add empty function as placeholder for shorthand renaming - apply vs code "format document" - add "shorthand" option to naming types in settings --- src/js/pages/Settings.tsx | 1 + src/js/utils/index.ts | 59 +++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/js/pages/Settings.tsx b/src/js/pages/Settings.tsx index e8e35f0..8bd8801 100644 --- a/src/js/pages/Settings.tsx +++ b/src/js/pages/Settings.tsx @@ -83,6 +83,7 @@ const Settings = () => { > Official Common + Shorthand Inputs diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 017fd87..6e3dc8c 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,22 +1,33 @@ import { mapKeys, isEqual } from 'lodash'; +/** + * Renames the moves in the character frame data to reflect the user's desired naming convention + * @param {string} rawFrameData The frame data for the current character + * @param {string} moveNameType The naming convention + * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed + * @returns The frame data JSON object with renamed moves + */ export function renameData(rawFrameData, moveNameType, inputNotationType) { - - let renameKey = ""; - if (moveNameType === "official") { - renameKey = "moveName" - } else if (moveNameType === "common") { - renameKey = "cmnName"; - } else if (moveNameType === "inputs" && inputNotationType) { - renameKey = inputNotationType; + const bulkRenameFrameData = (rawData, renameKey) => { + return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); } - const renamedFrameData = mapKeys(rawFrameData, - (moveData, moveKey) => - moveData[renameKey] ? moveData[renameKey] : moveKey - ); + const shorthandRename = (rawData) => { + + } - return renamedFrameData; + switch (moveNameType) { + case "official": + return bulkRenameFrameData(rawFrameData, "moveName"); + case "common": + return bulkRenameFrameData(rawFrameData, "cmnName"); + case "shorthand": + return shorthandRename(rawFrameData); + case "inputs": + return bulkRenameFrameData(rawFrameData, inputNotationType); + default: + break; + } } @@ -27,18 +38,18 @@ function vTriggerMerge(rawFrameData, vtState) { const vtMergedData = { ...rawFrameData.normal, ...rawFrameData[vtState] } - + Object.keys(rawFrameData[vtState]).forEach(vtMove => { - let changedValues = []; - Object.keys(rawFrameData[vtState][vtMove]).forEach(detail => { - if (!rawFrameData.normal[vtMove]) { - vtMergedData[vtMove]["uniqueInVt"] = true; - } else if (rawFrameData.normal[vtMove] && !isEqual(rawFrameData.normal[vtMove][detail], rawFrameData[vtState][vtMove][detail])) { - changedValues = [ ...changedValues, detail ] - } - }) - vtMergedData[vtMove] = { ...vtMergedData[vtMove], changedValues } + let changedValues = []; + Object.keys(rawFrameData[vtState][vtMove]).forEach(detail => { + if (!rawFrameData.normal[vtMove]) { + vtMergedData[vtMove]["uniqueInVt"] = true; + } else if (rawFrameData.normal[vtMove] && !isEqual(rawFrameData.normal[vtMove][detail], rawFrameData[vtState][vtMove][detail])) { + changedValues = [...changedValues, detail] } + }) + vtMergedData[vtMove] = { ...vtMergedData[vtMove], changedValues } + } ) // based on https://stackoverflow.com/a/39442287 @@ -47,7 +58,7 @@ function vTriggerMerge(rawFrameData, vtState) { .sort((moveOne: any, moveTwo: any) => { return moveOne[1].i - moveTwo[1].i }) - .reduce((_sortedObj, [k,v]) => ({ + .reduce((_sortedObj, [k, v]) => ({ ..._sortedObj, [k]: v }), {}) From cefd975f3a4873c44c5b93c7e5bde0e27b771dc1 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 10 Mar 2021 11:27:49 -0500 Subject: [PATCH 19/35] mostly pseudocode for move name formatting --- src/js/utils/index.ts | 43 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 6e3dc8c..532365a 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,4 +1,4 @@ -import { mapKeys, isEqual } from 'lodash'; +import { mapKeys, isEqual, has } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -9,11 +9,50 @@ import { mapKeys, isEqual } from 'lodash'; */ export function renameData(rawFrameData, moveNameType, inputNotationType) { const bulkRenameFrameData = (rawData, renameKey) => { - return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + let debugReturn = mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + + return debugReturn; + } + + const formatMoveName = (moveData) => { + + let splitMoveName = moveData.plnCmd.split(/[\\s+]+/); + + /* + split the plain command input into direction, button, and if it's present, aerial state + (the regex supplied will safely split the plain command if it's just a grounded direction and input) + if the length of the resulting array is greater than 2, we're dealing with an airborne move + return "j" for jump prepended to [direction].[input] + else + return [direction].[input] + */ + } const shorthandRename = (rawData) => { + // let foo = rawData; + + // for (const fdKey of Object.keys(foo)) { + // console.log(`Key: ${fdKey} ---- Value: ${foo[fdKey].moveType}`); + + // if (foo[fdKey].moveType === "normal") { + + // } + // } + + // return foo; + + let rename = mapKeys(rawData, (moveValue, moveKey) => { + if (moveValue.moveType === "normal") { + if (has(moveValue, "cmnName")) { + return moveValue.cmnName; + } else { + return moveKey; + } + } + }); + return rename; } switch (moveNameType) { From 1e2dea20a3c82b1e34870db2bdf0cf6084823345 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Thu, 11 Mar 2021 16:01:36 -0500 Subject: [PATCH 20/35] finish initial impl. of shorthand rename func --- src/js/utils/index.ts | 61 ++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 532365a..2ab8959 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,4 +1,4 @@ -import { mapKeys, isEqual, has } from 'lodash'; +import { mapKeys, isEqual } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -14,54 +14,43 @@ export function renameData(rawFrameData, moveNameType, inputNotationType) { return debugReturn; } - const formatMoveName = (moveData) => { - - let splitMoveName = moveData.plnCmd.split(/[\\s+]+/); - - /* - split the plain command input into direction, button, and if it's present, aerial state - (the regex supplied will safely split the plain command if it's just a grounded direction and input) - if the length of the resulting array is greater than 2, we're dealing with an airborne move - return "j" for jump prepended to [direction].[input] - else - return [direction].[input] - */ - - } - - const shorthandRename = (rawData) => { - // let foo = rawData; - - // for (const fdKey of Object.keys(foo)) { - // console.log(`Key: ${fdKey} ---- Value: ${foo[fdKey].moveType}`); - - // if (foo[fdKey].moveType === "normal") { - - // } - // } - - // return foo; - + const renameFrameDataToShorthand = (rawData) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { - if (moveValue.moveType === "normal") { - if (has(moveValue, "cmnName")) { - return moveValue.cmnName; - } else { - return moveKey; - } + if (moveValue.moveType === "normal" && !moveValue.movesList) { + return formatMoveName(moveValue); + } else { + return moveKey; } }); return rename; } + const formatMoveName = (moveData) => { + let truncatedMoveName: string = ""; + const numberToDirectionMap: Map = new Map([ + ["2", "cr."], + ["5", "st."], + ["8", "nj."] + ]); + + if (moveData.numCmd.includes("j")) { + truncatedMoveName = moveData.numCmd; + } else { + let [direction, input]: string[] = moveData.numCmd.split(/(\d)/).filter((x: string) => x !== ""); + truncatedMoveName = `${numberToDirectionMap.get(direction)}${input}`; + } + + return truncatedMoveName; + } + switch (moveNameType) { case "official": return bulkRenameFrameData(rawFrameData, "moveName"); case "common": return bulkRenameFrameData(rawFrameData, "cmnName"); case "shorthand": - return shorthandRename(rawFrameData); + return renameFrameDataToShorthand(rawFrameData); case "inputs": return bulkRenameFrameData(rawFrameData, inputNotationType); default: From 287747ab503d051e6962fe5ae2d3dd6a1bbba43c Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 12:49:07 -0500 Subject: [PATCH 21/35] renameData function signature initial changes d4rk and i discussed the actual implementation of the updated function into the app and his vision was to have an extra entirely separate setting for longform and shorthand notation, so i've adjusted the function a bit to prepare for him adding a redux store for that setting and some various other changes to support it --- src/js/utils/index.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 2ab8959..48bfa12 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -5,13 +5,19 @@ import { mapKeys, isEqual } from 'lodash'; * @param {string} rawFrameData The frame data for the current character * @param {string} moveNameType The naming convention * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed + * @param {string} notationDisplayType The move name size convention ("longform" & "shorthand") * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, moveNameType, inputNotationType) { - const bulkRenameFrameData = (rawData, renameKey) => { - let debugReturn = mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); - - return debugReturn; +export function renameData(rawFrameData, moveNameType, inputNotationType, notationDisplayType) { + const renameFrameData = (rawData, renameKey, notationDisplay) => { + switch (notationDisplay) { + case "longform": + return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); + case "shorthand": + return renameFrameDataToShorthand(rawData); + default: + break; + } } const renameFrameDataToShorthand = (rawData) => { @@ -46,13 +52,11 @@ export function renameData(rawFrameData, moveNameType, inputNotationType) { switch (moveNameType) { case "official": - return bulkRenameFrameData(rawFrameData, "moveName"); + return renameFrameData(rawFrameData, "moveName", notationDisplayType); case "common": - return bulkRenameFrameData(rawFrameData, "cmnName"); - case "shorthand": - return renameFrameDataToShorthand(rawFrameData); + return renameFrameData(rawFrameData, "cmnName", notationDisplayType); case "inputs": - return bulkRenameFrameData(rawFrameData, inputNotationType); + return renameFrameData(rawFrameData, inputNotationType, notationDisplayType); default: break; } From 164207f172cb81939d5fd2b29096a6ff0e265e34 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 14:23:25 -0500 Subject: [PATCH 22/35] rebase main onto abbreviation branch --- src/js/actions/index.ts | 4 ++-- src/js/pages/Settings.tsx | 7 ++----- src/js/utils/index.ts | 27 +++++++++++---------------- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/js/actions/index.ts b/src/js/actions/index.ts index 2ce4128..d28464b 100644 --- a/src/js/actions/index.ts +++ b/src/js/actions/index.ts @@ -128,9 +128,9 @@ export const setPlayer = (playerId: PlayerId, charName: PlayerData["name"]) => : "normal" const playerData: PlayerData = { name: charName, - frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState.moveNameType, dataDisplaySettingsState.inputNotationType, dataDisplaySettingsState.normalNotationType, stateToSet), + frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState, vTriggerStateToSet), stats: frameDataState[charName].stats, - vtState: stateToSet, + vtState: vTriggerStateToSet, } dispatch({ type: 'SET_PLAYER', diff --git a/src/js/pages/Settings.tsx b/src/js/pages/Settings.tsx index 8bd8801..7164702 100644 --- a/src/js/pages/Settings.tsx +++ b/src/js/pages/Settings.tsx @@ -83,7 +83,6 @@ const Settings = () => { > Official Common - Shorthand Inputs @@ -111,8 +110,7 @@ const Settings = () => { - {/* @Jon Uncomment this! */} - {/* +

Normal Notation

Choose long or short normal names

@@ -133,8 +131,7 @@ const Settings = () => { Full Word Shorthand -
*/} - +
{/* APP OPTIONS */} App Settings diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 48bfa12..f7913e3 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -3,15 +3,13 @@ import { mapKeys, isEqual } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention * @param {string} rawFrameData The frame data for the current character - * @param {string} moveNameType The naming convention - * @param {string} inputNotationType If the user selected "inputs" for their naming convention, the input notation displayed - * @param {string} notationDisplayType The move name size convention ("longform" & "shorthand") + * @param {DataDisplaySettingsReducerState} displayState The Redux state containing various move text render settings * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, moveNameType, inputNotationType, notationDisplayType) { +export function renameData(rawFrameData, displayState: DataDisplaySettingsReducerState) { const renameFrameData = (rawData, renameKey, notationDisplay) => { switch (notationDisplay) { - case "longform": + case "fullWord": return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); case "shorthand": return renameFrameDataToShorthand(rawData); @@ -50,15 +48,15 @@ export function renameData(rawFrameData, moveNameType, inputNotationType, notati return truncatedMoveName; } - switch (moveNameType) { + switch (displayState.moveNameType) { case "official": - return renameFrameData(rawFrameData, "moveName", notationDisplayType); + return renameFrameData(rawFrameData, "moveName", displayState.normalNotationType); case "common": - return renameFrameData(rawFrameData, "cmnName", notationDisplayType); + return renameFrameData(rawFrameData, "cmnName", displayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, inputNotationType, notationDisplayType); + return renameFrameData(rawFrameData, displayState.inputNotationType, displayState.normalNotationType); default: - break; + return rawFrameData; } } @@ -100,12 +98,9 @@ function vTriggerMerge(rawFrameData, vtState) { } // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet -export function helpCreateFrameDataJSON(rawFrameData, moveNameType, inputNotationType, normalNotationType, vtState) { +export function helpCreateFrameDataJSON(rawFrameData, displayState: DataDisplaySettingsReducerState, vtState: VtState) { - const dataToRename = vtState === "normal" - ? rawFrameData.normal - : vTriggerMerge(rawFrameData, vtState); - - return moveNameType === "official" ? dataToRename : renameData(dataToRename, moveNameType, inputNotationType); + const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); + return (displayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, displayState); } \ No newline at end of file From 433b2079a9c64d5c071351bcb199df1dcf43de33 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 13 Mar 2021 18:13:03 -0500 Subject: [PATCH 23/35] change variable for clarity based on feedback - 'change displayState' to 'dataDisplayState' --- src/js/utils/index.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index f7913e3..45d7a2e 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -3,10 +3,10 @@ import { mapKeys, isEqual } from 'lodash'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention * @param {string} rawFrameData The frame data for the current character - * @param {DataDisplaySettingsReducerState} displayState The Redux state containing various move text render settings + * @param {DataDisplaySettingsReducerState} dataDisplayState The Redux state containing various move text render settings * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, displayState: DataDisplaySettingsReducerState) { +export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState) { const renameFrameData = (rawData, renameKey, notationDisplay) => { switch (notationDisplay) { case "fullWord": @@ -48,13 +48,13 @@ export function renameData(rawFrameData, displayState: DataDisplaySettingsReduce return truncatedMoveName; } - switch (displayState.moveNameType) { + switch (dataDisplayState.moveNameType) { case "official": - return renameFrameData(rawFrameData, "moveName", displayState.normalNotationType); + return renameFrameData(rawFrameData, "moveName", dataDisplayState.normalNotationType); case "common": - return renameFrameData(rawFrameData, "cmnName", displayState.normalNotationType); + return renameFrameData(rawFrameData, "cmnName", dataDisplayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, displayState.inputNotationType, displayState.normalNotationType); + return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, dataDisplayState.normalNotationType); default: return rawFrameData; } @@ -98,9 +98,9 @@ function vTriggerMerge(rawFrameData, vtState) { } // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet -export function helpCreateFrameDataJSON(rawFrameData, displayState: DataDisplaySettingsReducerState, vtState: VtState) { +export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState) { const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); - return (displayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, displayState); + return (dataDisplayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, dataDisplayState); } \ No newline at end of file From 79a22413a0e61ee23b40e0bb6b7a22ad89acea72 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 17 Mar 2021 14:24:47 -0400 Subject: [PATCH 24/35] apply PR feedback - ensured that input name types won't be converted to shorthand - remove ternary operator in return statement in helpCreateFrameDataJSON --- src/js/utils/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 45d7a2e..85cf952 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -54,7 +54,7 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe case "common": return renameFrameData(rawFrameData, "cmnName", dataDisplayState.normalNotationType); case "inputs": - return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, dataDisplayState.normalNotationType); + return renameFrameData(rawFrameData, dataDisplayState.inputNotationType, "fullWord"); default: return rawFrameData; } @@ -102,5 +102,5 @@ export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisp const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); - return (dataDisplayState.moveNameType === "official") ? dataToRename : renameData(dataToRename, dataDisplayState); + return renameData(dataToRename, dataDisplayState); } \ No newline at end of file From 93178cbaf3aeb1c7ed4370bae5905e65fcefb2a2 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Fri, 19 Mar 2021 16:30:50 -0400 Subject: [PATCH 25/35] PR changes: - fix bug where common inputs with shorthand were leaving specials "untranslated" to common input - refactor formatMoveName to work with moveName property instead of numCmd property due to consistency changes in the way numeric inputs were represented --- src/js/utils/index.ts | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 85cf952..d6d81b1 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -12,18 +12,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe case "fullWord": return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); case "shorthand": - return renameFrameDataToShorthand(rawData); + return renameFrameDataToShorthand(rawData, renameKey); default: break; } } - const renameFrameDataToShorthand = (rawData) => { + const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { if (moveValue.moveType === "normal" && !moveValue.movesList) { return formatMoveName(moveValue); } else { - return moveKey; + return moveValue[nameTypeKey]; } }); @@ -32,18 +32,17 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const formatMoveName = (moveData) => { let truncatedMoveName: string = ""; - const numberToDirectionMap: Map = new Map([ - ["2", "cr."], - ["5", "st."], - ["8", "nj."] + const wordToAbbreviationMap: Map = new Map([ + ["stand", "st."], + ["crouch", "cr."], + ["jump", "j."], + ["neutral", "nj."] ]); - if (moveData.numCmd.includes("j")) { - truncatedMoveName = moveData.numCmd; - } else { - let [direction, input]: string[] = moveData.numCmd.split(/(\d)/).filter((x: string) => x !== ""); - truncatedMoveName = `${numberToDirectionMap.get(direction)}${input}`; - } + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; return truncatedMoveName; } @@ -99,7 +98,7 @@ function vTriggerMerge(rawFrameData, vtState) { // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState) { - + const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); return renameData(dataToRename, dataDisplayState); From 3a257622a5cc0f0b92e62e0f6d59c38461faebc7 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 20 Mar 2021 13:52:44 -0400 Subject: [PATCH 26/35] start handling moves with modifying parentheses --- src/js/utils/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index d6d81b1..65b32c1 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -39,10 +39,23 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; + if (!moveData.moveName.includes('(')) { + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; + } else { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ + let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + } return truncatedMoveName; } From 94d832b09c40d69f1eb9633b0ff4786233115f86 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Tue, 4 May 2021 11:13:39 -0400 Subject: [PATCH 27/35] rough work for implementing cross-game shorthand --- src/js/utils/index.ts | 63 ++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 65b32c1..6ac3063 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -20,7 +20,7 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { let rename = mapKeys(rawData, (moveValue, moveKey) => { - if (moveValue.moveType === "normal" && !moveValue.movesList) { + if ((moveValue.moveType === "normal" || moveValue.moveType === "held normal") && !moveValue.movesList) { return formatMoveName(moveValue); } else { return moveValue[nameTypeKey]; @@ -30,6 +30,24 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return rename; } + const skipFormattingMove = (moveData) => { + const TARGET_COMBO: string = "(TC)"; + const COMMAND_NORMAL: string[] = ["b", "f", ">", "(air)"]; + const MOVE_NAME: string = moveData.moveName; + + if (MOVE_NAME.includes(TARGET_COMBO)) { + return true; + } + + // Other languages have a cleaner way of representing this: if any of the values in the + // array are in the string, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + return true; + } + + return false; + } + const formatMoveName = (moveData) => { let truncatedMoveName: string = ""; const wordToAbbreviationMap: Map = new Map([ @@ -39,24 +57,31 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); - if (!moveData.moveName.includes('(')) { - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; - } else { - /* - Regex documentation: - Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result - The outermost parentheses start the capture group of characters we DO want to capture - The character combo of \( means that we want to find an actual opening parenthesis - [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" - Then we want to find the closing parenthesis with \) - The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression - */ - let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); - } - + if (!skipFormattingMove(moveData)) { + if (!moveData.moveName.includes('(')) { + let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); + let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); + let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input}`; + } else { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ + let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); + let modifierParens: string[] = splitMoveFromExtraParens.slice(1); + let abbr: string = wordToAbbreviationMap.get(splitMove[0].toLowerCase()); + let input: string = splitMove[splitMove.length - 1].toUpperCase(); + truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; + } + } + return truncatedMoveName; } From 1e433586ef1ae5ccf279cde5293462d1dcf2620b Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Wed, 26 May 2021 23:18:03 -0400 Subject: [PATCH 28/35] begin refactoring code to address changes in data - movesList will now also contain the type of normal it is, i.e., this will hold 'held normal' if it's normal that changes when the button is held - applying some logic changes to skipFormattingMove to function in line with the changes to the frame data - menat was breaking the implementation, i had to create a function for encapsulating the input extraction --- src/js/utils/index.ts | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 13 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 6ac3063..51d8b6f 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -19,9 +19,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe } const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { + const HELD_NORMAL: string = "Held Normal"; let rename = mapKeys(rawData, (moveValue, moveKey) => { - if ((moveValue.moveType === "normal" || moveValue.moveType === "held normal") && !moveValue.movesList) { - return formatMoveName(moveValue); + if ((moveValue.moveType === "normal")) { + if (moveValue.movesList) { + if (moveValue.movesList === HELD_NORMAL) { + return formatMoveName(moveValue); + } else { + return moveValue[nameTypeKey]; + } + } else { + return formatMoveName(moveValue); + } } else { return moveValue[nameTypeKey]; } @@ -31,25 +40,32 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe } const skipFormattingMove = (moveData) => { - const TARGET_COMBO: string = "(TC)"; - const COMMAND_NORMAL: string[] = ["b", "f", ">", "(air)"]; + const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; + const COMMAND_NORMAL: string[] = ["4", "6"]; + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)"] const MOVE_NAME: string = moveData.moveName; - if (MOVE_NAME.includes(TARGET_COMBO)) { + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } // Other languages have a cleaner way of representing this: if any of the values in the - // array are in the string, just return the move name since it's a command normal - if (COMMAND_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + // designated array is in the numCmd, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { + return true; + } + + // If the above check doesn't find anything, check for some other common indicators; if + // nothing comes back here, we're good and don't need to skip formatting + if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { return true; } return false; } - const formatMoveName = (moveData) => { - let truncatedMoveName: string = ""; + const formatMoveName = (moveData) => { + const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], @@ -57,12 +73,36 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe ["neutral", "nj."] ]); + let truncatedMoveName: string = ""; + + const extractInput = (splitMove: string[]) => { + let input: string[] = []; + + // Menat orb needs special attention + if (splitMove.some(x => x.toLocaleLowerCase().includes("orb"))) { + let orb: string = splitMove.find(x => x === "orb"); + let button: string = splitMove.find(x => strengths.some(y => y === x)); + + input.push(button.toUpperCase()); + input.push(orb); + } else { + input.push(splitMove[splitMove.length - 1].toUpperCase()); + } + + return input; + } + if (!skipFormattingMove(moveData)) { if (!moveData.moveName.includes('(')) { let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string = splitMoveName[splitMoveName.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input}`; + let input: string[] = extractInput(splitMoveName); + + if (input.length > 1) { + truncatedMoveName = `${abbr}${input[0]} ${input[1]}`; + } else { + truncatedMoveName = `${abbr}${input[0]}`; + } } else { /* Regex documentation: @@ -80,8 +120,8 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe let input: string = splitMove[splitMove.length - 1].toUpperCase(); truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; } - } - + } + return truncatedMoveName; } From f4bd360406788d7d51a6d55b0bad8c262a3a747d Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sun, 6 Jun 2021 00:18:17 -0400 Subject: [PATCH 29/35] hopefully last stages of shorthand for sf5 - implement 'default to official name' logic for shorthand rename - add edge case catching for Rashid to skipFormattingMove - add edge case catching for Young Zeku and Menat to formatMoveName --- src/js/utils/index.ts | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 51d8b6f..26f63af 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -21,18 +21,31 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { const HELD_NORMAL: string = "Held Normal"; let rename = mapKeys(rawData, (moveValue, moveKey) => { + const DEFAULT_MOVE_NAME = () => { + if (moveValue[nameTypeKey]) { + return moveValue[nameTypeKey]; + } else { + return moveKey; + } + } + if ((moveValue.moveType === "normal")) { if (moveValue.movesList) { if (moveValue.movesList === HELD_NORMAL) { return formatMoveName(moveValue); } else { - return moveValue[nameTypeKey]; + return DEFAULT_MOVE_NAME(); } } else { - return formatMoveName(moveValue); + let formatted: string = formatMoveName(moveValue); + if (formatted !== "") { + return formatted; + } else { + return DEFAULT_MOVE_NAME(); + } } } else { - return moveValue[nameTypeKey]; + return DEFAULT_MOVE_NAME(); } }); @@ -42,7 +55,8 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const skipFormattingMove = (moveData) => { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; const COMMAND_NORMAL: string[] = ["4", "6"]; - const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)"] + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; + const RASHID_WIND: string = "(wind)"; const MOVE_NAME: string = moveData.moveName; if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { @@ -61,11 +75,18 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return true; } + // Rashid should be the only one (for now) to trigger this condition for his mixers + if (MOVE_NAME.includes(RASHID_WIND)) { + return true; + } + return false; } const formatMoveName = (moveData) => { const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + const Y_ZEKU_LATE_HIT: string = "late"; + const MENAT_ORB: string = "orb"; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], @@ -79,12 +100,20 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe let input: string[] = []; // Menat orb needs special attention - if (splitMove.some(x => x.toLocaleLowerCase().includes("orb"))) { + if (splitMove.some(x => x.toLocaleLowerCase().includes(MENAT_ORB))) { let orb: string = splitMove.find(x => x === "orb"); let button: string = splitMove.find(x => strengths.some(y => y === x)); input.push(button.toUpperCase()); input.push(orb); + // Young Zeku's crouch HK also need special attention since it has a "late hit" + // state that gives it different properties and is listed as such in FAT. + } else if (splitMove.some(x => x.toLocaleLowerCase().includes(Y_ZEKU_LATE_HIT))) { + // Index 3 contains "late", index 4 contains "hit" + let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; + + input.push(splitMove[1].toUpperCase()); + input.push(lateHit); } else { input.push(splitMove[splitMove.length - 1].toUpperCase()); } From 46564e0776ca462230cde265e8efc06d165db3ec Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 24 Jul 2021 21:46:03 -0400 Subject: [PATCH 30/35] Additional changes to handle SF4 edge cases --- src/js/utils/index.ts | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 26f63af..16ce8d7 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -54,11 +54,17 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const skipFormattingMove = (moveData) => { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; - const COMMAND_NORMAL: string[] = ["4", "6"]; + const COMMAND_NORMAL: string[] = ["3", "4", "6"]; const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; const RASHID_WIND: string = "(wind)"; const MOVE_NAME: string = moveData.moveName; + if (!moveData.numCmd) { + if (!moveData.plnCmd) { + return true; + } + } + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } @@ -87,28 +93,44 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; const Y_ZEKU_LATE_HIT: string = "late"; const MENAT_ORB: string = "orb"; + const USF4_CODY_KNIFE_NORMAL: string = "knife"; const wordToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], ["jump", "j."], - ["neutral", "nj."] + ["neutral", "nj."], + ["close", "c."], + ["far", "f."], + ["downback", "db."], + ["back", "b."] ]); let truncatedMoveName: string = ""; const extractInput = (splitMove: string[]) => { - let input: string[] = []; + let input: string[] = []; + let uniqueAspect: string = ""; + let isMenatOrbNormal: boolean = splitMove.some(x => x.toLowerCase().includes(MENAT_ORB)); + let isUSF4CodyKnifeNormal: boolean = splitMove.some(x => x.toLowerCase().includes(USF4_CODY_KNIFE_NORMAL)); - // Menat orb needs special attention - if (splitMove.some(x => x.toLocaleLowerCase().includes(MENAT_ORB))) { - let orb: string = splitMove.find(x => x === "orb"); + // Menat orb and USF4 Cody with knife need special attention + if (isMenatOrbNormal || isUSF4CodyKnifeNormal) { + + if (isMenatOrbNormal) { + uniqueAspect = MENAT_ORB.charAt(0).toUpperCase() + MENAT_ORB.slice(1); + } + + if (isUSF4CodyKnifeNormal) { + uniqueAspect = USF4_CODY_KNIFE_NORMAL.charAt(0).toUpperCase() + USF4_CODY_KNIFE_NORMAL.slice(1); + } + let button: string = splitMove.find(x => strengths.some(y => y === x)); input.push(button.toUpperCase()); - input.push(orb); + input.push(uniqueAspect); // Young Zeku's crouch HK also need special attention since it has a "late hit" // state that gives it different properties and is listed as such in FAT. - } else if (splitMove.some(x => x.toLocaleLowerCase().includes(Y_ZEKU_LATE_HIT))) { + } else if (splitMove.some(x => x.toLowerCase().includes(Y_ZEKU_LATE_HIT))) { // Index 3 contains "late", index 4 contains "hit" let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; From 43560c752a1762b00522846e54ac00b3edd730d9 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sat, 16 Oct 2021 15:32:37 -0400 Subject: [PATCH 31/35] begin development of rules engine - logic in index.ts is getting too dense, filled with edge cases - created MoveFormatter class to hold all rules and perform shorthand formatting per rule - created BaseFormatRule class and example derived class --- src/js/utils/MoveFormatter.ts | 26 +++++++++ src/js/utils/format_rules/BaseFormatRule.ts | 58 +++++++++++++++++++ .../utils/format_rules/MenatSF5FormatRule.ts | 19 ++++++ 3 files changed, 103 insertions(+) create mode 100644 src/js/utils/MoveFormatter.ts create mode 100644 src/js/utils/format_rules/BaseFormatRule.ts create mode 100644 src/js/utils/format_rules/MenatSF5FormatRule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts new file mode 100644 index 0000000..c4b9b61 --- /dev/null +++ b/src/js/utils/MoveFormatter.ts @@ -0,0 +1,26 @@ +import BaseFormatRule from "./format_rules/BaseFormatRule"; +import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; + +export default class MoveFormatter { + rules: BaseFormatRule[]; + + constructor() { + this.rules = [ + new MenatSF5FormatRule() + ]; + } + + formatToShorthand(move: string): string { + let shorthand: string = ""; + + this.rules.forEach(rule => { + shorthand = rule.formatMove(move); + + if (shorthand !== "") { + return shorthand; + } + }); + + return null; + } +} \ No newline at end of file diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts new file mode 100644 index 0000000..ae7bcca --- /dev/null +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -0,0 +1,58 @@ +export default abstract class BaseFormatRule { + characterMoveRule: string; + strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + stanceToAbbreviationMap: Map = new Map([ + ["stand", "st."], + ["crouch", "cr."], + ["jump", "j."], + ["neutral", "nj."], + ["close", "c."], + ["far", "f."], + ["downback", "db."], + ["back", "b."] + ]); + + constructor(rule: string) { + this.characterMoveRule = rule; + } + + formatMove(move: string): string { + let moveName: string = ""; + + if (!move.includes(this.characterMoveRule)) { + return moveName; + } + + if (move.includes('(')) { + return this.formatMoveWithParenthesis(move); + } + + let stanceAbbreviation: string = this.getStanceToAbbreviation(move); + let moveInput: string[] = this.extractInput(move); + + if (moveInput.length > 1) { + moveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; + } else { + moveName = `${stanceAbbreviation}${moveInput[0]}`; + } + + return moveName; + } + + private getStanceToAbbreviation(move: string): string { + let stance = move.toLowerCase().split(' ')[0]; + return this.stanceToAbbreviationMap.get(stance); + } + + private formatMoveWithParenthesis(move: string) { + let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); + let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); + let modifierParens: string[] = splitMoveFromExtraParens.slice(1); + let stanceAbbreviation: string = this.stanceToAbbreviationMap.get(splitMove[0].toLowerCase()); + let input: string = splitMove[splitMove.length - 1].toUpperCase(); + + return `${stanceAbbreviation}${input} ${modifierParens.join(' ')}`; + } + + protected abstract extractInput(move: string): string[]; +} \ No newline at end of file diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts new file mode 100644 index 0000000..5d85698 --- /dev/null +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -0,0 +1,19 @@ +import BaseFormatRule from "./BaseFormatRule"; + +export default class MenatSF5FormatRule extends BaseFormatRule { + private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); + + constructor() { + super("orb"); + } + + protected extractInput(move: string): string[] { + let input: string[]; + let moveInput: string = move.split(' ').find(input => this.strengths.some(inputStr => inputStr === input)); + + input.push(moveInput.toUpperCase()); + input.push(this.orbLabel); + + return input; + } +} \ No newline at end of file From 6db0db6cacf189087452cf9652ed7371b7b124e0 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sun, 17 Oct 2021 12:54:52 -0400 Subject: [PATCH 32/35] add rule for young zeku, refine formatter - added the rule for young zeku - update logic for breaking out of format function in base class - add rule to MoveFormatter --- src/js/utils/MoveFormatter.ts | 4 ++- src/js/utils/format_rules/BaseFormatRule.ts | 32 ++++++++++++++++++- .../format_rules/YoungZekuSF5FormatRule.ts | 19 +++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/js/utils/format_rules/YoungZekuSF5FormatRule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index c4b9b61..28c3b0f 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -1,12 +1,14 @@ import BaseFormatRule from "./format_rules/BaseFormatRule"; import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; +import YoungZekuSF5FormatRule from "./format_rules/YoungZekuSF5FormatRule"; export default class MoveFormatter { rules: BaseFormatRule[]; constructor() { this.rules = [ - new MenatSF5FormatRule() + new MenatSF5FormatRule(), + new YoungZekuSF5FormatRule() ]; } diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index ae7bcca..98d468c 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -16,13 +16,23 @@ export default abstract class BaseFormatRule { this.characterMoveRule = rule; } + /** + * Given a move in its full-length form (i.e., Stand HP), returns the + * move in the common shorthand form of "abbreviated stance.abbreviated input", + * (i.e., st.HP) + * @param move The text representing the move as a string + * @returns A string containing the abbreviated move + */ formatMove(move: string): string { let moveName: string = ""; - if (!move.includes(this.characterMoveRule)) { + // If the move doesn't match the rule, we should break out of the method + // so the next rule can take over + if (!move.toLowerCase().includes(this.characterMoveRule.toLowerCase())) { return moveName; } + // If the move contains something like (Hold), use the regex format method if (move.includes('(')) { return this.formatMoveWithParenthesis(move); } @@ -39,11 +49,25 @@ export default abstract class BaseFormatRule { return moveName; } + /** + * This method abstracts getting the stance part of the move + * and retrieving its abbreviation from the abbreviation map + * @param move The move provided to the call to formatMove + * @returns The stance of the move in its abbreviated form + */ private getStanceToAbbreviation(move: string): string { let stance = move.toLowerCase().split(' ')[0]; return this.stanceToAbbreviationMap.get(stance); } + /** + * Given a move in its full-length form but with a trailing word + * surrounded by parenthesis (i.e., Stand HP (Hold)), returns the + * move in the common shorthand form of "abbr stance.abbr input (parenContent)" + * i.e., st.HP (Hold) + * @param move The move provided to the call to formatMove + * @returns A string containing the abbreviated move + */ private formatMoveWithParenthesis(move: string) { let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); @@ -54,5 +78,11 @@ export default abstract class BaseFormatRule { return `${stanceAbbreviation}${input} ${modifierParens.join(' ')}`; } + /** + * Given a move, extract the input from it. This method's logic will vary + * for some characters, but the default case simply retrieves the currently + * used input from the end of the string. + * @param move The move provided to the call to formatMove + */ protected abstract extractInput(move: string): string[]; } \ No newline at end of file diff --git a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts new file mode 100644 index 0000000..a2263f1 --- /dev/null +++ b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts @@ -0,0 +1,19 @@ +import BaseFormatRule from "./BaseFormatRule"; + +export default class YoungZekuSF5FormatRule extends BaseFormatRule { + constructor() { + super("late"); + } + + protected extractInput(move: string): string[] { + let input: string[]; + let splitMove = move.toLowerCase().split(' '); + let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; + + input.push(splitMove[1].toUpperCase()) + input.push(lateHit); + + return input; + } + +} \ No newline at end of file From ac279c3f1f1a1d92032079d6ec470fe079670ea6 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Mon, 3 Jan 2022 19:27:22 -0500 Subject: [PATCH 33/35] Extract formatting logic into rules engine --- src/js/utils/MoveFormatter.ts | 67 ++++++++-- src/js/utils/format_rules/BaseFormatRule.ts | 41 +++--- .../utils/format_rules/MenatSF5FormatRule.ts | 8 +- .../format_rules/YoungZekuSF5FormatRule.ts | 8 +- .../utils/format_rules/codyusf4formatrule.ts | 20 +++ .../utils/format_rules/defaultformatrule.ts | 17 +++ .../format_rules/dhalsimusf4formatrule.ts | 9 ++ src/js/utils/index.ts | 122 +----------------- 8 files changed, 141 insertions(+), 151 deletions(-) create mode 100644 src/js/utils/format_rules/codyusf4formatrule.ts create mode 100644 src/js/utils/format_rules/defaultformatrule.ts create mode 100644 src/js/utils/format_rules/dhalsimusf4formatrule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index 28c3b0f..03abb99 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -1,6 +1,8 @@ -import BaseFormatRule from "./format_rules/BaseFormatRule"; -import MenatSF5FormatRule from "./format_rules/MenatSF5FormatRule"; -import YoungZekuSF5FormatRule from "./format_rules/YoungZekuSF5FormatRule"; +import BaseFormatRule from "./format_rules/baseformatrule"; +import CodyUSF4FormatRule from "./format_rules/codyusf4formatrule"; +import DefaultFormatRule from "./format_rules/defaultformatrule"; +import MenatSF5FormatRule from "./format_rules/menatsf5formatrule"; +import YoungZekuSF5FormatRule from "./format_rules/youngzekusf5formatrule"; export default class MoveFormatter { rules: BaseFormatRule[]; @@ -8,21 +10,64 @@ export default class MoveFormatter { constructor() { this.rules = [ new MenatSF5FormatRule(), - new YoungZekuSF5FormatRule() + new YoungZekuSF5FormatRule(), + new CodyUSF4FormatRule(), + new DefaultFormatRule() ]; } - formatToShorthand(move: string): string { - let shorthand: string = ""; + formatToShorthand(moveData): string { + let shorthand: string; - this.rules.forEach(rule => { - shorthand = rule.formatMove(move); + for (const rule of this.rules) { + if (this.skipFormattingMove(moveData)) { + return ""; + } + + shorthand = rule.formatMove(moveData); - if (shorthand !== "") { + if (shorthand) { return shorthand; } - }); + } + + return ""; + } - return null; + private skipFormattingMove(moveData): boolean { + const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; + const COMMAND_NORMAL: string[] = ["3", "6"]; + const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; + const RASHID_WIND: string = "(wind)"; + const MOVE_NAME: string = moveData.moveName; + + if (!moveData.numCmd) { + if (!moveData.plnCmd) { + return true; + } + } + + if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { + return true; + } + + // Other languages have a cleaner way of representing this: if any of the values in the + // designated array is in the numCmd, just return the move name since it's a command normal + if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { + return true; + } + + // If the above check doesn't find anything, check for some other common indicators; if + // nothing comes back here, we're good and don't need to skip formatting + if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { + return true; + } + + // Rashid should be the only one (for now) to trigger this condition for his mixers + if (MOVE_NAME.includes(RASHID_WIND)) { + return true; + } + + return false; } } \ No newline at end of file diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index 98d468c..02708b0 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -12,6 +12,9 @@ export default abstract class BaseFormatRule { ["back", "b."] ]); + /** + * @param rule A word, digit, or character to use as criteria for formatting + */ constructor(rule: string) { this.characterMoveRule = rule; } @@ -19,39 +22,38 @@ export default abstract class BaseFormatRule { /** * Given a move in its full-length form (i.e., Stand HP), returns the * move in the common shorthand form of "abbreviated stance.abbreviated input", - * (i.e., st.HP) + * i.e. st.HP * @param move The text representing the move as a string * @returns A string containing the abbreviated move */ - formatMove(move: string): string { - let moveName: string = ""; + formatMove(moveData): string { + let formattedMoveName: string = ""; // If the move doesn't match the rule, we should break out of the method // so the next rule can take over - if (!move.toLowerCase().includes(this.characterMoveRule.toLowerCase())) { - return moveName; + if (!moveData.moveName.toLowerCase().includes(this.characterMoveRule.toLowerCase()) && this.characterMoveRule !== "") { + return formattedMoveName; } // If the move contains something like (Hold), use the regex format method - if (move.includes('(')) { - return this.formatMoveWithParenthesis(move); + if (moveData.moveName.includes('(')) { + return this.formatMoveWithParenthesis(moveData.moveName); } - let stanceAbbreviation: string = this.getStanceToAbbreviation(move); - let moveInput: string[] = this.extractInput(move); + let stanceAbbreviation: string = this.getStanceToAbbreviation(moveData.moveName); + let moveInput: string[] = this.extractInput(moveData); if (moveInput.length > 1) { - moveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; + formattedMoveName = `${stanceAbbreviation}${moveInput[0]} ${moveInput[1]}`; } else { - moveName = `${stanceAbbreviation}${moveInput[0]}`; + formattedMoveName = `${stanceAbbreviation}${moveInput[0]}`; } - return moveName; + return formattedMoveName; } /** - * This method abstracts getting the stance part of the move - * and retrieving its abbreviation from the abbreviation map + * Given a stance in full form (i.e., stand), returns the abbreviated version * @param move The move provided to the call to formatMove * @returns The stance of the move in its abbreviated form */ @@ -69,6 +71,15 @@ export default abstract class BaseFormatRule { * @returns A string containing the abbreviated move */ private formatMoveWithParenthesis(move: string) { + /* + Regex documentation: + Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result + The outermost parentheses start the capture group of characters we DO want to capture + The character combo of \( means that we want to find an actual opening parenthesis + [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" + Then we want to find the closing parenthesis with \) + The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression + */ let splitMoveFromExtraParens: string[] = move.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); let modifierParens: string[] = splitMoveFromExtraParens.slice(1); @@ -84,5 +95,5 @@ export default abstract class BaseFormatRule { * used input from the end of the string. * @param move The move provided to the call to formatMove */ - protected abstract extractInput(move: string): string[]; + protected abstract extractInput(moveData): string[]; } \ No newline at end of file diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts index 5d85698..b72e68a 100644 --- a/src/js/utils/format_rules/MenatSF5FormatRule.ts +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./BaseFormatRule"; +import BaseFormatRule from "./baseformatrule"; export default class MenatSF5FormatRule extends BaseFormatRule { private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); @@ -7,9 +7,9 @@ export default class MenatSF5FormatRule extends BaseFormatRule { super("orb"); } - protected extractInput(move: string): string[] { - let input: string[]; - let moveInput: string = move.split(' ').find(input => this.strengths.some(inputStr => inputStr === input)); + protected extractInput(moveData): string[] { + let input: string[] = []; + let moveInput: string = moveData.moveName.split(' ').find(x => this.strengths.some(y => y === x.toLowerCase())); input.push(moveInput.toUpperCase()); input.push(this.orbLabel); diff --git a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts index a2263f1..99fca37 100644 --- a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts +++ b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts @@ -1,13 +1,13 @@ -import BaseFormatRule from "./BaseFormatRule"; +import BaseFormatRule from "./baseformatrule"; export default class YoungZekuSF5FormatRule extends BaseFormatRule { constructor() { super("late"); } - protected extractInput(move: string): string[] { - let input: string[]; - let splitMove = move.toLowerCase().split(' '); + protected extractInput(moveData): string[] { + let input: string[] = []; + let splitMove = moveData.moveName.toLowerCase().split(' '); let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; input.push(splitMove[1].toUpperCase()) diff --git a/src/js/utils/format_rules/codyusf4formatrule.ts b/src/js/utils/format_rules/codyusf4formatrule.ts new file mode 100644 index 0000000..e7418e3 --- /dev/null +++ b/src/js/utils/format_rules/codyusf4formatrule.ts @@ -0,0 +1,20 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class CodyUSF4FormatRule extends BaseFormatRule { + private knifeLabel: string = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); + + constructor() { + super("knife"); + } + + protected extractInput(moveData): string[] { + let input: string[] = []; + let moveInput: string = moveData.moveName.split(' ').find(x => this.strengths.some(y => y === x.toLowerCase())); + + input.push(moveInput.toUpperCase()); + input.push(this.knifeLabel); + + return input; + } + +} \ No newline at end of file diff --git a/src/js/utils/format_rules/defaultformatrule.ts b/src/js/utils/format_rules/defaultformatrule.ts new file mode 100644 index 0000000..57bfd58 --- /dev/null +++ b/src/js/utils/format_rules/defaultformatrule.ts @@ -0,0 +1,17 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class DefaultFormatRule extends BaseFormatRule { + constructor() { + super(""); + } + + protected extractInput(moveData): string[] { + let input: string[] = []; + let splitMove = moveData.moveName.split(' '); + + input.push(splitMove[splitMove.length - 1].toUpperCase()); + + return input; + } + +} \ No newline at end of file diff --git a/src/js/utils/format_rules/dhalsimusf4formatrule.ts b/src/js/utils/format_rules/dhalsimusf4formatrule.ts new file mode 100644 index 0000000..4572119 --- /dev/null +++ b/src/js/utils/format_rules/dhalsimusf4formatrule.ts @@ -0,0 +1,9 @@ +import BaseFormatRule from "./baseformatrule"; + +export default class DhalsimUSF4FormatRule extends BaseFormatRule { + + protected extractInput(moveData: any): string[] { + throw new Error("Method not implemented."); + } + +} \ No newline at end of file diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index 16ce8d7..cfb809a 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,4 +1,7 @@ import { mapKeys, isEqual } from 'lodash'; +import { DataDisplaySettingsReducerState } from '../reducers/datadisplaysettings'; +import { VtState } from '../types'; +import MoveFormatter from './moveformatter'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -52,126 +55,11 @@ export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsRe return rename; } - const skipFormattingMove = (moveData) => { - const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; - const COMMAND_NORMAL: string[] = ["3", "4", "6"]; - const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; - const RASHID_WIND: string = "(wind)"; - const MOVE_NAME: string = moveData.moveName; - - if (!moveData.numCmd) { - if (!moveData.plnCmd) { - return true; - } - } - - if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { - return true; - } - - // Other languages have a cleaner way of representing this: if any of the values in the - // designated array is in the numCmd, just return the move name since it's a command normal - if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { - return true; - } - - // If the above check doesn't find anything, check for some other common indicators; if - // nothing comes back here, we're good and don't need to skip formatting - if (SYMBOLIC_CMD_NORMAL.some(indicator => moveData.plnCmd.includes(indicator))) { - return true; - } - - // Rashid should be the only one (for now) to trigger this condition for his mixers - if (MOVE_NAME.includes(RASHID_WIND)) { - return true; - } - - return false; - } - const formatMoveName = (moveData) => { - const strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; - const Y_ZEKU_LATE_HIT: string = "late"; - const MENAT_ORB: string = "orb"; - const USF4_CODY_KNIFE_NORMAL: string = "knife"; - const wordToAbbreviationMap: Map = new Map([ - ["stand", "st."], - ["crouch", "cr."], - ["jump", "j."], - ["neutral", "nj."], - ["close", "c."], - ["far", "f."], - ["downback", "db."], - ["back", "b."] - ]); - let truncatedMoveName: string = ""; + let moveFormatter = new MoveFormatter(); - const extractInput = (splitMove: string[]) => { - let input: string[] = []; - let uniqueAspect: string = ""; - let isMenatOrbNormal: boolean = splitMove.some(x => x.toLowerCase().includes(MENAT_ORB)); - let isUSF4CodyKnifeNormal: boolean = splitMove.some(x => x.toLowerCase().includes(USF4_CODY_KNIFE_NORMAL)); - - // Menat orb and USF4 Cody with knife need special attention - if (isMenatOrbNormal || isUSF4CodyKnifeNormal) { - - if (isMenatOrbNormal) { - uniqueAspect = MENAT_ORB.charAt(0).toUpperCase() + MENAT_ORB.slice(1); - } - - if (isUSF4CodyKnifeNormal) { - uniqueAspect = USF4_CODY_KNIFE_NORMAL.charAt(0).toUpperCase() + USF4_CODY_KNIFE_NORMAL.slice(1); - } - - let button: string = splitMove.find(x => strengths.some(y => y === x)); - - input.push(button.toUpperCase()); - input.push(uniqueAspect); - // Young Zeku's crouch HK also need special attention since it has a "late hit" - // state that gives it different properties and is listed as such in FAT. - } else if (splitMove.some(x => x.toLowerCase().includes(Y_ZEKU_LATE_HIT))) { - // Index 3 contains "late", index 4 contains "hit" - let lateHit: string = `${splitMove[2]} ${splitMove[3]}`; - - input.push(splitMove[1].toUpperCase()); - input.push(lateHit); - } else { - input.push(splitMove[splitMove.length - 1].toUpperCase()); - } - - return input; - } - - if (!skipFormattingMove(moveData)) { - if (!moveData.moveName.includes('(')) { - let splitMoveName: string[] = moveData.moveName.toLowerCase().split(' '); - let abbr: string = wordToAbbreviationMap.get(splitMoveName[0]); - let input: string[] = extractInput(splitMoveName); - - if (input.length > 1) { - truncatedMoveName = `${abbr}${input[0]} ${input[1]}`; - } else { - truncatedMoveName = `${abbr}${input[0]}`; - } - } else { - /* - Regex documentation: - Lead with \s to account for the leading space, i.e, " (Hold)", but we don't want to include it in the captured result - The outermost parentheses start the capture group of characters we DO want to capture - The character combo of \( means that we want to find an actual opening parenthesis - [a-z\s]* = Within the parenthesis, we want to find any combination of letters and spaces to account for cases like "(crouch large)" - Then we want to find the closing parenthesis with \) - The capture group is closed, and the "i" at the end sets a "case insensitive" flag for the regex expression - */ - let splitMoveFromExtraParens: string[] = moveData.moveName.split(/\s(\([a-z\s]*\))/i).filter((x: string) => x !== ""); - let splitMove: string[] = splitMoveFromExtraParens[0].split(' '); - let modifierParens: string[] = splitMoveFromExtraParens.slice(1); - let abbr: string = wordToAbbreviationMap.get(splitMove[0].toLowerCase()); - let input: string = splitMove[splitMove.length - 1].toUpperCase(); - truncatedMoveName = `${abbr}${input} ${modifierParens.join(' ')}`; - } - } + truncatedMoveName = moveFormatter.formatToShorthand(moveData); return truncatedMoveName; } From 168feb974a223e8742538063e23e790c6e32acc3 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Fri, 18 Feb 2022 22:56:03 -0500 Subject: [PATCH 34/35] Add final tweaks & rules for SF4 and Third Strike --- src/js/utils/MoveFormatter.ts | 35 ++++++++++++++++--- src/js/utils/format_rules/BaseFormatRule.ts | 12 ++++--- .../utils/format_rules/MenatSF5FormatRule.ts | 1 + .../utils/format_rules/codyusf4formatrule.ts | 1 + .../utils/format_rules/defaultformatrule.ts | 2 +- .../format_rules/dhalsimusf4formatrule.ts | 9 ----- 6 files changed, 40 insertions(+), 20 deletions(-) delete mode 100644 src/js/utils/format_rules/dhalsimusf4formatrule.ts diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index 03abb99..fc9a9d4 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -5,7 +5,7 @@ import MenatSF5FormatRule from "./format_rules/menatsf5formatrule"; import YoungZekuSF5FormatRule from "./format_rules/youngzekusf5formatrule"; export default class MoveFormatter { - rules: BaseFormatRule[]; + private rules: BaseFormatRule[]; constructor() { this.rules = [ @@ -34,11 +34,24 @@ export default class MoveFormatter { return ""; } + /** + * Skips character moves that meet various criteria in order to focus on applying + * formatting to normals. + * @remarks Some move types like command normals and others will sometimes get caught by the + * formatter engine because of their attributes in their JSON object. + * @param moveData The current move and its attributes as a JSON object + * @returns true if the move should not have formatting applied to it, false otherwise + */ private skipFormattingMove(moveData): boolean { const TARGET_COMBO: string[] = ["(TC)", "Target Combo"]; const COMMAND_NORMAL: string[] = ["3", "6"]; const SYMBOLIC_CMD_NORMAL: string[] = [">", "(air)", "(run)", "(lvl"]; const RASHID_WIND: string = "(wind)"; + const IGNORED_THIRD_STRIKE_MOVES: string[] = [ + "Kakushuu Rakukyaku" /* Chun-li b.MK (Hold) */, + "Inazuma Kakato Wari (Long)" /* Ken b.MK (Hold) */, + "Elbow Cannon" /* Necro db.HP */ + ]; const MOVE_NAME: string = moveData.moveName; if (!moveData.numCmd) { @@ -46,15 +59,15 @@ export default class MoveFormatter { return true; } } - + + // Do not attempt to apply formatting to target combos if (TARGET_COMBO.some(indicator => MOVE_NAME.includes(indicator))) { return true; } - // Other languages have a cleaner way of representing this: if any of the values in the - // designated array is in the numCmd, just return the move name since it's a command normal + // Do not attempt to apply formatting to command normals if (COMMAND_NORMAL.some(indicator => moveData.numCmd.includes(indicator))) { - return true; + return true; } // If the above check doesn't find anything, check for some other common indicators; if @@ -67,6 +80,18 @@ export default class MoveFormatter { if (MOVE_NAME.includes(RASHID_WIND)) { return true; } + + // For USF4, if the move motion is "back" but the move name doesn't include back, skip it + if (moveData.moveMotion === "B" && !MOVE_NAME.includes("Back")) { + return true; + } + + // There are three awkward moves that get caught by the formatting engine + // for the 3S data. If the 3S data is ever cleaned up, this could be removed + // or refactored + if (IGNORED_THIRD_STRIKE_MOVES.some(indicator => MOVE_NAME === indicator)) { + return true; + } return false; } diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index 02708b0..47c47c8 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -1,11 +1,11 @@ export default abstract class BaseFormatRule { - characterMoveRule: string; - strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; - stanceToAbbreviationMap: Map = new Map([ + protected characterMoveRule: string; + protected strengths: string[] = ["lp", "mp", "hp", "lk", "mk", "hk"]; + protected stanceToAbbreviationMap: Map = new Map([ ["stand", "st."], ["crouch", "cr."], ["jump", "j."], - ["neutral", "nj."], + ["neutral jump", "nj."], ["close", "c."], ["far", "f."], ["downback", "db."], @@ -58,7 +58,9 @@ export default abstract class BaseFormatRule { * @returns The stance of the move in its abbreviated form */ private getStanceToAbbreviation(move: string): string { - let stance = move.toLowerCase().split(' ')[0]; + let splitMove: string[] = move.trim().toLowerCase().split(' '); + let stance: string = splitMove[0]; + return this.stanceToAbbreviationMap.get(stance); } diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts index b72e68a..71a8075 100644 --- a/src/js/utils/format_rules/MenatSF5FormatRule.ts +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -1,6 +1,7 @@ import BaseFormatRule from "./baseformatrule"; export default class MenatSF5FormatRule extends BaseFormatRule { + // Sentence-casing for the "Orb" label private orbLabel = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); constructor() { diff --git a/src/js/utils/format_rules/codyusf4formatrule.ts b/src/js/utils/format_rules/codyusf4formatrule.ts index e7418e3..48b4e35 100644 --- a/src/js/utils/format_rules/codyusf4formatrule.ts +++ b/src/js/utils/format_rules/codyusf4formatrule.ts @@ -1,6 +1,7 @@ import BaseFormatRule from "./baseformatrule"; export default class CodyUSF4FormatRule extends BaseFormatRule { + // Sentence-casing for the "Knife" label private knifeLabel: string = this.characterMoveRule.charAt(0).toUpperCase() + this.characterMoveRule.slice(1); constructor() { diff --git a/src/js/utils/format_rules/defaultformatrule.ts b/src/js/utils/format_rules/defaultformatrule.ts index 57bfd58..b404180 100644 --- a/src/js/utils/format_rules/defaultformatrule.ts +++ b/src/js/utils/format_rules/defaultformatrule.ts @@ -7,7 +7,7 @@ export default class DefaultFormatRule extends BaseFormatRule { protected extractInput(moveData): string[] { let input: string[] = []; - let splitMove = moveData.moveName.split(' '); + let splitMove = moveData.moveName.trim().split(' '); input.push(splitMove[splitMove.length - 1].toUpperCase()); diff --git a/src/js/utils/format_rules/dhalsimusf4formatrule.ts b/src/js/utils/format_rules/dhalsimusf4formatrule.ts deleted file mode 100644 index 4572119..0000000 --- a/src/js/utils/format_rules/dhalsimusf4formatrule.ts +++ /dev/null @@ -1,9 +0,0 @@ -import BaseFormatRule from "./baseformatrule"; - -export default class DhalsimUSF4FormatRule extends BaseFormatRule { - - protected extractInput(moveData: any): string[] { - throw new Error("Method not implemented."); - } - -} \ No newline at end of file From 2b7c14ccd4c1098047a181058fdedc80c0f77423 Mon Sep 17 00:00:00 2001 From: Jonathan Markman Date: Sun, 8 May 2022 00:49:27 -0400 Subject: [PATCH 35/35] Address pull request feedback and refactor - Simplified if/else chain in renameFrameDataToShorthand - Update setPlayer and helpCreateFrameDataJSON params so we can check which game is currently active - Standardize capitalization in format rule classes - Update stance to abbreviation map --- src/js/actions/index.ts | 4 +- src/js/utils/MoveFormatter.ts | 10 ++-- src/js/utils/format_rules/BaseFormatRule.ts | 4 +- .../utils/format_rules/MenatSF5FormatRule.ts | 2 +- .../format_rules/YoungZekuSF5FormatRule.ts | 2 +- .../utils/format_rules/codyusf4formatrule.ts | 2 +- .../utils/format_rules/defaultformatrule.ts | 2 +- src/js/utils/index.ts | 56 +++++++++---------- 8 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/js/actions/index.ts b/src/js/actions/index.ts index d28464b..b3a0a72 100644 --- a/src/js/actions/index.ts +++ b/src/js/actions/index.ts @@ -128,9 +128,9 @@ export const setPlayer = (playerId: PlayerId, charName: PlayerData["name"]) => : "normal" const playerData: PlayerData = { name: charName, - frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState, vTriggerStateToSet), + frameData: helpCreateFrameDataJSON(frameDataState[charName].moves, dataDisplaySettingsState, stateToSet, activeGameState), stats: frameDataState[charName].stats, - vtState: vTriggerStateToSet, + vtState: stateToSet, } dispatch({ type: 'SET_PLAYER', diff --git a/src/js/utils/MoveFormatter.ts b/src/js/utils/MoveFormatter.ts index fc9a9d4..83841f4 100644 --- a/src/js/utils/MoveFormatter.ts +++ b/src/js/utils/MoveFormatter.ts @@ -1,8 +1,8 @@ -import BaseFormatRule from "./format_rules/baseformatrule"; -import CodyUSF4FormatRule from "./format_rules/codyusf4formatrule"; -import DefaultFormatRule from "./format_rules/defaultformatrule"; -import MenatSF5FormatRule from "./format_rules/menatsf5formatrule"; -import YoungZekuSF5FormatRule from "./format_rules/youngzekusf5formatrule"; +import BaseFormatRule from "./format_rules/BaseFormatRule"; +import CodyUSF4FormatRule from "./format_rules/CodyUsf4FormatRule"; +import DefaultFormatRule from "./format_rules/DefaultFormatRule"; +import MenatSF5FormatRule from "./format_rules/MenatSf5FormatRule"; +import YoungZekuSF5FormatRule from "./format_rules/YoungZekuSf5FormatRule"; export default class MoveFormatter { private rules: BaseFormatRule[]; diff --git a/src/js/utils/format_rules/BaseFormatRule.ts b/src/js/utils/format_rules/BaseFormatRule.ts index 47c47c8..a71f09d 100644 --- a/src/js/utils/format_rules/BaseFormatRule.ts +++ b/src/js/utils/format_rules/BaseFormatRule.ts @@ -5,8 +5,8 @@ export default abstract class BaseFormatRule { ["stand", "st."], ["crouch", "cr."], ["jump", "j."], - ["neutral jump", "nj."], - ["close", "c."], + ["neutral", "nj."], + ["close", "cl."], ["far", "f."], ["downback", "db."], ["back", "b."] diff --git a/src/js/utils/format_rules/MenatSF5FormatRule.ts b/src/js/utils/format_rules/MenatSF5FormatRule.ts index 71a8075..445ac85 100644 --- a/src/js/utils/format_rules/MenatSF5FormatRule.ts +++ b/src/js/utils/format_rules/MenatSF5FormatRule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./baseformatrule"; +import BaseFormatRule from "./BaseFormatRule"; export default class MenatSF5FormatRule extends BaseFormatRule { // Sentence-casing for the "Orb" label diff --git a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts index 99fca37..7f99c5e 100644 --- a/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts +++ b/src/js/utils/format_rules/YoungZekuSF5FormatRule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./baseformatrule"; +import BaseFormatRule from "./BaseFormatRule"; export default class YoungZekuSF5FormatRule extends BaseFormatRule { constructor() { diff --git a/src/js/utils/format_rules/codyusf4formatrule.ts b/src/js/utils/format_rules/codyusf4formatrule.ts index 48b4e35..5a91838 100644 --- a/src/js/utils/format_rules/codyusf4formatrule.ts +++ b/src/js/utils/format_rules/codyusf4formatrule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./baseformatrule"; +import BaseFormatRule from "./BaseFormatRule"; export default class CodyUSF4FormatRule extends BaseFormatRule { // Sentence-casing for the "Knife" label diff --git a/src/js/utils/format_rules/defaultformatrule.ts b/src/js/utils/format_rules/defaultformatrule.ts index b404180..208edbe 100644 --- a/src/js/utils/format_rules/defaultformatrule.ts +++ b/src/js/utils/format_rules/defaultformatrule.ts @@ -1,4 +1,4 @@ -import BaseFormatRule from "./baseformatrule"; +import BaseFormatRule from "./BaseFormatRule"; export default class DefaultFormatRule extends BaseFormatRule { constructor() { diff --git a/src/js/utils/index.ts b/src/js/utils/index.ts index cfb809a..f224707 100644 --- a/src/js/utils/index.ts +++ b/src/js/utils/index.ts @@ -1,7 +1,7 @@ import { mapKeys, isEqual } from 'lodash'; import { DataDisplaySettingsReducerState } from '../reducers/datadisplaysettings'; import { VtState } from '../types'; -import MoveFormatter from './moveformatter'; +import MoveFormatter from './MoveFormatter'; /** * Renames the moves in the character frame data to reflect the user's desired naming convention @@ -9,46 +9,46 @@ import MoveFormatter from './moveformatter'; * @param {DataDisplaySettingsReducerState} dataDisplayState The Redux state containing various move text render settings * @returns The frame data JSON object with renamed moves */ -export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState) { +export function renameData(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, activeGame: string) { const renameFrameData = (rawData, renameKey, notationDisplay) => { switch (notationDisplay) { case "fullWord": return mapKeys(rawData, (moveValue, moveKey) => moveValue[renameKey] ? moveValue[renameKey] : moveKey); case "shorthand": - return renameFrameDataToShorthand(rawData, renameKey); + return renameFrameDataToShorthand(rawData, renameKey, activeGame); default: break; } } - const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string) => { - const HELD_NORMAL: string = "Held Normal"; + const renameFrameDataToShorthand = (rawData: string, nameTypeKey: string, activeGame: string) => { + const MOVE_TYPE_HELD_NORMAL: string = "held normal"; + const MOVE_TYPE_NORMAL: string = "normal"; + const GUILTY_GEAR_STRIVE: string = "ggst"; + let rename = mapKeys(rawData, (moveValue, moveKey) => { - const DEFAULT_MOVE_NAME = () => { - if (moveValue[nameTypeKey]) { - return moveValue[nameTypeKey]; - } else { - return moveKey; - } + let activeGameIsGuiltyGearStrive: boolean = activeGame.toLowerCase() === GUILTY_GEAR_STRIVE; + let defaultMoveName: string = moveValue[nameTypeKey] ? moveValue[nameTypeKey] : moveKey; + let moveDataHasMovesList: boolean = Boolean(moveValue.movesList); + + // Conditional expressions are used here because checking moveValue.moveType for a truthy value + // is how JavaScript/TypeScript can simultaneously check if a string is empty, null, or undefined + let moveIsNormal: boolean = moveValue.moveType ? moveValue.moveType.toLowerCase() === MOVE_TYPE_NORMAL : false; + let moveIsHeldNormal: boolean = moveValue.moveType ? moveValue.moveType.toLowerCase() === MOVE_TYPE_HELD_NORMAL : false; + + if (activeGameIsGuiltyGearStrive) { + return defaultMoveName; } - - if ((moveValue.moveType === "normal")) { - if (moveValue.movesList) { - if (moveValue.movesList === HELD_NORMAL) { - return formatMoveName(moveValue); - } else { - return DEFAULT_MOVE_NAME(); - } + + if (moveIsNormal) { + if (moveDataHasMovesList) { + return moveIsHeldNormal ? formatMoveName(moveValue) : defaultMoveName; } else { - let formatted: string = formatMoveName(moveValue); - if (formatted !== "") { - return formatted; - } else { - return DEFAULT_MOVE_NAME(); - } + let formattedMove: string = formatMoveName(moveValue); + return formattedMove !== "" ? formattedMove : defaultMoveName; } } else { - return DEFAULT_MOVE_NAME(); + return defaultMoveName; } }); @@ -114,9 +114,9 @@ function vTriggerMerge(rawFrameData, vtState) { } // this allow me to build the JSON for the setPlayer action creator in selectCharacter, SegmentSwitcher and ____ componenet -export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState) { +export function helpCreateFrameDataJSON(rawFrameData, dataDisplayState: DataDisplaySettingsReducerState, vtState: VtState, activeGame: string) { const dataToRename = (vtState === "normal") ? rawFrameData.normal : vTriggerMerge(rawFrameData, vtState); - return renameData(dataToRename, dataDisplayState); + return renameData(dataToRename, dataDisplayState, activeGame); } \ No newline at end of file