Add smart resource transfer
This commit is contained in:
46
src/Action/FindSendResourcesPath.ts
Normal file
46
src/Action/FindSendResourcesPath.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { ActionController, registerAction } from './ActionController';
|
||||
import { FailTaskError, taskError, TryLaterError } from '../Errors';
|
||||
import { Resources } from '../Core/Resources';
|
||||
import { Coordinates } from '../Core/Village';
|
||||
import { aroundMinutes, timestamp } from '../utils';
|
||||
import { Args } from '../Queue/Args';
|
||||
import { Task } from '../Queue/TaskProvider';
|
||||
import { clickSendButton, fillSendResourcesForm } from '../Page/BuildingPage/MarketPage';
|
||||
import { VillageState } from '../VillageState';
|
||||
import { MerchantsInfo } from '../Core/Market';
|
||||
import { goToMarketSendResourcesPage, goToResourceViewPage } from '../Task/ActionBundles';
|
||||
import { ResourceTransferCalculator, ResourceTransferReport } from '../ResourceTransfer';
|
||||
import { ResourceTransferStorage } from '../Storage/ResourceTransferStorage';
|
||||
import { path } from '../Helpers/Path';
|
||||
import { MARKET_ID } from '../Core/Buildings';
|
||||
|
||||
@registerAction
|
||||
export class FindSendResourcesPath extends ActionController {
|
||||
async run(args: Args, task: Task): Promise<any> {
|
||||
const reports: Array<ResourceTransferReport> = [];
|
||||
const calculator = new ResourceTransferCalculator(this.villageFactory);
|
||||
|
||||
const villages = this.villageFactory.getAllVillages();
|
||||
for (let fromVillage of villages) {
|
||||
for (let toVillage of villages) {
|
||||
reports.push(calculator.calc(fromVillage.id, toVillage.id));
|
||||
}
|
||||
}
|
||||
|
||||
reports.sort((r1, r2) => r2.score - r1.score);
|
||||
|
||||
const bestReport = reports.shift();
|
||||
|
||||
if (!bestReport) {
|
||||
throw new FailTaskError('No best report for transfer resources');
|
||||
}
|
||||
|
||||
console.log('Best report', bestReport);
|
||||
|
||||
const storage = new ResourceTransferStorage();
|
||||
storage.storeReport(bestReport);
|
||||
|
||||
const marketPath = path('/build.php', { newdid: bestReport.fromVillageId, gid: MARKET_ID, t: 5 });
|
||||
window.location.assign(marketPath);
|
||||
}
|
||||
}
|
@ -1,123 +1,33 @@
|
||||
import { ActionController, registerAction } from './ActionController';
|
||||
import { taskError, TryLaterError } from '../Errors';
|
||||
import { Resources } from '../Core/Resources';
|
||||
import { Coordinates } from '../Core/Village';
|
||||
import { aroundMinutes, timestamp } from '../utils';
|
||||
import { Args } from '../Queue/Args';
|
||||
import { Task } from '../Queue/TaskProvider';
|
||||
import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage';
|
||||
import { VillageState } from '../VillageState';
|
||||
import { clickSendButton, fillSendResourcesForm } from '../Page/BuildingPage/MarketPage';
|
||||
import { ResourceTransferCalculator } from '../ResourceTransfer';
|
||||
import { ResourceTransferStorage } from '../Storage/ResourceTransferStorage';
|
||||
import { Resources } from '../Core/Resources';
|
||||
import { AbortTaskError } from '../Errors';
|
||||
|
||||
@registerAction
|
||||
export class SendResourcesAction extends ActionController {
|
||||
async run(args: Args, task: Task): Promise<any> {
|
||||
this.ensureSameVillage(args, task);
|
||||
const storage = new ResourceTransferStorage();
|
||||
const savedReport = storage.getReport();
|
||||
|
||||
const senderVillageId = args.villageId || taskError('No source village id');
|
||||
const targetVillageId = args.targetVillageId || taskError('No target village id');
|
||||
const fromVillage = this.villageFactory.getVillage(savedReport.fromVillageId);
|
||||
const toVillage = this.villageFactory.getVillage(savedReport.toVillageId);
|
||||
|
||||
const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates'));
|
||||
const coordinates = toVillage.crd;
|
||||
|
||||
const senderVillage = this.villageFactory.createState(senderVillageId);
|
||||
const recipientVillage = this.villageFactory.createState(targetVillageId);
|
||||
const calculator = new ResourceTransferCalculator(this.villageFactory);
|
||||
const report = calculator.calc(fromVillage.id, toVillage.id);
|
||||
|
||||
const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage);
|
||||
console.log('To transfer report', report);
|
||||
|
||||
console.log('To transfer res', readyToTransfer);
|
||||
if (Resources.fromObject(report.resources).empty()) {
|
||||
throw new AbortTaskError('No resources to transfer');
|
||||
}
|
||||
|
||||
// Schedule recurrent task
|
||||
const timeout = senderVillage.settings.sendResourcesTimeout;
|
||||
this.scheduler.scheduleTask(task.name, task.args, timestamp() + aroundMinutes(timeout));
|
||||
|
||||
fillSendResourcesForm(readyToTransfer, coordinates);
|
||||
fillSendResourcesForm(report.resources, coordinates);
|
||||
clickSendButton();
|
||||
}
|
||||
|
||||
private getMerchantsCapacity(timeout: number): number {
|
||||
const merchants = grabMerchantsInfo();
|
||||
const capacity = merchants.available * merchants.carry;
|
||||
if (!capacity) {
|
||||
throw new TryLaterError(aroundMinutes(timeout), 'No merchants');
|
||||
}
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private getSenderAvailableResources(senderState: VillageState): Resources {
|
||||
const balance = senderState.required.balance;
|
||||
const free = balance.max(Resources.zero());
|
||||
|
||||
console.table([
|
||||
{ name: 'Sender balance', ...balance },
|
||||
{ name: 'Sender free', ...free },
|
||||
]);
|
||||
|
||||
const amount = free.amount();
|
||||
const threshold = senderState.settings.sendResourcesThreshold;
|
||||
const timeout = senderState.settings.sendResourcesTimeout;
|
||||
|
||||
if (amount < threshold) {
|
||||
throw new TryLaterError(
|
||||
aroundMinutes(timeout),
|
||||
`No free resources (amount ${amount} < threshold ${threshold})`
|
||||
);
|
||||
}
|
||||
|
||||
return free;
|
||||
}
|
||||
|
||||
private getRecipientRequirements(recipientState: VillageState, timeout: number): Resources {
|
||||
const maxPossibleToStore = recipientState.storage.capacity.sub(recipientState.performance);
|
||||
const currentResources = recipientState.resources;
|
||||
const incomingResources = recipientState.incomingResources;
|
||||
const requirementResources = recipientState.required.resources;
|
||||
const missingResources = requirementResources
|
||||
.min(maxPossibleToStore)
|
||||
.sub(incomingResources)
|
||||
.sub(currentResources)
|
||||
.max(Resources.zero());
|
||||
|
||||
console.table([
|
||||
{ name: 'Recipient max possible', ...maxPossibleToStore },
|
||||
{ name: 'Recipient resources', ...currentResources },
|
||||
{ name: 'Recipient incoming', ...incomingResources },
|
||||
{ name: 'Recipient requirements', ...requirementResources },
|
||||
{ name: 'Recipient missing', ...missingResources },
|
||||
]);
|
||||
|
||||
if (missingResources.empty()) {
|
||||
throw new TryLaterError(aroundMinutes(timeout), 'No missing resources');
|
||||
}
|
||||
|
||||
return missingResources;
|
||||
}
|
||||
|
||||
private getResourcesForTransfer(senderState: VillageState, recipientState: VillageState): Resources {
|
||||
const multiplier = senderState.settings.sendResourcesMultiplier;
|
||||
const timeout = senderState.settings.sendResourcesTimeout;
|
||||
const senderReadyResources = this.getSenderAvailableResources(senderState).downTo(multiplier);
|
||||
const recipientNeedResources = this.getRecipientRequirements(recipientState, timeout).upTo(multiplier);
|
||||
const contractResources = senderReadyResources.min(recipientNeedResources);
|
||||
const merchantsCapacity = this.getMerchantsCapacity(timeout);
|
||||
|
||||
let readyToTransfer = contractResources;
|
||||
if (contractResources.amount() > merchantsCapacity) {
|
||||
const merchantScale = merchantsCapacity / contractResources.amount();
|
||||
readyToTransfer = contractResources.scale(merchantScale).downTo(multiplier);
|
||||
}
|
||||
|
||||
if (readyToTransfer.empty()) {
|
||||
throw new TryLaterError(aroundMinutes(timeout), 'Not enough resources to transfer');
|
||||
}
|
||||
|
||||
console.log('Merchants capacity', merchantsCapacity);
|
||||
|
||||
console.table([
|
||||
{ name: 'Sender', ...senderReadyResources },
|
||||
{ name: 'Recipient', ...recipientNeedResources },
|
||||
{ name: 'Prepared', ...contractResources },
|
||||
{ name: 'Ready to transfer', ...readyToTransfer },
|
||||
]);
|
||||
|
||||
return readyToTransfer;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user