122 lines
4.0 KiB
JavaScript
122 lines
4.0 KiB
JavaScript
import { Actor } from './actor.js';
|
|
import { Graph } from './graph.js';
|
|
import { Action } from './action.js';
|
|
import { CryptoUtil } from './crypto.js';
|
|
import params from '../params.js';
|
|
|
|
class Post extends Actor {
|
|
constructor(forum, authorPublicKey, postContent) {
|
|
const index = forum.posts.countVertices();
|
|
const name = `Post${index + 1}`;
|
|
super(name, forum.scene);
|
|
this.id = postContent.id ?? CryptoUtil.randomUUID();
|
|
this.authorPublicKey = authorPublicKey;
|
|
this.value = 0;
|
|
this.citations = postContent.citations;
|
|
this.title = postContent.title;
|
|
this.totalCitationWeight = this.citations.reduce((total, { weight }) => total += weight, 0);
|
|
if (this.totalCitationWeight > params.revaluationLimit) {
|
|
throw new Error('Post total citation weight exceeds revaluation limit '
|
|
+ `(${this.totalCitationWeight} > ${params.revaluationLimit})`);
|
|
}
|
|
if (this.citations.some(({ weight }) => Math.abs(weight) > 1)) {
|
|
throw new Error('Each citation weight must be in the range [-1, 1]');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Purpose: Maintain a directed, acyclic, weighted graph of posts referencing other posts
|
|
*/
|
|
export class Forum extends Actor {
|
|
constructor(name, scene) {
|
|
super(name, scene);
|
|
this.posts = new Graph(scene);
|
|
this.actions = {
|
|
addPost: new Action('add post', scene),
|
|
propagateValue: new Action('propagate value', this.scene),
|
|
};
|
|
}
|
|
|
|
async addPost(authorId, postContent) {
|
|
const post = new Post(this, authorId, postContent);
|
|
await this.actions.addPost.log(this, post);
|
|
this.posts.addVertex(post.id, post, post.title);
|
|
if (this.scene.flowchart) {
|
|
this.scene.flowchart.log(`${post.id} -- value --> ${post.id}_value[0]`);
|
|
}
|
|
for (const { postId: citedPostId, weight } of post.citations) {
|
|
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
|
if (this.scene.flowchart) {
|
|
this.scene.flowchart.log(`${post.id} -- ${weight} --> ${citedPostId}`);
|
|
}
|
|
}
|
|
return post.id;
|
|
}
|
|
|
|
getPost(postId) {
|
|
return this.posts.getVertexData(postId);
|
|
}
|
|
|
|
getPosts() {
|
|
return this.posts.getVerticesData();
|
|
}
|
|
|
|
async setPostValue(postId, value) {
|
|
const post = this.getPost(postId);
|
|
post.value = value;
|
|
await post.setValue('value', value);
|
|
if (this.scene.flowchart) {
|
|
this.scene.flowchart.log(`${post.id}_value[${value}]`);
|
|
}
|
|
}
|
|
|
|
getPostValue(postId) {
|
|
const post = this.getPost(postId);
|
|
return post.value;
|
|
}
|
|
|
|
getTotalValue() {
|
|
return this.getPosts().reduce((total, { value }) => total += value, 0);
|
|
}
|
|
|
|
async propagateValue(fromActor, postId, increment, depth = 0) {
|
|
if (depth === 0 && this.scene.flowchart) {
|
|
const randomId = CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8);
|
|
this.scene.flowchart.log(`${postId}_initial_value_${randomId}[${increment}] -- initial value --> ${postId}`);
|
|
}
|
|
if (params.maxPropagationDepth >= 0 && depth > params.maxPropagationDepth) {
|
|
return [];
|
|
}
|
|
const post = this.getPost(postId);
|
|
this.actions.propagateValue.log(fromActor, post, `(increment: ${increment})`);
|
|
|
|
const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight);
|
|
|
|
const rewards = new Map();
|
|
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
|
const addRewards = (r) => {
|
|
for (const [id, value] of r) {
|
|
addReward(id, value);
|
|
}
|
|
};
|
|
|
|
// Increment the value of the post
|
|
// Apply leaching value
|
|
const currentValue = this.getPostValue(postId);
|
|
const newValue = currentValue + adjustedIncrement;
|
|
await this.setPostValue(postId, newValue);
|
|
|
|
// Award reputation to post author
|
|
console.log('reward for post author', post.authorPublicKey, adjustedIncrement);
|
|
addReward(post.authorPublicKey, adjustedIncrement);
|
|
|
|
// Recursively distribute reputation to citations, according to weights
|
|
for (const { postId: citedPostId, weight } of post.citations) {
|
|
addRewards(await this.propagateValue(post, citedPostId, weight * increment, depth + 1));
|
|
}
|
|
|
|
return rewards;
|
|
}
|
|
}
|