From f392dcf3f07dccec2b8f8881c0ecd5b910146db6 Mon Sep 17 00:00:00 2001 From: nicidob Date: Sun, 22 Nov 2020 14:45:55 -0500 Subject: [PATCH 01/12] statistical shot distributions --- src/worker/core/GameSim.basketball.ts | 59 +++++++++++++++------------ 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 47689af789..3cfbe336d6 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1312,13 +1312,38 @@ class GameSim { let probMissAndFoul; let type: ShotType; - if ( - forceThreePointer || - (this.team[this.o].player[p].compositeRating.shootingThreePointer > - 0.35 && - Math.random() < - 0.67 * shootingThreePointerScaled * g.get("threePointTendencyFactor")) - ) { + const rShot = + 0.69 + + 1.57 * this.team[this.o].player[p].compositeRating.shootingAtRim - + 0.86 * this.team[this.o].player[p].compositeRating.shootingMidRange - + 1.06 * this.team[this.o].player[p].compositeRating.shootingThreePointer; + const lShot = + 0.11 * this.team[this.o].player[p].compositeRating.shootingLowPost - + 0.27 * this.team[this.o].player[p].compositeRating.shootingThreePointer; + const mShot = 0; + const tShot = + -0.8 * this.team[this.o].player[p].compositeRating.shootingLowPost - + 0.82 * this.team[this.o].player[p].compositeRating.shootingMidRange + + 3.3 * this.team[this.o].player[p].compositeRating.shootingThreePointer; + const minV = Math.min(rShot, lShot, mShot, tShot); + + const sFactor = + this.synergyFactor * + Math.exp(this.team[this.o].synergy.off - this.team[this.d].synergy.def); // Synergy makes easy shots either more likely or less likely + //console.log(sFactor); + const erShot = Math.exp(rShot - minV); + const elShot = Math.exp(lShot - minV); + const emShot = Math.exp(mShot - minV); + const etShot = Math.exp(tShot - minV) * g.get("threePointTendencyFactor"); + const shotTotal = erShot + elShot + emShot + etShot; + const sNum = Math.random(); + + const prShot = erShot / shotTotal; + //const plShot = (elShot/shotTotal); + const pmShot = emShot / shotTotal; + const ptShot = etShot / shotTotal; + + if (forceThreePointer || sNum < ptShot) { // Three pointer type = "threePointer"; probMissAndFoul = 0.02; @@ -1333,23 +1358,7 @@ class GameSim { this.recordPlay("fgaTp", this.o, [this.team[this.o].player[p].name]); } else { - const r1 = - 0.8 * - Math.random() * - this.team[this.o].player[p].compositeRating.shootingMidRange; - const r2 = - Math.random() * - (this.team[this.o].player[p].compositeRating.shootingAtRim + - this.synergyFactor * - (this.team[this.o].synergy.off - this.team[this.d].synergy.def)); // Synergy makes easy shots either more likely or less likely - - const r3 = - Math.random() * - (this.team[this.o].player[p].compositeRating.shootingLowPost + - this.synergyFactor * - (this.team[this.o].synergy.off - this.team[this.d].synergy.def)); // Synergy makes easy shots either more likely or less likely - - if (r1 > r2 && r1 > r3) { + if (sNum < pmShot + ptShot) { // Two point jumper type = "midRange"; probMissAndFoul = 0.07; @@ -1360,7 +1369,7 @@ class GameSim { this.recordPlay("fgaMidRange", this.o, [ this.team[this.o].player[p].name, ]); - } else if (r2 > r3) { + } else if (sNum < prShot + pmShot + ptShot) { // Dunk, fast break or half court type = "atRim"; probMissAndFoul = 0.37; From 2641e11f37b36f38f915f58bbd04dc01bb07d02c Mon Sep 17 00:00:00 2001 From: nicidob Date: Sun, 22 Nov 2020 17:20:47 -0500 Subject: [PATCH 02/12] moved to small neural net --- src/worker/core/GameSim.basketball.ts | 122 +++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 13 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 3cfbe336d6..220c6710ba 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -141,6 +141,56 @@ const pickPlayer = ( return 0; }; +// point • matrix +const linearTransform4D = ( + matrix: number[], + bias: number[], + point: number[], +): number[] => { + // Give a simple variable name to each part of the matrix, a column and row number + let c0r0 = matrix[0], + c1r0 = matrix[1], + c2r0 = matrix[2], + c3r0 = matrix[3]; + let c0r1 = matrix[4], + c1r1 = matrix[5], + c2r1 = matrix[6], + c3r1 = matrix[7]; + let c0r2 = matrix[8], + c1r2 = matrix[9], + c2r2 = matrix[10], + c3r2 = matrix[11]; + let c0r3 = matrix[12], + c1r3 = matrix[13], + c2r3 = matrix[14], + c3r3 = matrix[15]; + + // Now set some simple names for the point + let x = point[0]; + let y = point[1]; + let z = point[2]; + let w = point[3]; + + // Multiply the point against each part of the 1st column, then add together + let resultX = x * c0r0 + y * c0r1 + z * c0r2 + w * c0r3; + + // Multiply the point against each part of the 2nd column, then add together + let resultY = x * c1r0 + y * c1r1 + z * c1r2 + w * c1r3; + + // Multiply the point against each part of the 3rd column, then add together + let resultZ = x * c2r0 + y * c2r1 + z * c2r2 + w * c2r3; + + // Multiply the point against each part of the 4th column, then add together + let resultW = x * c3r0 + y * c3r1 + z * c3r2 + w * c3r3; + + return [ + resultX + bias[0], + resultY + bias[1], + resultZ + bias[2], + resultW + bias[3], + ]; +}; + class GameSim { id: number; @@ -1312,19 +1362,65 @@ class GameSim { let probMissAndFoul; let type: ShotType; - const rShot = - 0.69 + - 1.57 * this.team[this.o].player[p].compositeRating.shootingAtRim - - 0.86 * this.team[this.o].player[p].compositeRating.shootingMidRange - - 1.06 * this.team[this.o].player[p].compositeRating.shootingThreePointer; - const lShot = - 0.11 * this.team[this.o].player[p].compositeRating.shootingLowPost - - 0.27 * this.team[this.o].player[p].compositeRating.shootingThreePointer; - const mShot = 0; - const tShot = - -0.8 * this.team[this.o].player[p].compositeRating.shootingLowPost - - 0.82 * this.team[this.o].player[p].compositeRating.shootingMidRange + - 3.3 * this.team[this.o].player[p].compositeRating.shootingThreePointer; + const m1 = [ + 2.746696, + -0.4067031, + -0.16296275, + 1.2038584, + 1.0869961, + 0.29596367, + -0.12329404, + -0.6934256, + -1.9520527, + 0.8385392, + 0.51677364, + -0.4509902, + -2.4895766, + -1.7389809, + 0.26240268, + 0.02959079, + ]; + const b1 = [-1.2218589, 0.6188029, 0.18970709, -0.08382772]; + const m2 = [ + 0.7306026, + 0.58740854, + -0.041206032, + -1.7370037, + 0.63410217, + 0.7026119, + 0.37222016, + -1.5711316, + -0.38651368, + -0.14610946, + -0.31575158, + 0.23786432, + 1.1137322, + 0.1308731, + -1.5386553, + 0.9266571, + ]; + const b2 = [0.83704126, -0.027587852, -0.4608978, -0.88724256]; + + const inputVec = [ + this.team[this.o].player[p].compositeRating.shootingAtRim, + this.team[this.o].player[p].compositeRating.shootingLowPost, + this.team[this.o].player[p].compositeRating.shootingMidRange, + this.team[this.o].player[p].compositeRating.shootingThreePointer, + ]; + + const h1 = linearTransform4D(m1, b1, inputVec); + const res = linearTransform4D(m2, b2, [ + Math.tanh(h1[0]), + Math.tanh(h1[1]), + Math.tanh(h1[2]), + Math.tanh(h1[3]), + ]); + + const rShot = res[0]; + const lShot = res[1]; + const mShot = res[2]; + const tShot = res[3]; + const minV = Math.min(rShot, lShot, mShot, tShot); const sFactor = From b204178f734b5e247660166cc3854a07f78eedfc Mon Sep 17 00:00:00 2001 From: nicidob Date: Sun, 22 Nov 2020 18:34:30 -0500 Subject: [PATCH 03/12] added synergy boost --- src/worker/core/GameSim.basketball.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 220c6710ba..63893d42f7 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1423,11 +1423,14 @@ class GameSim { const minV = Math.min(rShot, lShot, mShot, tShot); - const sFactor = - this.synergyFactor * - Math.exp(this.team[this.o].synergy.off - this.team[this.d].synergy.def); // Synergy makes easy shots either more likely or less likely - //console.log(sFactor); - const erShot = Math.exp(rShot - minV); + // Synergy makes shots at the rim either more likely or less likely + const sFactor = Math.exp( + this.team[this.o].synergy.off - this.team[this.d].synergy.def, + ); // ranges from 0.6 to 1.4. Mean of 0.87 + const sFactor2 = 17.6 * this.synergyFactor * (sFactor - 0.87); // mean of 0, std of synergyFactor * 2, roughly + const sScale = 0.5 + 1 / (1 + Math.exp(-sFactor2)); // mean of 1, ranges [0.75 to 1.35 in playoffs], 2.5x less in regular season + + const erShot = Math.exp(rShot - minV) * sScale; const elShot = Math.exp(lShot - minV); const emShot = Math.exp(mShot - minV); const etShot = Math.exp(tShot - minV) * g.get("threePointTendencyFactor"); From 9fb9d3b2bb584f3c31bb30178e01ee74f371de69 Mon Sep 17 00:00:00 2001 From: nicidob Date: Tue, 24 Nov 2020 10:45:40 -0500 Subject: [PATCH 04/12] Moved from a 2 layer Tanh (with bias) to a 4 layer ReLU (no bias). Why? ReLU layers without bias are input scale-invariant (they're just multiplies and thresholds at zero). So this doesn't care if the ratings are near 20 or near 80... it can't tell the difference. I needed the extra layers to get comparable performance. Although it is slightly worse in terms of loss, I think being input scale invariant is a huge win. --- src/worker/core/GameSim.basketball.ts | 141 ++++++++++++++++++-------- 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 63893d42f7..0a134d8e04 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1363,44 +1363,77 @@ class GameSim { let type: ShotType; const m1 = [ - 2.746696, - -0.4067031, - -0.16296275, - 1.2038584, - 1.0869961, - 0.29596367, - -0.12329404, - -0.6934256, - -1.9520527, - 0.8385392, - 0.51677364, - -0.4509902, - -2.4895766, - -1.7389809, - 0.26240268, - 0.02959079, + 1.8114938, + 0.7741107, + 0.0031505486, + 1.263424, + -0.009806325, + 0.080395915, + 0.95797116, + -0.54190624, + -0.32714152, + 0.4649361, + 0.59234613, + 0.013358551, + -0.7457246, + 0.7668498, + -0.81250024, + -0.9172708, ]; - const b1 = [-1.2218589, 0.6188029, 0.18970709, -0.08382772]; const m2 = [ - 0.7306026, - 0.58740854, - -0.041206032, - -1.7370037, - 0.63410217, - 0.7026119, - 0.37222016, - -1.5711316, - -0.38651368, - -0.14610946, - -0.31575158, - 0.23786432, - 1.1137322, - 0.1308731, - -1.5386553, - 0.9266571, + 1.1324306, + 0.8232891, + 0.6142749, + -0.40989527, + 0.41180587, + 0.28479576, + -0.01726953, + 1.3427685, + -1.027349, + -1.2358073, + 0.45937437, + -0.82762986, + -1.3784989, + -1.2830316, + 1.2126924, + -0.79460925, + ]; + const m3 = [ + 0.9055171, + 0.8522915, + 1.3803011, + 0.8590033, + -2.4478855, + 0.67415154, + -2.7599597, + 0.966471, + 0.5884137, + 0.45802408, + 0.34657773, + -0.51301485, + -2.1621242, + -0.17271538, + -2.2478929, + 0.41187626, + ]; + const m4 = [ + -0.30959758, + 0.0011721842, + -1.0500107, + 4.4725413, + 0.76422995, + 0.055398513, + -0.6483635, + -0.65447193, + 1.0317212, + -0.0003368239, + 0.6956354, + -9.576447, + -0.19240071, + -0.5586338, + 0.00020757454, + 1.4662966, ]; - const b2 = [0.83704126, -0.027587852, -0.4608978, -0.88724256]; - const inputVec = [ this.team[this.o].player[p].compositeRating.shootingAtRim, this.team[this.o].player[p].compositeRating.shootingLowPost, @@ -1408,13 +1441,37 @@ class GameSim { this.team[this.o].player[p].compositeRating.shootingThreePointer, ]; - const h1 = linearTransform4D(m1, b1, inputVec); - const res = linearTransform4D(m2, b2, [ - Math.tanh(h1[0]), - Math.tanh(h1[1]), - Math.tanh(h1[2]), - Math.tanh(h1[3]), - ]); + const h1 = linearTransform4D(m1, [0, 0, 0, 0], inputVec); + const h2 = linearTransform4D( + m2, + [0, 0, 0, 0], + [ + Math.max(h1[0], 0), + Math.max(h1[1], 0), + Math.max(h1[2], 0), + Math.max(h1[3], 0), + ], + ); + const h3 = linearTransform4D( + m3, + [0, 0, 0, 0], + [ + Math.max(h2[0], 0), + Math.max(h2[1], 0), + Math.max(h2[2], 0), + Math.max(h2[3], 0), + ], + ); + const res = linearTransform4D( + m4, + [0, 0, 0, 0], + [ + Math.max(h3[0], 0), + Math.max(h3[1], 0), + Math.max(h3[2], 0), + Math.max(h3[3], 0), + ], + ); const rShot = res[0]; const lShot = res[1]; From 2e83a85e5e9b7b1c6dcb61db80b27b70ebbde56f Mon Sep 17 00:00:00 2001 From: nicidob Date: Tue, 24 Nov 2020 13:32:00 -0500 Subject: [PATCH 05/12] updated parameters to also work for random leagues --- src/worker/core/GameSim.basketball.ts | 129 +++++++++++++------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 0a134d8e04..e644f06ab8 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1363,77 +1363,78 @@ class GameSim { let type: ShotType; const m1 = [ - 1.8114938, - 0.7741107, - 0.0031505486, - 1.263424, - -0.009806325, - 0.080395915, - 0.95797116, - -0.54190624, - -0.32714152, - 0.4649361, - 0.59234613, - 0.013358551, - -0.7457246, - 0.7668498, - -0.81250024, - -0.9172708, + 1.8719419, + 0.60551083, + -0.5868094, + 0.77095133, + 0.13131863, + 0.50349385, + 0.86235386, + -0.7762957, + -0.6781802, + 0.129259, + 0.37431583, + -1.1135796, + -0.5564005, + 2.0143106, + -0.4110208, + 1.2805756, ]; const m2 = [ - 1.1324306, - 0.8232891, - 0.6142749, - -0.40989527, - 0.41180587, - 0.28479576, - -0.01726953, - 1.3427685, - -1.027349, - -1.2358073, - 0.45937437, - -0.82762986, - -1.3784989, - -1.2830316, - 1.2126924, - -0.79460925, + -1.3616861, + 0.07761483, + 0.59121853, + 2.4699638, + 0.93607324, + 0.62991214, + 0.5771888, + -1.4113994, + 0.33233893, + -0.30547172, + -1.6023605, + 0.4647654, + 2.1400461, + 1.3256168, + 0.85400367, + -1.8635037, ]; const m3 = [ - 0.9055171, - 0.8522915, - 1.3803011, - 0.8590033, - -2.4478855, - 0.67415154, - -2.7599597, - 0.966471, - 0.5884137, - 0.45802408, - 0.34657773, - -0.51301485, - -2.1621242, - -0.17271538, - -2.2478929, - 0.41187626, + 0.085133284, + 1.1095129, + 0.76330864, + 0.038485732, + 0.55271363, + 0.19886105, + -0.20755152, + 0.619819, + 0.9686617, + 0.3524881, + -0.55850863, + -0.376393, + 0.73838353, + -2.787762, + 8.7408e-40, + -9.231746, ]; const m4 = [ - -0.30959758, - 0.0011721842, - -1.0500107, - 4.4725413, - 0.76422995, - 0.055398513, - -0.6483635, - -0.65447193, - 1.0317212, - -0.0003368239, - 0.6956354, - -9.576447, - -0.19240071, - -0.5586338, - 0.00020757454, - 1.4662966, + 0.8547521, + 0.37307405, + -0.09692042, + -0.7886934, + -1.2119317, + -0.80929136, + -0.47266224, + 1.8498709, + 0.04826861, + 0.20717552, + 0.5179917, + -1.5013214, + 2.9796553, + 1.3788755, + 1.6872308, + -3.9838443, ]; + const inputVec = [ this.team[this.o].player[p].compositeRating.shootingAtRim, this.team[this.o].player[p].compositeRating.shootingLowPost, From 5b54afa9b4c78409b56e8a02c217be4a91a2cbc5 Mon Sep 17 00:00:00 2001 From: nicidob Date: Tue, 24 Nov 2020 18:34:50 -0500 Subject: [PATCH 06/12] new params. slightly better dist for random leagues --- src/worker/core/GameSim.basketball.ts | 152 +++++++++++++------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index e644f06ab8..36b4da386f 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1363,76 +1363,76 @@ class GameSim { let type: ShotType; const m1 = [ - 1.8719419, - 0.60551083, - -0.5868094, - 0.77095133, - 0.13131863, - 0.50349385, - 0.86235386, - -0.7762957, - -0.6781802, - 0.129259, - 0.37431583, - -1.1135796, - -0.5564005, - 2.0143106, - -0.4110208, - 1.2805756, + 1.3113543, + -0.6263279, + 0.14344317, + 1.1925222, + -0.07763273, + 0.6175058, + 0.5638721, + 0.12563407, + 0.18570572, + 1.2480364, + 0.9913909, + 0.36482584, + 1.3063713, + 0.17891374, + 0.13886125, + -0.72557, ]; const m2 = [ - -1.3616861, - 0.07761483, - 0.59121853, - 2.4699638, - 0.93607324, - 0.62991214, - 0.5771888, - -1.4113994, - 0.33233893, - -0.30547172, - -1.6023605, - 0.4647654, - 2.1400461, - 1.3256168, - 0.85400367, - -1.8635037, + -0.085642695, + 1.2607394, + 1.01802, + -1.7136612, + -0.28531465, + -1.681668, + 0.4259014, + 1.3577137, + 0.7025676, + -0.38678032, + 0.6859606, + -0.001837992, + 1.5314473, + 1.0258932, + -1.185248, + 0.448846, ]; const m3 = [ - 0.085133284, - 1.1095129, - 0.76330864, - 0.038485732, - 0.55271363, - 0.19886105, - -0.20755152, - 0.619819, - 0.9686617, - 0.3524881, - -0.55850863, - -0.376393, - 0.73838353, - -2.787762, - 8.7408e-40, - -9.231746, + -0.99058425, + -0.17723367, + -3.3057556, + 0.4959605, + 0.5384492, + -8.830978, + 0.44290268, + 1.02302, + 0.8216422, + 0.111711904, + -2.202688, + -0.827327, + -0.89683855, + 12.239624, + -0.24065332, + 0.07474789, ]; const m4 = [ - 0.8547521, - 0.37307405, - -0.09692042, - -0.7886934, - -1.2119317, - -0.80929136, - -0.47266224, - 1.8498709, - 0.04826861, - 0.20717552, - 0.5179917, - -1.5013214, - 2.9796553, - 1.3788755, - 1.6872308, - -3.9838443, + -0.25629416, + -0.3995677, + -0.1308312, + 1.0516244, + -1.9253706, + 1.6582984, + 7.713786, + -4.127112, + -2.7931354, + 0.57621497, + -2.501346, + 3.4125862, + 0.76335984, + 0.57280207, + 0.2098833, + -1.3665546, ]; const inputVec = [ @@ -1447,30 +1447,30 @@ class GameSim { m2, [0, 0, 0, 0], [ - Math.max(h1[0], 0), - Math.max(h1[1], 0), - Math.max(h1[2], 0), - Math.max(h1[3], 0), + Math.max(h1[0], 0.01 * h1[0]), + Math.max(h1[1], 0.01 * h1[1]), + Math.max(h1[2], 0.01 * h1[2]), + Math.max(h1[3], 0.01 * h1[3]), ], ); const h3 = linearTransform4D( m3, [0, 0, 0, 0], [ - Math.max(h2[0], 0), - Math.max(h2[1], 0), - Math.max(h2[2], 0), - Math.max(h2[3], 0), + Math.max(h2[0], 0.01 * h2[0]), + Math.max(h2[1], 0.01 * h2[1]), + Math.max(h2[2], 0.01 * h2[2]), + Math.max(h2[3], 0.01 * h2[3]), ], ); const res = linearTransform4D( m4, [0, 0, 0, 0], [ - Math.max(h3[0], 0), - Math.max(h3[1], 0), - Math.max(h3[2], 0), - Math.max(h3[3], 0), + Math.max(h3[0], 0.01 * h3[0]), + Math.max(h3[1], 0.01 * h3[1]), + Math.max(h3[2], 0.01 * h3[2]), + Math.max(h3[3], 0.01 * h3[3]), ], ); From f766e4d74dbfd6b8e334e6f5fabfd4debf667076 Mon Sep 17 00:00:00 2001 From: nicidob Date: Tue, 24 Nov 2020 19:51:17 -0500 Subject: [PATCH 07/12] alt idea of scale invariance --- src/worker/core/GameSim.basketball.ts | 190 +++++++++++++------------- 1 file changed, 94 insertions(+), 96 deletions(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 36b4da386f..8380c94c29 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1361,79 +1361,82 @@ class GameSim { let probMake; let probMissAndFoul; let type: ShotType; - const m1 = [ - 1.3113543, - -0.6263279, - 0.14344317, - 1.1925222, - -0.07763273, - 0.6175058, - 0.5638721, - 0.12563407, - 0.18570572, - 1.2480364, - 0.9913909, - 0.36482584, - 1.3063713, - 0.17891374, - 0.13886125, - -0.72557, + -0.11062113, + 0.2145893, + 0.21443088, + -1.286721, + 0.05455208, + -0.9112426, + -0.285477, + 0.5084881, + -0.40539294, + -1.0017884, + -0.9085916, + 0.47239658, + -0.2735867, + 0.96344316, + 1.0659783, + 0.70627713, ]; + const b1 = [-0.25969836, 0.215397, 0.3555345, 0.2896994]; const m2 = [ - -0.085642695, - 1.2607394, - 1.01802, - -1.7136612, - -0.28531465, - -1.681668, - 0.4259014, - 1.3577137, - 0.7025676, - -0.38678032, - 0.6859606, - -0.001837992, - 1.5314473, - 1.0258932, - -1.185248, - 0.448846, + -0.6771088, + -0.5624548, + -0.3640189, + 0.39685425, + -1.6664761, + 0.021767367, + 0.17568927, + 1.4071639, + -1.2445395, + 1.1329722, + -0.14028981, + -0.5465622, + 0.44314194, + 0.76718783, + 1.7255903, + 1.1030437, ]; + const b2 = [0.5321581, 0.14918292, -0.027641574, 0.0352142]; const m3 = [ - -0.99058425, - -0.17723367, - -3.3057556, - 0.4959605, - 0.5384492, - -8.830978, - 0.44290268, - 1.02302, - 0.8216422, - 0.111711904, - -2.202688, - -0.827327, - -0.89683855, - 12.239624, - -0.24065332, - 0.07474789, + 0.9709988, + 0.8057134, + -1.0169693, + -0.059781134, + 0.12667929, + 0.0025585638, + 0.7535072, + 0.5613678, + 1.2599803, + -0.74605703, + 0.61769235, + -1.3749193, + -0.03890923, + 0.26556912, + 0.93223006, + -1.3593911, ]; + const b3 = [-0.112184435, 0.980346, 0.0077717914, 0.58999795]; const m4 = [ - -0.25629416, - -0.3995677, - -0.1308312, - 1.0516244, - -1.9253706, - 1.6582984, - 7.713786, - -4.127112, - -2.7931354, - 0.57621497, - -2.501346, - 3.4125862, - 0.76335984, - 0.57280207, - 0.2098833, - -1.3665546, + -0.5897572, + -0.20046012, + 1.1114576, + -0.59119254, + 0.8493401, + 0.59680605, + -0.09680688, + -0.13142292, + -0.45576066, + -0.67522925, + -0.6649587, + 1.0499781, + 2.0329778, + 1.4694291, + 0.7106988, + -2.1637397, ]; + const b4 = [0.47565907, 0.057101946, -0.5868888, -0.02368892]; const inputVec = [ this.team[this.o].player[p].compositeRating.shootingAtRim, @@ -1442,37 +1445,32 @@ class GameSim { this.team[this.o].player[p].compositeRating.shootingThreePointer, ]; - const h1 = linearTransform4D(m1, [0, 0, 0, 0], inputVec); - const h2 = linearTransform4D( - m2, - [0, 0, 0, 0], - [ - Math.max(h1[0], 0.01 * h1[0]), - Math.max(h1[1], 0.01 * h1[1]), - Math.max(h1[2], 0.01 * h1[2]), - Math.max(h1[3], 0.01 * h1[3]), - ], - ); - const h3 = linearTransform4D( - m3, - [0, 0, 0, 0], - [ - Math.max(h2[0], 0.01 * h2[0]), - Math.max(h2[1], 0.01 * h2[1]), - Math.max(h2[2], 0.01 * h2[2]), - Math.max(h2[3], 0.01 * h2[3]), - ], - ); - const res = linearTransform4D( - m4, - [0, 0, 0, 0], - [ - Math.max(h3[0], 0.01 * h3[0]), - Math.max(h3[1], 0.01 * h3[1]), - Math.max(h3[2], 0.01 * h3[2]), - Math.max(h3[3], 0.01 * h3[3]), - ], - ); + const iVT = inputVec[0] + inputVec[1] + inputVec[2] + inputVec[3]; + + const h1 = linearTransform4D(m1, b1, [ + inputVec[0] / iVT, + inputVec[1] / iVT, + inputVec[2] / iVT, + inputVec[3] / iVT, + ]); + const h2 = linearTransform4D(m2, b2, [ + Math.max(h1[0], 0.01 * h1[0]), + Math.max(h1[1], 0.01 * h1[1]), + Math.max(h1[2], 0.01 * h1[2]), + Math.max(h1[3], 0.01 * h1[3]), + ]); + const h3 = linearTransform4D(m3, b3, [ + Math.max(h2[0], 0.01 * h2[0]), + Math.max(h2[1], 0.01 * h2[1]), + Math.max(h2[2], 0.01 * h2[2]), + Math.max(h2[3], 0.01 * h2[3]), + ]); + const res = linearTransform4D(m4, b4, [ + Math.max(h3[0], 0.01 * h3[0]), + Math.max(h3[1], 0.01 * h3[1]), + Math.max(h3[2], 0.01 * h3[2]), + Math.max(h3[3], 0.01 * h3[3]), + ]); const rShot = res[0]; const lShot = res[1]; From 1a67ccbd7aaf0d2b3abf99750d3368bb16862a6c Mon Sep 17 00:00:00 2001 From: nicidob Date: Wed, 25 Nov 2020 14:50:28 -0500 Subject: [PATCH 08/12] why not more synergy --- src/worker/core/GameSim.basketball.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/worker/core/GameSim.basketball.ts b/src/worker/core/GameSim.basketball.ts index 8380c94c29..77868e7241 100644 --- a/src/worker/core/GameSim.basketball.ts +++ b/src/worker/core/GameSim.basketball.ts @@ -1488,7 +1488,7 @@ class GameSim { const erShot = Math.exp(rShot - minV) * sScale; const elShot = Math.exp(lShot - minV); - const emShot = Math.exp(mShot - minV); + const emShot = Math.exp(mShot - minV) / sScale; const etShot = Math.exp(tShot - minV) * g.get("threePointTendencyFactor"); const shotTotal = erShot + elShot + emShot + etShot; const sNum = Math.random(); From ab0325b19e14a7348755152f9b0d6e0bcb16eb4a Mon Sep 17 00:00:00 2001 From: nicidob Date: Thu, 26 Nov 2020 16:41:14 -0500 Subject: [PATCH 09/12] updated ovr --- src/worker/core/player/ovr.basketball.ts | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/worker/core/player/ovr.basketball.ts b/src/worker/core/player/ovr.basketball.ts index 4af809df14..1460039bfa 100644 --- a/src/worker/core/player/ovr.basketball.ts +++ b/src/worker/core/player/ovr.basketball.ts @@ -10,22 +10,22 @@ import type { PlayerRatings } from "../../../common/types.basketball"; const ovr = (ratings: PlayerRatings): number => { // See analysis/player-ovr-basketball const r = - 0.159 * (ratings.hgt - 47.5) + - 0.0777 * (ratings.stre - 50.2) + - 0.123 * (ratings.spd - 50.8) + - 0.051 * (ratings.jmp - 48.7) + - 0.0632 * (ratings.endu - 39.9) + - 0.0126 * (ratings.ins - 42.4) + - 0.0286 * (ratings.dnk - 49.5) + - 0.0202 * (ratings.ft - 47.0) + - 0.0726 * (ratings.tp - 47.1) + - 0.133 * (ratings.oiq - 46.8) + - 0.159 * (ratings.diq - 46.7) + - 0.059 * (ratings.drb - 54.8) + - 0.062 * (ratings.pss - 51.3) + - 0.01 * (ratings.fg - 47.0) + - 0.01 * (ratings.reb - 51.4) + - 48.5; + 0.209 * ratings.hgt + + 0.0648 * ratings.stre + + 0.148 * ratings.spd + + 0.0609 * ratings.jmp + + 0.0314 * ratings.endu + + 0.0109 * ratings.ins + + 0.0288 * ratings.dnk + + 0.0112 * ratings.ft + + 0.15 * ratings.tp + + 0.107 * ratings.oiq + + 0.0799 * ratings.diq + + 0.103 * ratings.drb + + 0.0869 * ratings.pss + + -0.024 * ratings.fg + + 0.0436 * ratings.reb + + -6.12; // Fudge factor to keep ovr ratings the same as they used to be (back before 2018 ratings rescaling) // +8 at 68 From 4e1683438cfe89bb3d170841b82615cbf67e6ada Mon Sep 17 00:00:00 2001 From: nicidob Date: Sat, 28 Nov 2020 19:05:36 -0500 Subject: [PATCH 10/12] adjust pace to seemingly match --- src/worker/core/game/loadTeams.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/worker/core/game/loadTeams.ts b/src/worker/core/game/loadTeams.ts index fc63ed4985..0d8fb2754a 100644 --- a/src/worker/core/game/loadTeams.ts +++ b/src/worker/core/game/loadTeams.ts @@ -149,7 +149,10 @@ const processTeam = ( t.pace /= numPlayers; t.pace = t.pace * 15 + 100; // Scale between 100 and 115 - + const pace_avg = 108.4; + const pace_diff = t.pace - pace_avg; + const pace_adj = 15 * Math.tanh(1 * pace_diff); + t.pace = pace_avg + pace_adj; if (allStarGame) { t.pace *= 1.15; } From 115545a94975fdac40b251cced83314fc3d953b0 Mon Sep 17 00:00:00 2001 From: nicidob Date: Sun, 30 May 2021 12:06:34 -0400 Subject: [PATCH 11/12] just moar husky fixes --- .husky/common.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .husky/common.sh diff --git a/.husky/common.sh b/.husky/common.sh new file mode 100644 index 0000000000..b8215689db --- /dev/null +++ b/.husky/common.sh @@ -0,0 +1,8 @@ +command_exists () { + command -v "$1" >/dev/null 2>&1 +} + +# Windows 10, Git Bash and Yarn workaround +if command_exists winpty && test -t 1; then + exec < /dev/tty +fi From 411635f74a0acee49273f7ccca30e76d2512f187 Mon Sep 17 00:00:00 2001 From: nicidob Date: Sun, 30 May 2021 12:11:52 -0400 Subject: [PATCH 12/12] just changing lets to consts in the matrix mul --- src/worker/core/GameSim.basketball/index.ts | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/worker/core/GameSim.basketball/index.ts b/src/worker/core/GameSim.basketball/index.ts index c5c64adc26..fafc2608ec 100644 --- a/src/worker/core/GameSim.basketball/index.ts +++ b/src/worker/core/GameSim.basketball/index.ts @@ -170,40 +170,40 @@ const linearTransform4D = ( point: number[], ): number[] => { // Give a simple variable name to each part of the matrix, a column and row number - let c0r0 = matrix[0], + const c0r0 = matrix[0], c1r0 = matrix[1], c2r0 = matrix[2], c3r0 = matrix[3]; - let c0r1 = matrix[4], + const c0r1 = matrix[4], c1r1 = matrix[5], c2r1 = matrix[6], c3r1 = matrix[7]; - let c0r2 = matrix[8], + const c0r2 = matrix[8], c1r2 = matrix[9], c2r2 = matrix[10], c3r2 = matrix[11]; - let c0r3 = matrix[12], + const c0r3 = matrix[12], c1r3 = matrix[13], c2r3 = matrix[14], c3r3 = matrix[15]; // Now set some simple names for the point - let x = point[0]; - let y = point[1]; - let z = point[2]; - let w = point[3]; + const x = point[0]; + const y = point[1]; + const z = point[2]; + const w = point[3]; // Multiply the point against each part of the 1st column, then add together - let resultX = x * c0r0 + y * c0r1 + z * c0r2 + w * c0r3; + const resultX = x * c0r0 + y * c0r1 + z * c0r2 + w * c0r3; // Multiply the point against each part of the 2nd column, then add together - let resultY = x * c1r0 + y * c1r1 + z * c1r2 + w * c1r3; + const resultY = x * c1r0 + y * c1r1 + z * c1r2 + w * c1r3; // Multiply the point against each part of the 3rd column, then add together - let resultZ = x * c2r0 + y * c2r1 + z * c2r2 + w * c2r3; + const resultZ = x * c2r0 + y * c2r1 + z * c2r2 + w * c2r3; // Multiply the point against each part of the 4th column, then add together - let resultW = x * c3r0 + y * c3r1 + z * c3r2 + w * c3r3; + const resultW = x * c3r0 + y * c3r1 + z * c3r2 + w * c3r3; return [ resultX + bias[0],