howlongtobeat-core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +157 -0
- package/esm/mod.d.ts +30 -0
- package/esm/mod.d.ts.map +1 -0
- package/esm/mod.js +31 -0
- package/esm/package.json +3 -0
- package/esm/src/HowLongToBeat.d.ts +63 -0
- package/esm/src/HowLongToBeat.d.ts.map +1 -0
- package/esm/src/HowLongToBeat.js +142 -0
- package/esm/src/http/client.d.ts +29 -0
- package/esm/src/http/client.d.ts.map +1 -0
- package/esm/src/http/client.js +250 -0
- package/esm/src/parser/json.d.ts +17 -0
- package/esm/src/parser/json.d.ts.map +1 -0
- package/esm/src/parser/json.js +113 -0
- package/esm/src/types.d.ts +179 -0
- package/esm/src/types.d.ts.map +1 -0
- package/esm/src/types.js +19 -0
- package/esm/src/utils/similarity.d.ts +28 -0
- package/esm/src/utils/similarity.d.ts.map +1 -0
- package/esm/src/utils/similarity.js +127 -0
- package/package.json +38 -0
- package/script/mod.d.ts +30 -0
- package/script/mod.d.ts.map +1 -0
- package/script/mod.js +39 -0
- package/script/package.json +3 -0
- package/script/src/HowLongToBeat.d.ts +63 -0
- package/script/src/HowLongToBeat.d.ts.map +1 -0
- package/script/src/HowLongToBeat.js +146 -0
- package/script/src/http/client.d.ts +29 -0
- package/script/src/http/client.d.ts.map +1 -0
- package/script/src/http/client.js +258 -0
- package/script/src/parser/json.d.ts +17 -0
- package/script/src/parser/json.d.ts.map +1 -0
- package/script/src/parser/json.js +118 -0
- package/script/src/types.d.ts +179 -0
- package/script/src/types.d.ts.map +1 -0
- package/script/src/types.js +22 -0
- package/script/src/utils/similarity.d.ts +28 -0
- package/script/src/utils/similarity.d.ts.map +1 -0
- package/script/src/utils/similarity.js +133 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* String similarity algorithms for HowLongToBeat
|
|
4
|
+
*
|
|
5
|
+
* Provides both Gestalt pattern matching (like Python's difflib.SequenceMatcher)
|
|
6
|
+
* and Levenshtein distance-based similarity.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.calculateSimilarity = calculateSimilarity;
|
|
10
|
+
exports.gestaltSimilarity = gestaltSimilarity;
|
|
11
|
+
exports.levenshteinSimilarity = levenshteinSimilarity;
|
|
12
|
+
exports.createSimilarityCalculator = createSimilarityCalculator;
|
|
13
|
+
/**
|
|
14
|
+
* Calculate similarity using the specified algorithm
|
|
15
|
+
*/
|
|
16
|
+
function calculateSimilarity(a, b, algorithm = "gestalt") {
|
|
17
|
+
if (algorithm === "levenshtein") {
|
|
18
|
+
return levenshteinSimilarity(a, b);
|
|
19
|
+
}
|
|
20
|
+
return gestaltSimilarity(a, b);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Gestalt Pattern Matching similarity (like Python's difflib.SequenceMatcher)
|
|
24
|
+
*
|
|
25
|
+
* This implements the Ratcliff/Obershelp algorithm which finds the longest
|
|
26
|
+
* common substring and recursively processes the remaining parts.
|
|
27
|
+
*/
|
|
28
|
+
function gestaltSimilarity(a, b) {
|
|
29
|
+
if (a === b)
|
|
30
|
+
return 1.0;
|
|
31
|
+
if (a.length === 0 || b.length === 0)
|
|
32
|
+
return 0.0;
|
|
33
|
+
const matches = countMatchingCharacters(a, b);
|
|
34
|
+
return (2.0 * matches) / (a.length + b.length);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Count matching characters using Ratcliff/Obershelp algorithm
|
|
38
|
+
*/
|
|
39
|
+
function countMatchingCharacters(a, b) {
|
|
40
|
+
const [lcs, aStart, aEnd, bStart, bEnd] = findLongestCommonSubstring(a, b);
|
|
41
|
+
if (lcs === 0) {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
let count = lcs;
|
|
45
|
+
// Recursively process left parts
|
|
46
|
+
if (aStart > 0 && bStart > 0) {
|
|
47
|
+
count += countMatchingCharacters(a.substring(0, aStart), b.substring(0, bStart));
|
|
48
|
+
}
|
|
49
|
+
// Recursively process right parts
|
|
50
|
+
if (aEnd < a.length && bEnd < b.length) {
|
|
51
|
+
count += countMatchingCharacters(a.substring(aEnd), b.substring(bEnd));
|
|
52
|
+
}
|
|
53
|
+
return count;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Find the longest common substring between two strings
|
|
57
|
+
* Returns [length, aStart, aEnd, bStart, bEnd]
|
|
58
|
+
*/
|
|
59
|
+
function findLongestCommonSubstring(a, b) {
|
|
60
|
+
let longestLength = 0;
|
|
61
|
+
let aStart = 0;
|
|
62
|
+
let bStart = 0;
|
|
63
|
+
// Build a table of matching positions
|
|
64
|
+
const table = Array(a.length + 1)
|
|
65
|
+
.fill(null)
|
|
66
|
+
.map(() => Array(b.length + 1).fill(0));
|
|
67
|
+
for (let i = 1; i <= a.length; i++) {
|
|
68
|
+
for (let j = 1; j <= b.length; j++) {
|
|
69
|
+
if (a[i - 1] === b[j - 1]) {
|
|
70
|
+
table[i][j] = table[i - 1][j - 1] + 1;
|
|
71
|
+
if (table[i][j] > longestLength) {
|
|
72
|
+
longestLength = table[i][j];
|
|
73
|
+
aStart = i - longestLength;
|
|
74
|
+
bStart = j - longestLength;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [
|
|
80
|
+
longestLength,
|
|
81
|
+
aStart,
|
|
82
|
+
aStart + longestLength,
|
|
83
|
+
bStart,
|
|
84
|
+
bStart + longestLength,
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Calculate Levenshtein distance between two strings
|
|
89
|
+
*/
|
|
90
|
+
function levenshteinDistance(a, b) {
|
|
91
|
+
if (a.length === 0)
|
|
92
|
+
return b.length;
|
|
93
|
+
if (b.length === 0)
|
|
94
|
+
return a.length;
|
|
95
|
+
const matrix = [];
|
|
96
|
+
// Initialize first column
|
|
97
|
+
for (let i = 0; i <= a.length; i++) {
|
|
98
|
+
matrix[i] = [i];
|
|
99
|
+
}
|
|
100
|
+
// Initialize first row
|
|
101
|
+
for (let j = 0; j <= b.length; j++) {
|
|
102
|
+
matrix[0][j] = j;
|
|
103
|
+
}
|
|
104
|
+
// Fill in the rest of the matrix
|
|
105
|
+
for (let i = 1; i <= a.length; i++) {
|
|
106
|
+
for (let j = 1; j <= b.length; j++) {
|
|
107
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
108
|
+
matrix[i][j] = Math.min(matrix[i - 1][j] + 1, // deletion
|
|
109
|
+
matrix[i][j - 1] + 1, // insertion
|
|
110
|
+
matrix[i - 1][j - 1] + cost);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return matrix[a.length][b.length];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Calculate similarity based on Levenshtein distance
|
|
117
|
+
* Returns a value between 0 and 1, where 1 means identical strings
|
|
118
|
+
*/
|
|
119
|
+
function levenshteinSimilarity(a, b) {
|
|
120
|
+
if (a === b)
|
|
121
|
+
return 1.0;
|
|
122
|
+
if (a.length === 0 || b.length === 0)
|
|
123
|
+
return 0.0;
|
|
124
|
+
const distance = levenshteinDistance(a, b);
|
|
125
|
+
const maxLength = Math.max(a.length, b.length);
|
|
126
|
+
return 1 - distance / maxLength;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Create a similarity calculator function with a specific algorithm
|
|
130
|
+
*/
|
|
131
|
+
function createSimilarityCalculator(algorithm = "gestalt") {
|
|
132
|
+
return (a, b) => calculateSimilarity(a, b, algorithm);
|
|
133
|
+
}
|