Improve send resources task

This commit is contained in:
Anton Vakhrushev 2020-05-10 10:52:49 +03:00
parent d1a2128411
commit c5de5ca901
2 changed files with 78 additions and 63 deletions

View File

@ -1,27 +1,30 @@
import { ActionController, taskError, registerAction } from './ActionController'; import { ActionController, taskError, registerAction } from './ActionController';
import { AbortTaskError, TryLaterError } from '../Errors'; import { TryLaterError } from '../Errors';
import { Resources } from '../Core/Resources'; import { Resources } from '../Core/Resources';
import { Coordinates, Village } from '../Core/Village'; import { Coordinates } from '../Core/Village';
import { grabVillageResources } from '../Page/ResourcesBlock';
import { grabActiveVillageId, grabVillageList } from '../Page/VillageBlock';
import { aroundMinutes, timestamp } from '../utils'; import { aroundMinutes, timestamp } from '../utils';
import { VillageStorage } from '../Storage/VillageStorage';
import { Args } from '../Queue/Args'; import { Args } from '../Queue/Args';
import { Task } from '../Queue/TaskProvider'; import { Task } from '../Queue/TaskProvider';
import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage'; import { clickSendButton, fillSendResourcesForm, grabMerchantsInfo } from '../Page/BuildingPage/MarketPage';
import { VillageState } from '../VillageState';
const TIMEOUT = 15; const TIMEOUT = 15;
const AMOUNT_THRESHOLD = 100;
@registerAction @registerAction
export class SendResourcesAction extends ActionController { export class SendResourcesAction extends ActionController {
async run(args: Args, task: Task): Promise<any> { async run(args: Args, task: Task): Promise<any> {
this.ensureSameVillage(args, task);
const senderVillageId = args.villageId || taskError('No source village id');
const targetVillageId = args.targetVillageId || taskError('No target village id');
const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates')); const coordinates = Coordinates.fromObject(args.coordinates || taskError('No coordinates'));
const recipientVillage = args.targetVillageId const senderVillage = this.villageStateRepository.getVillageState(senderVillageId);
? this.findRecipientVillageById(args.targetVillageId) const recipientVillage = this.villageStateRepository.getVillageState(targetVillageId);
: this.findRecipientVillage(coordinates);
const readyToTransfer = this.getResourcesForTransfer(recipientVillage.id); const readyToTransfer = this.getResourcesForTransfer(senderVillage, recipientVillage);
console.log('To transfer res', readyToTransfer); console.log('To transfer res', readyToTransfer);
@ -32,24 +35,6 @@ export class SendResourcesAction extends ActionController {
clickSendButton(); clickSendButton();
} }
private findRecipientVillageById(villageId: number): Village {
const villageList = grabVillageList();
const village = villageList.find(v => v.id === villageId);
if (!village) {
throw new AbortTaskError('No village');
}
return village;
}
private findRecipientVillage(coordinates: Coordinates): Village {
const villageList = grabVillageList();
const village = villageList.find(v => v.crd.eq(coordinates));
if (!village) {
throw new AbortTaskError('No village');
}
return village;
}
private getMerchantsCapacity(): number { private getMerchantsCapacity(): number {
const merchants = grabMerchantsInfo(); const merchants = grabMerchantsInfo();
const capacity = merchants.available * merchants.carry; const capacity = merchants.available * merchants.carry;
@ -59,55 +44,67 @@ export class SendResourcesAction extends ActionController {
return capacity; return capacity;
} }
private getSenderAvailableResources(): Resources { private getSenderAvailableResources(senderState: VillageState): Resources {
const villageId = grabActiveVillageId(); const balance = senderState.required.balance;
const resources = grabVillageResources(); const free = balance.max(Resources.zero());
const requirements = this.scheduler.getVillageRequiredResources(villageId);
const free = resources.sub(requirements).max(Resources.zero()); console.table([
console.log('Sender res', resources); { name: 'Sender balance', ...balance },
console.log('Sender req', requirements); { name: 'Sender free', ...free },
console.log('Sender free', free); ]);
if (free.amount() < 100) {
if (free.amount() < AMOUNT_THRESHOLD) {
throw new TryLaterError(aroundMinutes(TIMEOUT), 'Little free resources'); throw new TryLaterError(aroundMinutes(TIMEOUT), 'Little free resources');
} }
return free; return free;
} }
private getRecipientRequirements(villageId: number): Resources { private getRecipientRequirements(recipientState: VillageState): Resources {
const state = new VillageStorage(villageId); const maxPossibleToStore = recipientState.storage.sub(recipientState.performance);
const resources = state.getResources(); const currentResources = recipientState.resources;
const incoming = state.getIncomingMerchants().reduce((m, i) => m.add(i.resources), Resources.zero()); const incomingResources = recipientState.incomingResources;
const requirements = this.scheduler.getVillageRequiredResources(villageId); const requirementResources = recipientState.required.resources;
const missing = requirements const missingResources = requirementResources
.sub(incoming) .min(maxPossibleToStore)
.sub(resources) .sub(incomingResources)
.sub(currentResources)
.max(Resources.zero()); .max(Resources.zero());
console.log('Recipient res', resources);
console.log('Recipient incoming', incoming); console.table([
console.log('Recipient req', requirements); { name: 'Recipient max possible', ...maxPossibleToStore },
console.log('Recipient missing', missing); { name: 'Recipient resources', ...currentResources },
if (missing.empty()) { { name: 'Recipient incoming', ...incomingResources },
{ name: 'Recipient requirements', ...requirementResources },
{ name: 'Recipient missing', ...missingResources },
]);
if (missingResources.empty()) {
throw new TryLaterError(aroundMinutes(TIMEOUT), 'No missing resources'); throw new TryLaterError(aroundMinutes(TIMEOUT), 'No missing resources');
} }
return missing;
return missingResources;
} }
private getResourcesForTransfer(recipientVillageId: number): Resources { private getResourcesForTransfer(senderState: VillageState, recipientState: VillageState): Resources {
const senderResources = this.getSenderAvailableResources(); const senderReadySendResources = this.getSenderAvailableResources(senderState);
const recipientNeeds = this.getRecipientRequirements(recipientVillageId); const recipientNeedsResources = this.getRecipientRequirements(recipientState);
const prepared = senderResources.min(recipientNeeds); const contractResources = senderReadySendResources.min(recipientNeedsResources);
const capacity = this.getMerchantsCapacity(); const merchantsCapacity = this.getMerchantsCapacity();
let readyToTransfer = prepared; let readyToTransfer = contractResources;
if (prepared.amount() > capacity) { if (contractResources.amount() > merchantsCapacity) {
readyToTransfer = prepared.scale(capacity / prepared.amount()); const merchantScale = merchantsCapacity / contractResources.amount();
readyToTransfer = contractResources.scale(merchantScale);
} }
console.log('Sender', senderResources); console.log('Merchants capacity', merchantsCapacity);
console.log('Recipient', recipientNeeds);
console.log('Prepared', prepared); console.table([
console.log('Capacity', capacity); { name: 'Sender', ...senderReadySendResources },
console.log('Ready to transfer', readyToTransfer); { name: 'Recipient', ...recipientNeedsResources },
{ name: 'Prepared', ...contractResources },
{ name: 'Ready to transfer', ...readyToTransfer },
]);
return readyToTransfer; return readyToTransfer;
} }

View File

@ -16,14 +16,32 @@ interface StorageBalance {
} }
interface RequiredResources { interface RequiredResources {
/**
* Required resources (always positive)
*/
resources: Resources; resources: Resources;
/**
* Balance resources (current - required), may be negative
*/
balance: Resources; balance: Resources;
/**
* Time to gather all type of resources (slowest time)
*/
time: GatheringTime; time: GatheringTime;
} }
interface VillageOwnState { interface VillageOwnState {
/**
* Village id
*/
id: number; id: number;
/**
* Village object
*/
village: Village; village: Village;
/**
* Current village resources
*/
resources: Resources; resources: Resources;
performance: Resources; performance: Resources;
storage: Resources; storage: Resources;