Better profile scoring weights
This commit is contained in:
parent
0b5b005d91
commit
fec16d1f89
|
@ -28,6 +28,11 @@
|
|||
<search-history ref="searchHistory" :callback="updateSearch" :curSearch="data"></search-history>
|
||||
</div>
|
||||
<div v-else-if="state === 'results'" class="results">
|
||||
<div class="debug" v-show="false">
|
||||
<textarea v-model="debugSearchJson"></textarea>
|
||||
<button class="btn" @click.prevent="debugUpdateResults()">Update</button>
|
||||
</div>
|
||||
|
||||
<h4 v-if="hasReceivedResults">
|
||||
{{results.length}} {{l('characterSearch.results')}}
|
||||
|
||||
|
@ -66,7 +71,14 @@
|
|||
import {EventBus} from './preview/event-bus';
|
||||
import CharacterSearchHistory from './CharacterSearchHistory.vue';
|
||||
import { Matcher } from '../learn/matcher';
|
||||
import { nonAnthroSpecies, Species, speciesMapping, speciesNames } from '../learn/matcher-types';
|
||||
import {
|
||||
kinkMatchScoreMap,
|
||||
kinkMatchWeights,
|
||||
nonAnthroSpecies,
|
||||
Species,
|
||||
speciesMapping,
|
||||
speciesNames
|
||||
} from '../learn/matcher-types';
|
||||
import { CharacterCacheRecord } from '../learn/profile-cache';
|
||||
import Bluebird from 'bluebird';
|
||||
|
||||
|
@ -140,6 +152,15 @@
|
|||
state = 'search';
|
||||
hasReceivedResults = false;
|
||||
|
||||
debugSearchJson = JSON.stringify(
|
||||
{
|
||||
scoreMap: kinkMatchScoreMap,
|
||||
weights: kinkMatchWeights
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
|
||||
private countUpdater?: ResultCountUpdater;
|
||||
|
||||
data: ExtendedSearchData = {
|
||||
|
@ -194,6 +215,39 @@
|
|||
}
|
||||
|
||||
|
||||
async debugUpdateResults(): Promise<void> {
|
||||
if (this.state !== 'results') {
|
||||
return;
|
||||
}
|
||||
|
||||
const data = JSON.parse(this.debugSearchJson);
|
||||
|
||||
_.assign(kinkMatchScoreMap, data.scoreMap);
|
||||
_.assign(kinkMatchWeights, data.weights);
|
||||
|
||||
core.cache.profileCache.clear();
|
||||
|
||||
const results = this.results;
|
||||
|
||||
this.results = [];
|
||||
|
||||
await Bluebird.delay(10);
|
||||
|
||||
// pre-warm cache
|
||||
await Bluebird.mapSeries(
|
||||
results,
|
||||
(c) => core.cache.profileCache.get(c.character.name)
|
||||
);
|
||||
|
||||
this.resultsPending = this.countPendingResults(undefined, results);
|
||||
|
||||
this.countUpdater?.start();
|
||||
this.resort(results);
|
||||
|
||||
console.log('Done!');
|
||||
}
|
||||
|
||||
|
||||
@Hook('mounted')
|
||||
mounted(): void {
|
||||
core.connection.onMessage('ERR', (data) => {
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
import { Component, Hook, Prop, Watch } from '@f-list/vue-ts';
|
||||
import Vue from 'vue';
|
||||
import {Channel, Character} from '../fchat';
|
||||
import { Matcher, Score, Scoring } from '../learn/matcher';
|
||||
import { Score, Scoring } from '../learn/matcher';
|
||||
import core from './core';
|
||||
import { EventBus } from './preview/event-bus';
|
||||
import { kinkMatchWeights } from '../learn/matcher-types';
|
||||
|
||||
|
||||
export function getStatusIcon(status: Character.Status): string {
|
||||
|
@ -71,7 +72,7 @@ export function getStatusClasses(
|
|||
const cache = core.cache.profileCache.getSync(character.name);
|
||||
|
||||
if (cache) {
|
||||
if ((cache.match.searchScore > Matcher.UNICORN_LEVEL) && (cache.match.matchScore === Scoring.MATCH)) {
|
||||
if ((cache.match.searchScore > kinkMatchWeights.unicornThreshold) && (cache.match.matchScore === Scoring.MATCH)) {
|
||||
matchClass = 'match-found unicorn';
|
||||
matchScore = 'unicorn';
|
||||
} else {
|
||||
|
|
|
@ -11,4 +11,8 @@ export abstract class AsyncCache<RecordType> {
|
|||
static nameKey(name: string): string {
|
||||
return Cache.nameKey(name);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.cache = {};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -484,32 +484,39 @@ export interface SpeciesMappingCache {
|
|||
}
|
||||
|
||||
|
||||
export const kinkMatchWeights = {
|
||||
logBase: 10,
|
||||
weakMismatchThreshold: 0.3,
|
||||
weakMatchThreshold: 0.3,
|
||||
unicornThreshold: 8.0
|
||||
};
|
||||
|
||||
export const kinkMatchScoreMap = {
|
||||
favorite: {
|
||||
favorite: 0.55,
|
||||
favorite: 0.5,
|
||||
yes: 0.25,
|
||||
maybe: -0.5,
|
||||
no: -1.0
|
||||
no: -2
|
||||
},
|
||||
|
||||
yes: {
|
||||
favorite: 0.20,
|
||||
yes: 0.20,
|
||||
maybe: -0.25,
|
||||
favorite: 0.3,
|
||||
yes: 0.2,
|
||||
maybe: -0.15,
|
||||
no: -0.5
|
||||
},
|
||||
|
||||
maybe: {
|
||||
favorite: -0.5,
|
||||
yes: -0.25,
|
||||
yes: -0.2,
|
||||
maybe: 0,
|
||||
no: 0
|
||||
},
|
||||
|
||||
no: {
|
||||
favorite: -1.0,
|
||||
favorite: -2,
|
||||
yes: -0.5,
|
||||
maybe: -0.1,
|
||||
maybe: 0,
|
||||
no: 0
|
||||
}
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
FurryPreference,
|
||||
Gender, genderKinkMapping,
|
||||
Kink, KinkBucketScore, kinkComparisonExclusionGroups, kinkComparisonExclusions, kinkComparisonSwaps,
|
||||
kinkMapping, kinkMatchScoreMap,
|
||||
kinkMapping, kinkMatchScoreMap, kinkMatchWeights,
|
||||
KinkPreference, likelyHuman, mammalSpecies, nonAnthroSpecies,
|
||||
Orientation,
|
||||
Species, SpeciesMap, speciesMapping, SpeciesMappingCache,
|
||||
|
@ -180,9 +180,6 @@ export class Matcher {
|
|||
readonly yourAnalysis: CharacterAnalysis;
|
||||
readonly theirAnalysis: CharacterAnalysis;
|
||||
|
||||
static readonly UNICORN_LEVEL = 6.0;
|
||||
|
||||
|
||||
constructor(you: Character, them: Character, yourAnalysis?: CharacterAnalysis, theirAnalysis?: CharacterAnalysis) {
|
||||
this.you = you;
|
||||
this.them = them;
|
||||
|
@ -198,8 +195,8 @@ export class Matcher {
|
|||
const youThem = new Matcher(you, them, yourAnalysis, theirAnalysis);
|
||||
const themYou = new Matcher(them, you, theirAnalysis, yourAnalysis);
|
||||
|
||||
const youThemMatch = youThem.match();
|
||||
const themYouMatch = themYou.match();
|
||||
const youThemMatch = youThem.match('their');
|
||||
const themYouMatch = themYou.match('your');
|
||||
|
||||
const report: MatchReport = {
|
||||
_isVue: true,
|
||||
|
@ -240,8 +237,8 @@ export class Matcher {
|
|||
const youThem = new Matcher(yourAnalysis.character, theirAnalysis.character, yourAnalysis.analysis, theirAnalysis.analysis);
|
||||
const themYou = new Matcher(theirAnalysis.character, yourAnalysis.character, theirAnalysis.analysis, yourAnalysis.analysis);
|
||||
|
||||
const youThemMatch = youThem.match();
|
||||
const themYouMatch = themYou.match();
|
||||
const youThemMatch = youThem.match('their');
|
||||
const themYouMatch = themYou.match('your');
|
||||
|
||||
const report: MatchReport = {
|
||||
_isVue: true,
|
||||
|
@ -377,7 +374,7 @@ export class Matcher {
|
|||
return (finalScore === null) ? Scoring.NEUTRAL : finalScore;
|
||||
}
|
||||
|
||||
match(): MatchResult {
|
||||
match(pronoun: string): MatchResult {
|
||||
const data: MatchResult = {
|
||||
you: this.you,
|
||||
them: this.them,
|
||||
|
@ -394,7 +391,7 @@ export class Matcher {
|
|||
[TagId.FurryPreference]: this.resolveFurryPairingsScore(),
|
||||
[TagId.Species]: this.resolveSpeciesScore(),
|
||||
[TagId.SubDomRole]: this.resolveSubDomScore(),
|
||||
[TagId.Kinks]: this.resolveKinkScore()
|
||||
[TagId.Kinks]: this.resolveKinkScore(pronoun)
|
||||
},
|
||||
|
||||
info: {
|
||||
|
@ -570,7 +567,7 @@ export class Matcher {
|
|||
}
|
||||
|
||||
|
||||
private resolveKinkScore(): Score {
|
||||
private resolveKinkScore(pronoun: string): Score {
|
||||
const kinkScore = this.resolveKinkBucketScore('all');
|
||||
|
||||
log.debug('report.score.kink', this.them.name, this.you.name, kinkScore.count, kinkScore.score, kinkScore.weighted);
|
||||
|
@ -580,18 +577,18 @@ export class Matcher {
|
|||
}
|
||||
|
||||
if (kinkScore.weighted < 0) {
|
||||
if (Math.abs(kinkScore.weighted) < 0.45) {
|
||||
return new Score(Scoring.WEAK_MISMATCH, 'Challenging <span>kinks</span>');
|
||||
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMismatchThreshold) {
|
||||
return new Score(Scoring.WEAK_MISMATCH, `Hesitant about ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
return new Score(Scoring.MISMATCH, 'Mismatching <span>kinks</span>');
|
||||
return new Score(Scoring.MISMATCH, `Dislikes ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
if (Math.abs(kinkScore.weighted) < 0.45) {
|
||||
return new Score(Scoring.WEAK_MATCH, 'Good <span>kinks</span>');
|
||||
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMatchThreshold) {
|
||||
return new Score(Scoring.WEAK_MATCH, `Likes ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
return new Score(Scoring.MATCH, 'Great <span>kinks</span>');
|
||||
return new Score(Scoring.MATCH, `Loves ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
|
||||
|
@ -789,6 +786,8 @@ export class Matcher {
|
|||
const yourKinks = this.getAllStandardKinks(this.you);
|
||||
const theirKinks = this.getAllStandardKinks(this.them);
|
||||
|
||||
// let missed = 0;
|
||||
|
||||
const result: any = _.reduce(
|
||||
yourKinks,
|
||||
(accum, yourKinkValue: any, yourKinkId: any) => {
|
||||
|
@ -816,15 +815,19 @@ export class Matcher {
|
|||
};
|
||||
}
|
||||
|
||||
// missed += 1;
|
||||
return accum;
|
||||
},
|
||||
{ score: 0, count: 0 }
|
||||
);
|
||||
|
||||
// const yourBucketCounts = this.countKinksByBucket(yourKinks);
|
||||
// const theirBucketCounts = this.countKinksByBucket(theirKinks);
|
||||
|
||||
result.weighted = (result.count === 0)
|
||||
? 0
|
||||
: (
|
||||
(Math.log(result.count) / Math.log(5)) // log 5 base
|
||||
(Math.log(result.count) / Math.log(kinkMatchWeights.logBase)) // log 8 base
|
||||
* (result.score / result.count)
|
||||
);
|
||||
|
||||
|
@ -832,6 +835,23 @@ export class Matcher {
|
|||
}
|
||||
|
||||
|
||||
// private countKinksByBucket(kinks: { [key: number]: KinkChoice }): { favorite: number, yes: number, maybe: number, no: number } {
|
||||
// return _.reduce(
|
||||
// kinks,
|
||||
// (accum, kinkValue) => {
|
||||
// accum[kinkValue] += 1;
|
||||
// return accum;
|
||||
// },
|
||||
// {
|
||||
// favorite: 0,
|
||||
// yes: 0,
|
||||
// maybe: 0,
|
||||
// no: 0
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
|
||||
private getAllStandardKinks(c: Character): { [key: number]: KinkChoice } {
|
||||
const kinks = _.pickBy(c.kinks, _.isString);
|
||||
|
||||
|
|
Loading…
Reference in New Issue