diff --git a/.all-contributorsrc b/.all-contributorsrc index bd2db9f..e758acc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -130,6 +130,15 @@ "code", "ideas" ] + }, + { + "login": "kevin8181", + "name": "kevin8181", + "avatar_url": "https://avatars.githubusercontent.com/u/66894759?v=4", + "profile": "https://github.com/kevin8181", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/src/GradeScale.ts b/src/GradeScale.ts index 098b4a9..63f51aa 100644 --- a/src/GradeScale.ts +++ b/src/GradeScale.ts @@ -11,7 +11,7 @@ export default interface GradeScale { name: GradeScalesTypes offset: number conversionGroup: ConversionGroupsTypes - grades?: string[] + grades: string[] } export const GradeScales = { diff --git a/src/index.ts b/src/index.ts index a900ae2..a0cec2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,3 @@ - import { GradeScales, GradeScalesTypes } from './GradeScale' import { getScale, @@ -8,7 +7,20 @@ import { convertGrade } from './GradeParser' import { GradeBands, GradeBandTypes } from './GradeBands' -import { AI, Aid, Ewbank, Font, French, Norwegian, Saxon, UIAA, VScale, WI, YosemiteDecimal, BrazilianCrux } from './scales' +import { + AI, + Aid, + Ewbank, + Font, + French, + Norwegian, + Saxon, + UIAA, + VScale, + WI, + YosemiteDecimal, + BrazilianCrux +} from './scales' // Free Climbing Grades // YDS @@ -23,49 +35,13 @@ import { AI, Aid, Ewbank, Font, French, Norwegian, Saxon, UIAA, VScale, WI, Yose // Brazilian // Kurtyka (Poland) -const YDS_ARRAY = [ - '5.0', - '5.1', - '5.2', - '5.3', - '5.4', - '5.5', - '5.6', - '5.7', - '5.8', - '5.9', - '5.10a', - '5.10b', - '5.10c', - '5.10d', - '5.11a', - '5.11b', - '5.11c', - '5.11d', - '5.12a', - '5.12b', - '5.12c', - '5.12d', - '5.13a', - '5.13b', - '5.13c', - '5.13d', - '5.14a', - '5.14b', - '5.14c', - '5.14d', - '5.15a', - '5.15b', - '5.15c', - '5.15d' -] - -const SAXON_ARRAY = [ - '1', '2', '3', '4', '5', '6', '7a', - '7b', '7c', '8a', '8b', '8c', '9a', - '9b', '9c', '10a', '10b', '10c', '11a', - '11b', '11c', '12a', '12b' -] +const CLASS_ARRAY = [ + 'Class 1', + 'Class 2', + 'Class 3', + 'Class 4', + 'Class 5' +] as const const BRITISH_TECH_ARRAY = [ '1', @@ -82,7 +58,7 @@ const BRITISH_TECH_ARRAY = [ '6c', '7a', '7b' -] +] as const const BRITISH_ADJ_ARRAY = [ 'M', @@ -102,6 +78,43 @@ const BRITISH_ADJ_ARRAY = [ 'E9', 'E10', 'E11' +] as const + +const YDS_ARRAY = [ + '5.0', + '5.1', + '5.2', + '5.3', + '5.4', + '5.5', + '5.6', + '5.7', + '5.8', + '5.9', + '5.10a', + '5.10b', + '5.10c', + '5.10d', + '5.11a', + '5.11b', + '5.11c', + '5.11d', + '5.12a', + '5.12b', + '5.12c', + '5.12d', + '5.13a', + '5.13b', + '5.13c', + '5.13d', + '5.14a', + '5.14b', + '5.14c', + '5.14d', + '5.15a', + '5.15b', + '5.15c', + '5.15d' ] const FRENCH_ARRAY = [ @@ -222,6 +235,32 @@ const EWBANK_ARRAY = [ '40' ] +const SAXON_ARRAY = [ + '1', + '2', + '3', + '4', + '5', + '6', + '7a', + '7b', + '7c', + '8a', + '8b', + '8c', + '9a', + '9b', + '9c', + '10a', + '10b', + '10c', + '11a', + '11b', + '11c', + '12a', + '12b' +] + const NORWAY_ARRAY = [ '1-', '1', @@ -261,20 +300,16 @@ const NORWAY_ARRAY = [ '12+' ] -const CLASS_ARRAY = ['Class 1', 'Class 2', 'Class 3', 'Class 4', 'Class 5'] - -export const protection = ['G', 'PG', 'PG13', 'R', 'X'] - -// Bouldering -// Hueco -// Fontainebleau - +/** + * @deprecated - grades can now be accessed as a property of each GradeScale + */ export const freeClimbing = { clean: { - yds: YDS_ARRAY, class: CLASS_ARRAY, britishTech: BRITISH_TECH_ARRAY, britishAdj: BRITISH_ADJ_ARRAY, + + yds: YDS_ARRAY, French: FRENCH_ARRAY, UIAA: UIAA_ARRAY, Ewbank: EWBANK_ARRAY, @@ -285,7 +320,7 @@ export const freeClimbing = { community: {} } -export const bouldering = {} +export const protection = ['G', 'PG', 'PG13', 'R', 'X'] as const export { convertGrade } diff --git a/src/scales/ai.ts b/src/scales/ai.ts index 09f34be..015d39d 100644 --- a/src/scales/ai.ts +++ b/src/scales/ai.ts @@ -3,6 +3,8 @@ import ice_table from '../data/ice.json' import { IceGrade } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const AI_ARRAY = Array.from(new Set(ice_table.map((r) => r.ai))) + // Supports AI1 -> AI13, aith + grades on AI3 -> AI13 and no slash grades // https://en.aikipedia.org/aiki/Grade_(climbing)#Ice_and_mixed_climbing const aiGradeRegex = /^(AI)([1-2]|[3-9]\+?|1[0-3]\+?)$/ @@ -12,6 +14,7 @@ const isAI = (grade: string): RegExpMatchArray | null => grade.match(aiGradeRege const AIScale: GradeScale = { displayName: 'AI Grade', name: GradeScales.AI, + grades: AI_ARRAY, offset: 1000, conversionGroup: ConversionGroups.ICE, isType: (grade: string): boolean => { diff --git a/src/scales/aid.ts b/src/scales/aid.ts index 4e70ff7..79f01ef 100644 --- a/src/scales/aid.ts +++ b/src/scales/aid.ts @@ -3,6 +3,8 @@ import aid_table from '../data/aid.json' import { AidGrade } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const AID_ARRAY = Array.from(new Set(aid_table.map((r) => r.aid))) + // Supports [AC]0 -> [AC]5, with + grades on [AC]2 -> [AC]4 and no slash grades // https://en.wikipedia.org/wiki/Grade_(climbing)#Clean_scale const aidGradeRegex = /^([AC])([0-5]|[2-4]\+)$/i @@ -11,6 +13,7 @@ const isAid = (grade: string): RegExpMatchArray | null => grade.match(aidGradeRe const AidScale: GradeScale = { displayName: 'Aid Grade', name: GradeScales.AID, + grades: AID_ARRAY, offset: 1000, conversionGroup: ConversionGroups.AID, isType: (grade: string): boolean => { diff --git a/src/scales/brazilian.ts b/src/scales/brazilian.ts index 9924177..a7062ff 100644 --- a/src/scales/brazilian.ts +++ b/src/scales/brazilian.ts @@ -67,9 +67,9 @@ const getScore = (grade: string): number | Tuple => { // https://www.cap.com.br/post/sistema-brasileiro-de-gradua%C3%A7%C3%A3o-de-vias-de-escalada // alt mirror: https://web.archive.org/web/20220125022634/https://www.cap.com.br/post/sistema-brasileiro-de-gradua%C3%A7%C3%A3o-de-vias-de-escalada const BrazilianCrux: GradeScale = { - grades: BRAZILIAN_ARRAY, displayName: 'Brazilian Crux Scale', name: GradeScales.BRAZILIAN_CRUX, + grades: BRAZILIAN_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => isBrazilianCrux(grade) !== null, diff --git a/src/scales/ewbank.ts b/src/scales/ewbank.ts index 3743be9..0ecc911 100644 --- a/src/scales/ewbank.ts +++ b/src/scales/ewbank.ts @@ -3,6 +3,8 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const EWBANK_ARRAY = Array.from(new Set(routes.map((r) => r[GradeScales.EWBANK]))) + // Supports 1 -> 40, slash grades i.e. 25/26 // NOTE: this currently assumes "incorrect" slash grades follows the normal pattern // i.e. 26/35 => 26/27 @@ -14,6 +16,7 @@ const isEwbank = (grade: string): RegExpMatchArray | null => grade.match(ewbankG const EwbankScale: GradeScale = { displayName: 'Ewbank Grade', name: GradeScales.EWBANK, + grades: EWBANK_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => { diff --git a/src/scales/font.ts b/src/scales/font.ts index 9f64404..e395c00 100644 --- a/src/scales/font.ts +++ b/src/scales/font.ts @@ -4,6 +4,8 @@ import GradeScale, { findScoreRange, getAvgScore, GradeScales, ConversionGroups, import { Boulder } from '.' import { boulderScoreToBand, GradeBandTypes } from '../GradeBands' +const FONT_ARRAY = Array.from(new Set(boulder.map((b) => b.font))) + const fontGradeRegex = /^([1-9][a-c][+]?){1}(?:(\/)([1-9][a-c][+]?))?$/i // Supports 1a -> 9c+, slash grades i.e. 5a/5a+ or 6a+/6b // NOTE: this currently assumes "incorrect" slash grades follows the normal pattern @@ -13,6 +15,7 @@ const isFont = (grade: string): RegExpMatchArray | null => grade.match(fontGrade const FontScale: GradeScale = { displayName: 'Fontainebleau', name: GradeScales.FONT, + grades: FONT_ARRAY, offset: 1000, conversionGroup: ConversionGroups.BOULDERING, isType: (grade: string): boolean => { diff --git a/src/scales/french.ts b/src/scales/french.ts index 0a92f6d..bc2adf6 100644 --- a/src/scales/french.ts +++ b/src/scales/french.ts @@ -3,6 +3,8 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const FRENCH_ARRAY = Array.from(new Set(routes.map((r) => r[GradeScales.FRENCH]))) + const frenchGradeRegex = /^([1-9][a-c][+]?){1}(?:(\/)([1-9][a-c][+]?))?$/i // Supports 1a -> 9c+, slash grades i.e. 5a/5a+ or 6a+/6b // NOTE: this currently assumes "incorrect" slash grades follows the normal pattern @@ -12,6 +14,7 @@ const isFrench = (grade: string): RegExpMatchArray | null => grade.match(frenchG const FrenchScale: GradeScale = { displayName: 'French Scale', name: GradeScales.FRENCH, + grades: FRENCH_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => { diff --git a/src/scales/norwegian.ts b/src/scales/norwegian.ts index a9691ee..4086bc1 100644 --- a/src/scales/norwegian.ts +++ b/src/scales/norwegian.ts @@ -3,6 +3,8 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const NORWEGIAN_ARRAY = Array.from(new Set(routes.map((r) => r[GradeScales.NORWEGIAN]))) + // Supports 1- -> 11+, slash grades i.e. 6-/6 or 7+/8- // NOTE: this currently assumes "incorrect" slash grades follow the normal pattern // i.e. 6-/5 => 6-/6 @@ -12,6 +14,7 @@ const isNorwegian = (grade: string): RegExpMatchArray | null => grade.match(norw const Norwegian: GradeScale = { displayName: 'Norwegian Scale', name: GradeScales.NORWEGIAN, + grades: NORWEGIAN_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => { diff --git a/src/scales/saxon.ts b/src/scales/saxon.ts index b7c3a13..10df969 100644 --- a/src/scales/saxon.ts +++ b/src/scales/saxon.ts @@ -3,6 +3,8 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const SAXON_ARRAY = Array.from(new Set(routes.map((r) => r[GradeScales.SAXON]))) + const saxonGradeRegex = /^((([7-9]|1[0-3])([a-c]))|([1-6]))$/i // Saxon grading system, predominant in Central Europe (esp. Germany, Austria, Switzerland) // Supports 1 -> 13c, slash grades i.e. 7a/7b @@ -13,6 +15,7 @@ const isSaxon = (grade: string): RegExpMatchArray | null => grade.match(saxonGra const SaxonScale: GradeScale = { displayName: 'Saxon Scale', name: GradeScales.SAXON, + grades: SAXON_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => { diff --git a/src/scales/uiaa.ts b/src/scales/uiaa.ts index 083d7ff..91c6132 100644 --- a/src/scales/uiaa.ts +++ b/src/scales/uiaa.ts @@ -3,6 +3,8 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const UIAA_ARRAY = Array.from(new Set(routes.map((r) => r[GradeScales.UIAA]))) + const uiaaGradeRegex = /^(\d{1,2}[+-]?\/?\d?[+-]?)$/ const isUIAA = (grade: string): RegExpMatchArray | null => grade.match(uiaaGradeRegex) @@ -13,6 +15,7 @@ const isUIAA = (grade: string): RegExpMatchArray | null => grade.match(uiaaGrade const UIAAScale: GradeScale = { displayName: 'UIAA Scale', name: GradeScales.UIAA, + grades: UIAA_ARRAY, offset: 2000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => { diff --git a/src/scales/v.ts b/src/scales/v.ts index 430c7f6..0f455f8 100644 --- a/src/scales/v.ts +++ b/src/scales/v.ts @@ -3,11 +3,14 @@ import boulder from '../data/boulder.json' import { Boulder } from '.' import { boulderScoreToBand, GradeBandTypes } from '../GradeBands' +const V_ARRAY = Array.from(new Set(boulder.map((b) => b.v))) + const vGradeRegex = /^(V[0-9]{1,2}|VB(?![0-9]))([/+])?([/-])?([0-9]{1,2})?$/i const VScale: GradeScale = { displayName: 'V Scale', name: GradeScales.VSCALE, + grades: V_ARRAY, offset: 1000, conversionGroup: ConversionGroups.BOULDERING, isType: (grade: string): boolean => { diff --git a/src/scales/wi.ts b/src/scales/wi.ts index 604025b..3aaf280 100644 --- a/src/scales/wi.ts +++ b/src/scales/wi.ts @@ -3,6 +3,8 @@ import ice_table from '../data/ice.json' import { IceGrade } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const WI_ARRAY = Array.from(new Set(ice_table.map((r) => r.wi))) + // Supports WI1 -> WI13, with + grades on WI3 -> WI13 and no slash grades // https://en.wikipedia.org/wiki/Grade_(climbing)#Ice_and_mixed_climbing const wiGradeRegex = /^(WI)([1-2]|[3-9]\+?|1[0-3]\+?)$/ @@ -12,6 +14,7 @@ const isWI = (grade: string): RegExpMatchArray | null => grade.match(wiGradeRege const WIScale: GradeScale = { displayName: 'WI Grade', name: GradeScales.WI, + grades: WI_ARRAY, offset: 1000, conversionGroup: ConversionGroups.ICE, isType: (grade: string): boolean => { diff --git a/src/scales/yds.ts b/src/scales/yds.ts index 7b810b3..67994a6 100644 --- a/src/scales/yds.ts +++ b/src/scales/yds.ts @@ -3,6 +3,10 @@ import routes from '../data/routes.json' import { Route } from '.' import { GradeBandTypes, routeScoreToBand } from '../GradeBands' +const YDS_ARRAY = Array.from( + new Set(routes.map((r) => r[GradeScales.YDS])) +) + const REGEX_5_X = /(^5\.([0-9]|1[0-6]))()([+-])?$/i // Support 5.0 to 5.16 with + and - const REGEX_5_10_LETTER = /(^5\.(1[0-6]))([abcd])(\/[abcd])?$/i @@ -18,6 +22,7 @@ const isYds = (grade: string): RegExpMatchArray | null => const YosemiteDecimal: GradeScale = { displayName: 'Yosemite Decimal System', name: GradeScales.YDS, + grades: YDS_ARRAY, offset: 1000, conversionGroup: ConversionGroups.FREE, isType: (grade: string): boolean => {