Simplify village state manipulation

This commit is contained in:
Anton Vakhrushev 2020-10-10 13:27:24 +03:00
parent 9bbe3ff78d
commit d03894c89d
11 changed files with 80 additions and 179 deletions

View File

@ -111,7 +111,8 @@ export class ControlPanel {
const getBuildingsInQueue = () =>
this.villageFactory
.createTaskCollection(villageId)
.getById(villageId)
.taskCollection()
.getTasks()
.filter((t) => t.name === UpgradeBuildingTask.name)
.map((t) => t.args.buildId || 0);
@ -136,7 +137,7 @@ export class ControlPanel {
const buildPage = new BuildingPageController(
this.scheduler,
getBuildingPageAttributes(),
this.villageFactory.createController(villageId)
this.villageFactory.getById(villageId).controller()
);
buildPage.run();
}

View File

@ -76,7 +76,7 @@ export function createStore(villageFactory: VillageFactory) {
},
actions: {
[Actions.OpenVillageEditor]({ commit }, { villageId }) {
const state = villageFactory.createState(villageId);
const state = villageFactory.getById(villageId).state();
const settings = state.settings;
commit(Mutations.SetVillageSettings, {
villageId: state.id,
@ -89,7 +89,7 @@ export function createStore(villageFactory: VillageFactory) {
[Actions.SaveVillageSettings]({ state }) {
const villageName = state.villageSettings.villageName;
const villageId = state.villageSettings.villageId;
const villageState = villageFactory.createState(villageId);
const villageState = villageFactory.getById(villageId).state();
const newSettings: VillageSettings = {
sendResourcesThreshold:
state.villageSettings.sendResourcesThreshold ||
@ -104,19 +104,19 @@ export function createStore(villageFactory: VillageFactory) {
notify(`Настройки для ${villageName} сохранены`);
},
[Actions.ToggleVillageReceiveMode]({}, { villageId }) {
const controller = villageFactory.createController(villageId);
const controller = villageFactory.getById(villageId).controller();
controller.toggleReceiveResourcesMode();
},
[Actions.RemoveVillageTask]({}, { villageId, taskId }) {
const controller = villageFactory.createController(villageId);
const controller = villageFactory.getById(villageId).controller();
controller.removeTask(taskId);
},
[Actions.UpVillageTask]({}, { villageId, taskId }) {
const controller = villageFactory.createController(villageId);
const controller = villageFactory.getById(villageId).controller();
controller.upTask(taskId);
},
[Actions.DownVillageTask]({}, { villageId, taskId }) {
const controller = villageFactory.createController(villageId);
const controller = villageFactory.getById(villageId).controller();
controller.downTask(taskId);
},
},

View File

@ -24,8 +24,8 @@ export class GrabberManager {
}
private createGrabbers(): Array<Grabber> {
const storage = this.factory.createStorageForActiveVillage();
const taskCollection = this.factory.createTaskCollectionForActiveVillage();
const storage = this.factory.active().storage();
const taskCollection = this.factory.active().taskCollection();
const grabbers: Array<Grabber> = [];
grabbers.push(new VillageResourceGrabber(taskCollection, storage));
grabbers.push(new VillageOverviewPageGrabber(taskCollection, storage));

View File

@ -19,11 +19,10 @@ export class BalanceHeroResourcesAction extends BaseAction {
return;
}
const thisVillageState = this.villageFactory.createState(thisVillageId);
const thisVillageState = this.villageFactory.getById(thisVillageId).state();
const requirements = [
thisVillageState.required.balance,
thisVillageState.commitments,
thisVillageState.resources.sub(thisVillageState.warehouse.capacity),
];

View File

@ -14,8 +14,8 @@ export class SendResourcesAction extends BaseAction {
const storage = new ResourceTransferStorage();
const savedReport = storage.getReport();
const fromVillage = this.villageFactory.getVillage(savedReport.fromVillageId);
const toVillage = this.villageFactory.getVillage(savedReport.toVillageId);
const fromVillage = this.villageFactory.getById(savedReport.fromVillageId).short();
const toVillage = this.villageFactory.getById(savedReport.toVillageId).short();
const coordinates = toVillage.crd;

View File

@ -12,7 +12,7 @@ export class UpdateResourceContractsTask extends BaseTask {
const villages = this.factory.getAllVillages();
const tasks = villages
.map((v) => this.factory.createTaskCollection(v.id).getTasks())
.map((v) => this.factory.getById(v.id).taskCollection().getTasks())
.reduce((acc, tasks) => acc.concat(tasks), []);
const paths = uniqPaths([

View File

@ -106,12 +106,14 @@ export class Scheduler {
const villageId = task.args.villageId;
// First stage - plan new tasks if needed.
const controllerForPlan = this.villageControllerFactory.createController(villageId);
const controllerForPlan = this.villageControllerFactory.getById(villageId).controller();
controllerForPlan.planTasks();
// Second stage - select isReady for production task.
// We recreate controller, because need new village state.
const controllerForSelect = this.villageControllerFactory.createController(villageId);
const controllerForSelect = this.villageControllerFactory
.getById(villageId)
.controller();
const villageTask = controllerForSelect.getReadyProductionTask();
if (villageTask) {
@ -129,7 +131,7 @@ export class Scheduler {
scheduleTask(name: string, args: Args, ts?: number | undefined): void {
if (isProductionTask(name) && args.villageId) {
const controller = this.villageControllerFactory.createController(args.villageId);
const controller = this.villageControllerFactory.getById(args.villageId).controller();
controller.addTask(name, args);
} else {
this.logger.info('Schedule task', name, args, ts);
@ -161,7 +163,7 @@ export class Scheduler {
const task = this.taskQueue.findById(taskId);
const villageId = task ? task.args.villageId : undefined;
if (villageId) {
const controller = this.villageControllerFactory.createController(villageId);
const controller = this.villageControllerFactory.getById(villageId).controller();
controller.removeTask(taskId);
}
this.removeTask(taskId);
@ -174,7 +176,9 @@ export class Scheduler {
}
if (isProductionTask(task.name) && task.args.villageId) {
const controller = this.villageControllerFactory.createController(task.args.villageId);
const controller = this.villageControllerFactory
.getById(task.args.villageId)
.controller();
controller.postponeTask(taskId, seconds);
this.removeTask(taskId);
} else {

View File

@ -30,8 +30,8 @@ export class ResourceTransferCalculator {
}
calc(fromVillageId: number, toVillageId: number): ResourceTransferReport {
const sender = this.factory.createController(fromVillageId);
const recipient = this.factory.createController(toVillageId);
const sender = this.factory.getById(fromVillageId).controller();
const recipient = this.factory.getById(toVillageId).controller();
let [senderReadyResources, recipientNeedResources] = this.getTransferResourcePair(
sender,

View File

@ -1,71 +1,68 @@
import { VillageController } from './VillageController';
import { VillageStorage } from '../Storage/VillageStorage';
import { VillageRepository } from './VillageRepository';
import { VillageRepositoryInterface } from './VillageRepository';
import { VillageTaskCollection } from './VillageTaskCollection';
import { VillageState, VillageStateFactory } from './VillageState';
import { Village } from '../Core/Village';
export class VillageFactory {
private readonly villageRepository: VillageRepository;
private readonly villageRepository: VillageRepositoryInterface;
constructor(villageRepository: VillageRepository) {
constructor(villageRepository: VillageRepositoryInterface) {
this.villageRepository = villageRepository;
}
getById(villageId: number): IntVillageFactory {
return this.makeInternalFactory(this.villageRepository.getById(villageId));
}
active(): IntVillageFactory {
return this.makeInternalFactory(this.villageRepository.getActive());
}
private makeInternalFactory(village: Village): IntVillageFactory {
return new IntVillageFactory(village, new VillageStateFactory());
}
getAllVillages(): Array<Village> {
return this.villageRepository.all();
}
getVillage(villageId: number): Village {
return this.villageRepository.get(villageId);
}
createStorage(villageId: number): VillageStorage {
const village = this.villageRepository.get(villageId);
return new VillageStorage(village.id);
}
createStorageForActiveVillage(): VillageStorage {
const village = this.villageRepository.getActive();
return this.createStorage(village.id);
}
createTaskCollection(villageId: number): VillageTaskCollection {
const village = this.villageRepository.get(villageId);
return new VillageTaskCollection(village.id, this.createStorage(villageId));
}
createTaskCollectionForActiveVillage(): VillageTaskCollection {
const village = this.villageRepository.getActive();
return this.createTaskCollection(village.id);
}
createState(villageId: number): VillageState {
const village = this.villageRepository.get(villageId);
const stateFactory = new VillageStateFactory(
this.villageRepository,
(id: number) => this.createStorage(id),
(id: number) => this.createTaskCollection(id)
);
return stateFactory.getVillageState(village.id);
}
getAllVillageStates(): Array<VillageState> {
const stateFactory = new VillageStateFactory(
this.villageRepository,
(id: number) => this.createStorage(id),
(id: number) => this.createTaskCollection(id)
);
return stateFactory.getAllVillageStates();
return this.villageRepository
.all()
.map((village) => this.makeInternalFactory(village).state());
}
}
createController(villageId: number): VillageController {
const village = this.villageRepository.get(villageId);
class IntVillageFactory {
constructor(
private readonly village: Village,
private readonly stateFactory: VillageStateFactory
) {}
short(): Village {
return this.village;
}
storage(): VillageStorage {
return new VillageStorage(this.village.id);
}
taskCollection(): VillageTaskCollection {
return new VillageTaskCollection(this.village.id, this.storage());
}
state(): VillageState {
return this.stateFactory.getVillageState(this.village, this.storage());
}
controller(): VillageController {
return new VillageController(
village.id,
this.createStorage(village.id),
this.createTaskCollection(village.id),
this.createState(village.id)
this.village.id,
this.storage(),
this.taskCollection(),
this.state()
);
}
}

View File

@ -4,6 +4,8 @@ import { VillageNotFound } from '../Errors';
export interface VillageRepositoryInterface {
all(): Array<Village>;
getById(villageId: number): Village;
getByCrd(crd: Coordinates): Village;
getActive(): Village;
}
@ -12,7 +14,7 @@ export class VillageRepository implements VillageRepositoryInterface {
return grabVillageList();
}
get(villageId: number): Village {
getById(villageId: number): Village {
const village = this.all().find((vlg) => vlg.id === villageId);
if (!village) {
throw new VillageNotFound('Active village not found');

View File

@ -2,10 +2,7 @@ import { Village, VillageSettings } from '../Core/Village';
import { Resources } from '../Core/Resources';
import { VillageStorage } from '../Storage/VillageStorage';
import { calcGatheringTimings, GatheringTime } from '../Core/GatheringTimings';
import { VillageRepositoryInterface } from './VillageRepository';
import { VillageNotFound } from '../Errors';
import { OrderedProductionQueues, ProductionQueue } from '../Core/ProductionQueue';
import { VillageTaskCollection } from './VillageTaskCollection';
import { TrainTroopTask } from '../Handler/Task/TrainTroopTask';
import { Args } from '../Queue/Args';
import { timestamp } from '../Helpers/Time';
@ -73,7 +70,7 @@ interface ResourceLineState {
active: boolean;
}
interface VillageOwnState {
export interface VillageState {
/**
* Village id
*/
@ -99,17 +96,6 @@ interface VillageOwnState {
settings: VillageSettings;
}
interface VillageOwnStateDictionary {
[id: number]: VillageOwnState;
}
export interface VillageState extends VillageOwnState {
/**
* Resource commitments of this village to other (may be negative)
*/
commitments: Resources;
}
function makeResourceState(
resources: Resources,
current: Resources,
@ -274,19 +260,13 @@ function makeTaskState(task: TaskCore, maxResourcesForTask: Resources): TaskStat
};
}
function createVillageOwnState(
village: Village,
storage: VillageStorage,
taskCollection: VillageTaskCollection
): VillageOwnState {
function createVillageState(village: Village, storage: VillageStorage): VillageState {
const settings = storage.getSettings();
const resources = storage.getResources();
const capacity = storage.getWarehouseCapacity();
const performance = storage.getResourcesPerformance();
const storageState = makeWarehouseState(resources, capacity, performance);
const tasks = taskCollection
.getTasks()
.map((t) => makeTaskState(t, storageState.optimumFullness));
const tasks = storage.getTasks().map((t) => makeTaskState(t, storageState.optimumFullness));
const queues = createTaskQueueStates(storageState, tasks, storage);
const firstReadyTask = getReadyForProductionTask(queues);
const requiredResources = getTaskResources(firstReadyTask);
@ -305,90 +285,8 @@ function createVillageOwnState(
};
}
function createVillageOwnStates(
villages: Array<Village>,
storageFactory: VillageStorageFactory,
taskCollectionFactory: VillageTaskCollectionFactory
): VillageOwnStateDictionary {
const result: VillageOwnStateDictionary = {};
for (let village of villages) {
result[village.id] = createVillageOwnState(
village,
storageFactory(village.id),
taskCollectionFactory(village.id)
);
}
return result;
}
function createVillageState(
ownState: VillageOwnState,
otherOwnStates: VillageOwnStateDictionary
): VillageState {
const otherVillageIds = Object.keys(otherOwnStates).map((key) => +key);
const reducer = (memo: Resources, shipmentVillageId: number) => {
const shipmentVillageState = otherOwnStates[shipmentVillageId];
const shipmentVillageRequired = shipmentVillageState.required;
const shipmentVillageIncoming = shipmentVillageState.incomingResources;
const targetVillageMissing = shipmentVillageRequired.balance
.add(shipmentVillageIncoming)
.min(Resources.zero());
return memo.add(targetVillageMissing);
};
const commitments = otherVillageIds.reduce(reducer, Resources.zero());
return { ...ownState, commitments };
}
function getVillageStates(
villages: Array<Village>,
storageFactory: VillageStorageFactory,
taskCollectionFactory: VillageTaskCollectionFactory
): Array<VillageState> {
const ownStates = createVillageOwnStates(villages, storageFactory, taskCollectionFactory);
return villages.map((village) => createVillageState(ownStates[village.id], ownStates));
}
interface VillageStorageFactory {
(villageId: number): VillageStorage;
}
interface VillageTaskCollectionFactory {
(villageId: number): VillageTaskCollection;
}
export class VillageStateFactory {
private readonly villageRepository: VillageRepositoryInterface;
private readonly storageFactory: VillageStorageFactory;
private readonly taskCollectionFactory: VillageTaskCollectionFactory;
constructor(
villageRepository: VillageRepositoryInterface,
storageFactory: VillageStorageFactory,
taskCollectionFactory: VillageTaskCollectionFactory
) {
this.villageRepository = villageRepository;
this.storageFactory = storageFactory;
this.taskCollectionFactory = taskCollectionFactory;
}
getAllVillageStates(): Array<VillageState> {
return getVillageStates(
this.villageRepository.all(),
this.storageFactory,
this.taskCollectionFactory
);
}
getVillageState(villageId: number): VillageState {
const states = getVillageStates(
this.villageRepository.all(),
this.storageFactory,
this.taskCollectionFactory
);
const needle = states.find((s) => s.id === villageId);
if (!needle) {
throw new VillageNotFound(`Village ${villageId} not found`);
}
return needle;
getVillageState(village: Village, storage: VillageStorage): VillageState {
return createVillageState(village, storage);
}
}