Skip to content

Commit

Permalink
feat: US aid grades
Browse files Browse the repository at this point in the history
Add a scale for aid grades.

It treats A# and C# as being part of the same scale, so that different
grade contexts do not need to be assigned to neighbouring routes, e.g.
if there is a grade with grade A2 next to a route with grade C1.

Aid + mandatory free is not covered by this commit.

Resolves: OpenBeta#79
  • Loading branch information
musoke committed Apr 24, 2023
1 parent e153d97 commit 31a9f1f
Show file tree
Hide file tree
Showing 8 changed files with 411 additions and 5 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

Javascript utilities for working with rock climbing grades.

### Supported systems
### Supported systems

**Sport & Traditional climbing**
- [x] Yosemite Decimal System
Expand All @@ -18,6 +18,10 @@ Javascript utilities for working with rock climbing grades.
- [x] Vermin (V-scale)
- [x] Fontainebleau

**Aid**
- [x] A# & C#
- [] Aid with mandatory free climbing (5.8 A0, etc)

### Help Wanted

Code contributions are most welcome!
Expand Down
3 changes: 2 additions & 1 deletion src/GradeScale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export const GradeScales = {
FONT: 'font',
FRENCH: 'french',
UIAA: 'uiaa',
EWBANK: 'ewbank'
EWBANK: 'ewbank',
AID: 'aid'
} as const

export type GradeScalesTypes = typeof GradeScales[keyof typeof GradeScales]
Expand Down
37 changes: 37 additions & 0 deletions src/data/aid.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
Score,Aid
0,A0
1,A0
2,A0
3,A0
4,A1
5,A1
6,A1
7,A1
8,A2
9,A2
10,A2
11,A2
12,A2+
13,A2+
14,A2+
15,A2+
16,A3
17,A3
18,A3
19,A3
20,A3+
21,A3+
22,A3+
23,A3+
24,A4
25,A4
26,A4
27,A4
28,A4+
29,A4+
30,A4+
31,A4+
32,A5
33,A5
34,A5
35,A5
146 changes: 146 additions & 0 deletions src/data/aid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
[
{
"score": 0,
"aid": "A0"
},
{
"score": 1,
"aid": "A0"
},
{
"score": 2,
"aid": "A0"
},
{
"score": 3,
"aid": "A0"
},
{
"score": 4,
"aid": "A1"
},
{
"score": 5,
"aid": "A1"
},
{
"score": 6,
"aid": "A1"
},
{
"score": 7,
"aid": "A1"
},
{
"score": 8,
"aid": "A2"
},
{
"score": 9,
"aid": "A2"
},
{
"score": 10,
"aid": "A2"
},
{
"score": 11,
"aid": "A2"
},
{
"score": 12,
"aid": "A2+"
},
{
"score": 13,
"aid": "A2+"
},
{
"score": 14,
"aid": "A2+"
},
{
"score": 15,
"aid": "A2+"
},
{
"score": 16,
"aid": "A3"
},
{
"score": 17,
"aid": "A3"
},
{
"score": 18,
"aid": "A3"
},
{
"score": 19,
"aid": "A3"
},
{
"score": 20,
"aid": "A3+"
},
{
"score": 21,
"aid": "A3+"
},
{
"score": 22,
"aid": "A3+"
},
{
"score": 23,
"aid": "A3+"
},
{
"score": 24,
"aid": "A4"
},
{
"score": 25,
"aid": "A4"
},
{
"score": 26,
"aid": "A4"
},
{
"score": 27,
"aid": "A4"
},
{
"score": 28,
"aid": "A4+"
},
{
"score": 29,
"aid": "A4+"
},
{
"score": 30,
"aid": "A4+"
},
{
"score": 31,
"aid": "A4+"
},
{
"score": 32,
"aid": "A5"
},
{
"score": 33,
"aid": "A5"
},
{
"score": 34,
"aid": "A5"
},
{
"score": 35,
"aid": "A5"
}
]
19 changes: 18 additions & 1 deletion src/data/csvtojson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

import csv from 'csv-parser'
import * as fs from 'fs'
import { Boulder, Route } from '../scales'
import { Boulder, Route, Aid } from '../scales'

const boulderGrades: Boulder[] = []
const routeGrades: Route[] = []
const aidGrades: Aid[] = []

fs.createReadStream('./boulder.csv')
.pipe(csv())
Expand Down Expand Up @@ -53,3 +54,19 @@ fs.createReadStream('./routes.csv')
// { score: 27, yds: '5.10a', french: '3b', band: 'beginner' },
// ]
})

