Post length comparison
This commit is contained in:
parent
81ad12b03f
commit
34cb62b1ad
|
@ -6,10 +6,10 @@
|
|||
import { Component, Hook, Prop, Watch } from '@f-list/vue-ts';
|
||||
import Vue from 'vue';
|
||||
import {Channel, Character} from '../fchat';
|
||||
import { Score, Scoring } from '../learn/matcher';
|
||||
import { Score } from '../learn/matcher';
|
||||
import core from './core';
|
||||
import { EventBus } from './preview/event-bus';
|
||||
import { kinkMatchWeights } from '../learn/matcher-types';
|
||||
import { kinkMatchWeights, Scoring } from '../learn/matcher-types';
|
||||
|
||||
|
||||
export function getStatusIcon(status: Character.Status): string {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { Component, Hook, Prop } from '@f-list/vue-ts';
|
||||
import {CreateElement, default as Vue, VNode, VNodeChildrenArrayContents} from 'vue';
|
||||
import {Channel} from '../fchat';
|
||||
import { Score, Scoring } from '../learn/matcher';
|
||||
import { Score } from '../learn/matcher';
|
||||
import {BBCodeView} from '../bbcode/view';
|
||||
import {formatTime} from './common';
|
||||
import core from './core';
|
||||
import {Conversation} from './interfaces';
|
||||
import UserView from './UserView.vue';
|
||||
import { Scoring } from '../learn/matcher-types';
|
||||
|
||||
const userPostfix: {[key: number]: string | undefined} = {
|
||||
[Conversation.Message.Type.Message]: ': ',
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
async mounted(): Promise<void> {
|
||||
log.debug('init.window.mounting');
|
||||
// top bar devtools
|
||||
// browserWindow.webContents.openDevTools({ mode: 'detach' });
|
||||
browserWindow.webContents.openDevTools({ mode: 'detach' });
|
||||
|
||||
updateSupportedLanguages(browserWindow.webContents.session.availableSpellCheckerLanguages);
|
||||
|
||||
|
@ -262,7 +262,7 @@
|
|||
);
|
||||
|
||||
// tab devtools
|
||||
// view.webContents.openDevTools();
|
||||
view.webContents.openDevTools();
|
||||
|
||||
// console.log('ADD TAB LANGUAGES', getSafeLanguages(this.settings.spellcheckLang), this.settings.spellcheckLang);
|
||||
view.webContents.session.setSpellCheckerLanguages(getSafeLanguages(this.settings.spellcheckLang));
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
export enum Scoring {
|
||||
MATCH = 1,
|
||||
WEAK_MATCH = 0.5,
|
||||
NEUTRAL = 0,
|
||||
WEAK_MISMATCH = -0.5,
|
||||
MISMATCH = -1
|
||||
}
|
||||
|
||||
|
||||
export enum TagId {
|
||||
Age = 1,
|
||||
Orientation = 2,
|
||||
|
@ -11,10 +20,23 @@ export enum TagId {
|
|||
RelationshipStatus = 42,
|
||||
Species = 9,
|
||||
LanguagePreference = 49,
|
||||
PostLength = 24,
|
||||
|
||||
Kinks = 99999
|
||||
}
|
||||
|
||||
|
||||
export enum PostLengthPreference {
|
||||
NoPreference = 63,
|
||||
VeryShort_1_2 = 26,
|
||||
Short_2_4 = 27,
|
||||
SemiParagraph_4_7 = 28,
|
||||
Paragraph_7_10 = 60,
|
||||
StrongParagraph_10_14 = 61,
|
||||
MultiParagraph_14_25 = 62
|
||||
}
|
||||
|
||||
|
||||
export enum Gender {
|
||||
Male = 1,
|
||||
Female = 2,
|
||||
|
@ -103,6 +125,72 @@ export const furryPreferenceMapping = {
|
|||
};
|
||||
|
||||
|
||||
export const postLengthPreferenceMapping = {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: 'multi-paragraph posts',
|
||||
[PostLengthPreference.StrongParagraph_10_14]: 'strong paragraph posts',
|
||||
[PostLengthPreference.Paragraph_7_10]: 'paragraph posts',
|
||||
[PostLengthPreference.SemiParagraph_4_7]: 'semi-paragraph posts',
|
||||
[PostLengthPreference.Short_2_4]: 'short posts',
|
||||
[PostLengthPreference.VeryShort_1_2]: 'very short posts'
|
||||
};
|
||||
|
||||
|
||||
export const postLengthPreferenceScoreMapping = {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.MATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.MATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.WEAK_MISMATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.MISMATCH
|
||||
},
|
||||
|
||||
[PostLengthPreference.StrongParagraph_10_14]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.MATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.MATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.MATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.WEAK_MISMATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.MISMATCH
|
||||
},
|
||||
|
||||
[PostLengthPreference.Paragraph_7_10]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.MATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.MATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.MISMATCH
|
||||
},
|
||||
|
||||
[PostLengthPreference.SemiParagraph_4_7]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.WEAK_MISMATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.MATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.WEAK_MATCH
|
||||
},
|
||||
|
||||
[PostLengthPreference.Short_2_4]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.WEAK_MISMATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.MATCH
|
||||
},
|
||||
|
||||
[PostLengthPreference.VeryShort_1_2]: {
|
||||
[PostLengthPreference.MultiParagraph_14_25]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.StrongParagraph_10_14]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.Paragraph_7_10]: Scoring.MISMATCH,
|
||||
[PostLengthPreference.SemiParagraph_4_7]: Scoring.WEAK_MATCH,
|
||||
[PostLengthPreference.Short_2_4]: Scoring.MATCH,
|
||||
[PostLengthPreference.VeryShort_1_2]: Scoring.MATCH
|
||||
}
|
||||
};
|
||||
|
||||
export interface GenderKinkIdMap {
|
||||
[key: number]: Kink
|
||||
}
|
||||
|
@ -485,37 +573,37 @@ export interface SpeciesMappingCache {
|
|||
|
||||
|
||||
export const kinkMatchWeights = {
|
||||
logBase: 10,
|
||||
weakMismatchThreshold: 0.3,
|
||||
weakMatchThreshold: 0.3,
|
||||
unicornThreshold: 8.0
|
||||
// logBase: 10,
|
||||
weakMismatchThreshold: 16,
|
||||
weakMatchThreshold: 16,
|
||||
unicornThreshold: 9
|
||||
};
|
||||
|
||||
export const kinkMatchScoreMap = {
|
||||
favorite: {
|
||||
favorite: 0.5,
|
||||
yes: 0.25,
|
||||
maybe: -0.5,
|
||||
no: -2
|
||||
favorite: 1,
|
||||
yes: 0.5,
|
||||
maybe: -0.65,
|
||||
no: -1.5
|
||||
},
|
||||
|
||||
yes: {
|
||||
favorite: 0.3,
|
||||
yes: 0.2,
|
||||
maybe: -0.15,
|
||||
favorite: 0.5,
|
||||
yes: 0.5,
|
||||
maybe: -0.25,
|
||||
no: -0.5
|
||||
},
|
||||
|
||||
maybe: {
|
||||
favorite: -0.5,
|
||||
yes: -0.2,
|
||||
yes: -0.25,
|
||||
maybe: 0,
|
||||
no: 0
|
||||
},
|
||||
|
||||
no: {
|
||||
favorite: -2,
|
||||
yes: -0.5,
|
||||
favorite: -1.5,
|
||||
yes: -0.65,
|
||||
maybe: 0,
|
||||
no: 0
|
||||
}
|
||||
|
@ -571,6 +659,7 @@ export interface KinkBucketScore {
|
|||
score: number;
|
||||
count: number;
|
||||
weighted: number;
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface MatchResultKinkScores {
|
||||
|
|
118
learn/matcher.ts
118
learn/matcher.ts
|
@ -3,22 +3,36 @@
|
|||
import * as _ from 'lodash';
|
||||
import { Character, CharacterInfotag, KinkChoice } from '../interfaces';
|
||||
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
||||
|
||||
// tslint:disable-next-line ban-ts-ignore
|
||||
// @ts-ignore
|
||||
import anyAscii from 'any-ascii';
|
||||
|
||||
import {Store} from '../site/character_page/data_store';
|
||||
import { Store } from '../site/character_page/data_store';
|
||||
|
||||
import {
|
||||
BodyType, fchatGenderMap,
|
||||
BodyType,
|
||||
fchatGenderMap,
|
||||
FurryPreference,
|
||||
Gender, genderKinkMapping,
|
||||
Kink, KinkBucketScore, kinkComparisonExclusionGroups, kinkComparisonExclusions, kinkComparisonSwaps,
|
||||
kinkMapping, kinkMatchScoreMap, kinkMatchWeights,
|
||||
KinkPreference, likelyHuman, mammalSpecies, nonAnthroSpecies,
|
||||
Gender,
|
||||
genderKinkMapping,
|
||||
Kink,
|
||||
KinkBucketScore,
|
||||
kinkComparisonExclusionGroups,
|
||||
kinkComparisonExclusions,
|
||||
kinkComparisonSwaps,
|
||||
kinkMapping,
|
||||
kinkMatchScoreMap,
|
||||
kinkMatchWeights,
|
||||
KinkPreference,
|
||||
likelyHuman,
|
||||
mammalSpecies,
|
||||
nonAnthroSpecies,
|
||||
Orientation,
|
||||
Species, SpeciesMap, speciesMapping, SpeciesMappingCache,
|
||||
PostLengthPreference, postLengthPreferenceMapping, postLengthPreferenceScoreMapping, Scoring,
|
||||
Species,
|
||||
SpeciesMap,
|
||||
speciesMapping,
|
||||
SpeciesMappingCache,
|
||||
speciesNames,
|
||||
SubDomRole,
|
||||
TagId
|
||||
|
@ -68,13 +82,6 @@ export interface MatchResult {
|
|||
theirAnalysis: CharacterAnalysis;
|
||||
}
|
||||
|
||||
export enum Scoring {
|
||||
MATCH = 1,
|
||||
WEAK_MATCH = 0.5,
|
||||
NEUTRAL = 0,
|
||||
WEAK_MISMATCH = -0.5,
|
||||
MISMATCH = -1
|
||||
}
|
||||
|
||||
export interface ScoreClassMap {
|
||||
[key: number]: string;
|
||||
|
@ -141,6 +148,7 @@ export class CharacterAnalysis {
|
|||
readonly furryPreference: FurryPreference | null;
|
||||
readonly age: number | null;
|
||||
readonly subDomRole: SubDomRole | null;
|
||||
readonly postLengthPreference: PostLengthPreference | null;
|
||||
|
||||
readonly isAnthro: boolean | null;
|
||||
readonly isHuman: boolean | null;
|
||||
|
@ -154,6 +162,7 @@ export class CharacterAnalysis {
|
|||
this.species = Matcher.species(c);
|
||||
this.furryPreference = Matcher.getTagValueList(TagId.FurryPreference, c);
|
||||
this.subDomRole = Matcher.getTagValueList(TagId.SubDomRole, c);
|
||||
this.postLengthPreference = Matcher.getTagValueList(TagId.PostLength, c);
|
||||
|
||||
const ageTag = Matcher.getTagValue(TagId.Age, c);
|
||||
|
||||
|
@ -391,7 +400,8 @@ export class Matcher {
|
|||
[TagId.FurryPreference]: this.resolveFurryPairingsScore(),
|
||||
[TagId.Species]: this.resolveSpeciesScore(),
|
||||
[TagId.SubDomRole]: this.resolveSubDomScore(),
|
||||
[TagId.Kinks]: this.resolveKinkScore(pronoun)
|
||||
[TagId.Kinks]: this.resolveKinkScore(pronoun),
|
||||
[TagId.PostLength]: this.resolvePostLengthScore()
|
||||
},
|
||||
|
||||
info: {
|
||||
|
@ -487,6 +497,24 @@ export class Matcher {
|
|||
return new Score(Scoring.NEUTRAL);
|
||||
}
|
||||
|
||||
private resolvePostLengthScore(): Score {
|
||||
const yourLength = this.yourAnalysis.postLengthPreference;
|
||||
const theirLength = this.theirAnalysis.postLengthPreference;
|
||||
|
||||
if (
|
||||
(!yourLength)
|
||||
|| (!theirLength)
|
||||
|| (yourLength === PostLengthPreference.NoPreference)
|
||||
|| (theirLength === PostLengthPreference.NoPreference)
|
||||
) {
|
||||
return new Score(Scoring.NEUTRAL);
|
||||
}
|
||||
|
||||
const score = postLengthPreferenceScoreMapping[yourLength][theirLength];
|
||||
|
||||
return this.formatScoring(score, postLengthPreferenceMapping[theirLength]);
|
||||
}
|
||||
|
||||
private resolveSpeciesScore(): Score {
|
||||
const you = this.you;
|
||||
const theirAnalysis = this.theirAnalysis;
|
||||
|
@ -568,23 +596,32 @@ export class Matcher {
|
|||
|
||||
|
||||
private resolveKinkScore(pronoun: string): Score {
|
||||
const kinkScore = this.resolveKinkBucketScore('all');
|
||||
// const kinkScore = this.resolveKinkBucketScore('all');
|
||||
|
||||
log.debug('report.score.kink', this.them.name, this.you.name, kinkScore.count, kinkScore.score, kinkScore.weighted);
|
||||
const scores = {
|
||||
favorite: this.resolveKinkBucketScore('favorite'),
|
||||
yes: this.resolveKinkBucketScore('yes'),
|
||||
maybe: this.resolveKinkBucketScore('maybe'),
|
||||
no: this.resolveKinkBucketScore('no')
|
||||
};
|
||||
|
||||
if (kinkScore.weighted === 0) {
|
||||
const weighted = scores.favorite.weighted + scores.yes.weighted + scores.maybe.weighted + scores.no.weighted;
|
||||
|
||||
log.debug('report.score.kink', this.them.name, this.you.name, scores, weighted);
|
||||
|
||||
if (weighted === 0) {
|
||||
return new Score(Scoring.NEUTRAL);
|
||||
}
|
||||
|
||||
if (kinkScore.weighted < 0) {
|
||||
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMismatchThreshold) {
|
||||
if (weighted < 0) {
|
||||
if (Math.abs(weighted) < kinkMatchWeights.weakMismatchThreshold) {
|
||||
return new Score(Scoring.WEAK_MISMATCH, `Hesitant about ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
return new Score(Scoring.MISMATCH, `Dislikes ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
if (Math.abs(kinkScore.weighted) < kinkMatchWeights.weakMatchThreshold) {
|
||||
if (Math.abs(weighted) < kinkMatchWeights.weakMatchThreshold) {
|
||||
return new Score(Scoring.WEAK_MATCH, `Likes ${pronoun} <span>kinks</span>`);
|
||||
}
|
||||
|
||||
|
@ -793,42 +830,49 @@ export class Matcher {
|
|||
(accum, yourKinkValue: any, yourKinkId: any) => {
|
||||
const theirKinkId = (yourKinkId in kinkComparisonSwaps) ? kinkComparisonSwaps[yourKinkId] : yourKinkId;
|
||||
|
||||
const isExcluded = (yourKinkId in kinkComparisonExclusions)
|
||||
|| ((Store.shared.kinks[yourKinkId]) && (Store.shared.kinks[yourKinkId].kink_group in kinkComparisonExclusionGroups));
|
||||
|
||||
const isBucketMatch = (yourKinkValue === bucket)
|
||||
|| (bucket === 'all')
|
||||
|| ((bucket === 'negative') && ((yourKinkValue === 'no') || (yourKinkValue === 'maybe')))
|
||||
|| ((bucket === 'positive') && ((yourKinkValue === 'favorite') || (yourKinkValue === 'yes')));
|
||||
|
||||
if ((isBucketMatch) && (!isExcluded)) {
|
||||
accum.total += 1;
|
||||
}
|
||||
|
||||
if (
|
||||
(!(theirKinkId in theirKinks))
|
||||
|| (yourKinkId in kinkComparisonExclusions)
|
||||
|| ((Store.shared.kinks[yourKinkId]) && (Store.shared.kinks[yourKinkId].kink_group in kinkComparisonExclusionGroups))
|
||||
|| (isExcluded)
|
||||
) {
|
||||
return accum;
|
||||
}
|
||||
|
||||
const theirKinkValue = theirKinks[theirKinkId] as any;
|
||||
|
||||
if (
|
||||
(yourKinkValue === bucket)
|
||||
|| (bucket === 'all')
|
||||
|| ((bucket === 'negative') && ((yourKinkValue === 'no') || (yourKinkValue === 'maybe')))
|
||||
|| ((bucket === 'positive') && ((yourKinkValue === 'favorite') || (yourKinkValue === 'yes')))
|
||||
) {
|
||||
if (isBucketMatch) {
|
||||
return {
|
||||
score: accum.score + this.getKinkMatchScore(yourKinkValue, theirKinkValue),
|
||||
count: accum.count + 1
|
||||
count: accum.count + 1,
|
||||
total: accum.total
|
||||
};
|
||||
}
|
||||
|
||||
// missed += 1;
|
||||
return accum;
|
||||
},
|
||||
{ score: 0, count: 0 }
|
||||
{ score: 0, count: 0, total: 0 }
|
||||
);
|
||||
|
||||
// const yourBucketCounts = this.countKinksByBucket(yourKinks);
|
||||
// const theirBucketCounts = this.countKinksByBucket(theirKinks);
|
||||
|
||||
result.weighted = (result.count === 0)
|
||||
result.weighted = ((result.count === 0) || (Math.abs(result.score) < 1))
|
||||
? 0
|
||||
: (
|
||||
(Math.log(result.count) / Math.log(kinkMatchWeights.logBase)) // log 8 base
|
||||
* (result.score / result.count)
|
||||
Math.log(result.total) * Math.log(Math.abs(result.score)) * Math.sign(result.score)
|
||||
// (Math.log(result.count) / Math.log(kinkMatchWeights.logBase)) // log 8 base
|
||||
// * (result.score / result.count)
|
||||
);
|
||||
|
||||
return result;
|
||||
|
@ -873,7 +917,7 @@ export class Matcher {
|
|||
|
||||
|
||||
private getKinkMatchScore(aValue: string, bValue: string): number {
|
||||
return _.get(kinkMatchScoreMap, `${aValue}.${bValue}`, 0);
|
||||
return _.get(kinkMatchScoreMap, `${aValue}.${bValue}`, 0) * 7; // forces range above 1.0
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,9 +3,10 @@ import * as _ from 'lodash';
|
|||
import core from '../chat/core';
|
||||
import {Character as ComplexCharacter, CharacterGroup, Guestbook} from '../site/character_page/interfaces';
|
||||
import { AsyncCache } from './async-cache';
|
||||
import { Matcher, MatchReport, Scoring } from './matcher';
|
||||
import { Matcher, MatchReport } from './matcher';
|
||||
import { PermanentIndexedStore } from './store/types';
|
||||
import { CharacterImage, SimpleCharacter } from '../interfaces';
|
||||
import { Scoring } from './matcher-types';
|
||||
|
||||
|
||||
export interface MetaRecord {
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
}
|
||||
|
||||
theirInterestIsRelevant(id: number): boolean {
|
||||
return ((id === TagId.FurryPreference) || (id === TagId.Orientation) || (id === TagId.SubDomRole));
|
||||
return ((id === TagId.FurryPreference) || (id === TagId.Orientation) || (id === TagId.SubDomRole) || (id === TagId.PostLength));
|
||||
}
|
||||
|
||||
yourInterestIsRelevant(id: number): boolean {
|
||||
|
|
|
@ -37,9 +37,9 @@
|
|||
import * as _ from 'lodash';
|
||||
import Vue from 'vue';
|
||||
import * as Utils from '../utils';
|
||||
import { Matcher, MatchReport, MatchResult, Score, Scoring } from '../../learn/matcher';
|
||||
import { Matcher, MatchReport, MatchResult, Score } from '../../learn/matcher';
|
||||
import core from '../../chat/core';
|
||||
import { TagId } from '../../learn/matcher-types';
|
||||
import { Scoring, TagId } from '../../learn/matcher-types';
|
||||
|
||||
export interface CssClassMap {
|
||||
[key: string]: boolean;
|
||||
|
|
Loading…
Reference in New Issue