fs.createReadStream('./aid.csv')
.pipe(csv())
.on('data', (data) => {
if (data.Aid === '' && data.Aid === '') {
return
}
aidGrades.push({
score: parseInt(data.Score, 10),
aid: data.Aid
})
})
.on('end', () => {
const data = JSON.stringify(aidGrades)
fs.writeFileSync('aid.json', data)
})
133 changes: 133 additions & 0 deletions src/scales/__tests__/aid.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Aid } from '../../scales'

describe('Aid', () => {
describe('Get Score', () => {
describe('valid grade formats', () => {
// Check that no warnings are raised and that the score is not -1
// Don't check for correct values of score, that is separate
jest.spyOn(console, 'warn').mockImplementation()
beforeEach(() => {
jest.clearAllMocks()
})

test('basic grade A', () => {
const score = Aid.getScore('A0')
expect(console.warn).not.toHaveBeenCalled()
expect(score).not.toEqual(-1)
})

test('basic grade C', () => {
const score = Aid.getScore('C0')
expect(console.warn).not.toHaveBeenCalled()
expect(score).not.toEqual(-1)
})

test('valid + modifier', () => {
const score = Aid.getScore('A3+')
expect(console.warn).not.toHaveBeenCalled()
expect(score).not.toEqual(-1)
})
})

describe('invalid grade formats', () => {
jest.spyOn(console, 'warn').mockImplementation()
beforeEach(() => {
jest.clearAllMocks()
})

test('A6 out of range', () => {
const score = Aid.getScore('A6')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: A6 for grade scale Aid')
expect(score).toEqual(-1)
})

test('invalid minus modifier', () => {
const score = Aid.getScore('A3-')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: A3- for grade scale Aid')
expect(score).toEqual(-1)
})

test('invalid plus modifier', () => {
const score = Aid.getScore('A5+')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: A5+ for grade scale Aid')
expect(score).toEqual(-1)
})

test('plain YDS grade', () => {
const score = Aid.getScore('5.9')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: 5.9 for grade scale Aid')
expect(score).toEqual(-1)
})

test('slash grade', () => {
const score = Aid.getScore('A0/A1')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: A0/A1 for grade scale Aid')
expect(score).toEqual(-1)
})

test('not aid scale', () => {
const score = Aid.getScore('v11')
expect(console.warn).toHaveBeenCalledWith('Unexpected grade format: v11 for grade scale Aid')
expect(score).toEqual(-1)
})
})

describe('correct relative scores', () => {
test('C0 = A0', () => {
const aGrade = Aid.getScore('A0')
const cGrade = Aid.getScore('C0')
expect(cGrade).toEqual(aGrade)
})

test('A3+ > A1', () => {
const lowGrade = Aid.getScore('A1')
const highGrade = Aid.getScore('A3+')
expect(highGrade[0]).toBeGreaterThan(lowGrade[1])
})

test('A4 > A3+', () => {
const lowGrade = Aid.getScore('A3+')
const highGrade = Aid.getScore('A4')
expect(highGrade[0]).toBeGreaterThan(lowGrade[1])
})

test('C4 > C3+', () => {
const lowGrade = Aid.getScore('A3+')
const highGrade = Aid.getScore('A4')
expect(highGrade[0]).toBeGreaterThan(lowGrade[1])
})

test('C4 > A3+', () => {
const lowGrade = Aid.getScore('A3+')
const highGrade = Aid.getScore('A4')
expect(highGrade[0]).toBeGreaterThan(lowGrade[1])
})

test('C5 = A5', () => {
const aGrade = Aid.getScore('A5')
const cGrade = Aid.getScore('C5')
expect(cGrade).toEqual(aGrade)
})
})
})

describe('Get Grade', () => {
test('bottom of range', () => {
expect(Aid.getGrade(0)).toBe('A0')
})

test('top of range', () => {
expect(Aid.getGrade(Infinity)).toBe('A5')
})

test('single score provided', () => {
expect(Aid.getGrade(34)).toBe('A5')
expect(Aid.getGrade(34.5)).toBe('A5')
})

test('range of scores provided', () => {
expect(Aid.getGrade([0.5, 2])).toBe('A0')
expect(Aid.getGrade([8, 13])).toBe('A2/A2+')
})
})
})
Loading

0 comments on commit 31a9f1f

Please sign in to comment.