Advertisement
Guest User

Untitled

a guest
Jan 24th, 2017
96
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 13.32 KB | None | 0 0
  1. import {WorldMap} from "./WorldMap";
  2. export class TradeNetwork {
  3.  
  4. public storages: StructureStorage[] = [];
  5. public terminals: StructureTerminal[] = [];
  6.  
  7. private shortages: {[resourceType: string]: StructureTerminal[] } = {};
  8. private surpluses: {[resourceType: string]: StructureTerminal[] } = {};
  9. private _inventory: {[key: string]: number};
  10. private map: WorldMap;
  11.  
  12. private alreadyTraded: {[roomName: string]: boolean } = {};
  13.  
  14. private memory: {
  15. tradeRoomIndex: number;
  16. };
  17.  
  18. constructor(map: WorldMap) {
  19. this.map = map;
  20.  
  21. if (!Memory.empire) { Memory.empire = {}; }
  22. this.memory = Memory.empire;
  23. }
  24.  
  25. init() {
  26. this.registerMyRooms();
  27. this.registerPartnerRooms();
  28. }
  29.  
  30. public actions() {
  31. this.observeTradeRoom();
  32. this.tradeMonkey();
  33. this.reportTransactions();
  34. }
  35.  
  36. // should only be accessed after Init()
  37. get inventory(): {[key: string]: number} {
  38. if (!this._inventory) {
  39. let inventory: {[key: string]: number } = {};
  40.  
  41. for (let terminal of this.terminals) {
  42.  
  43. for (let mineralType in terminal.store) {
  44. if (!terminal.store.hasOwnProperty(mineralType)) continue;
  45. if (inventory[mineralType] === undefined) {
  46. inventory[mineralType] = 0;
  47. }
  48. inventory[mineralType] += terminal.store[mineralType];
  49. }
  50. }
  51.  
  52. // gather mineral/storage data
  53. for (let storage of this.storages) {
  54. for (let mineralType in storage.store) {
  55. if (inventory[mineralType] === undefined) {
  56. inventory[mineralType] = 0;
  57. }
  58. inventory[mineralType] += storage.store[mineralType];
  59. }
  60. }
  61.  
  62. this._inventory = inventory;
  63. }
  64. return this._inventory;
  65. }
  66.  
  67. private observeTradeRoom() {
  68. let tradeRoomNames = Object.keys(this.map.tradeMap);
  69. if (tradeRoomNames.length === 0) { return; }
  70.  
  71. let count = 0;
  72. while (count < tradeRoomNames.length) {
  73. if (this.memory.tradeRoomIndex === undefined || this.memory.tradeRoomIndex >= tradeRoomNames.length) {
  74. this.memory.tradeRoomIndex = 0;
  75. }
  76. let roomName = tradeRoomNames[this.memory.tradeRoomIndex++];
  77. let room = Game.rooms[roomName];
  78. if (room) {
  79. count++;
  80. continue;
  81. }
  82.  
  83. let roomMemory = this.map.tradeMap[roomName];
  84. if (Game.time < roomMemory.nextTrade) {
  85. count++;
  86. continue;
  87. }
  88.  
  89. let observer = this.findObserver(roomName);
  90. if (!observer) {
  91. roomMemory.nextTrade = Game.time + 10000;
  92. count++;
  93. continue;
  94. }
  95.  
  96. observer.observeRoom(roomName, "tradeNetwork");
  97. break;
  98. }
  99. }
  100.  
  101. private findObserver(observedRoomName: string): StructureObserver {
  102. for (let observingRoomName in this.map.controlledRooms) {
  103. if (Game.map.getRoomLinearDistance(observedRoomName, observingRoomName) > 10) { continue; }
  104. let room = this.map.controlledRooms[observingRoomName];
  105. if (room.controller.level < 8) { continue; }
  106. let observer = _(room.find<StructureObserver>(FIND_STRUCTURES))
  107. .filter(s => s.structureType === STRUCTURE_OBSERVER)
  108. .head();
  109. if (observer) { return observer; }
  110. }
  111. }
  112.  
  113. /**
  114. * Used to determine whether there is an abundance of a given resource type among all terminals.
  115. * Should only be used after init() phase
  116. * @param resourceType
  117. * @param amountPerRoom - specify how much per missionRoom you consider an abundance, default value is SURPLUS_AMOUNT
  118. */
  119. public hasAbundance(resourceType: string, amountPerRoom = RESERVE_AMOUNT * 2) {
  120. let abundanceAmount = this.terminals.length * amountPerRoom;
  121. return this.inventory[resourceType] && this.inventory[resourceType] > abundanceAmount;
  122. }
  123.  
  124. private registerMyRooms() {
  125. for (let roomName in this.map.controlledRooms) {
  126. let room = this.map.controlledRooms[roomName];
  127. if (room.terminal && room.terminal.my && room.controller.level >= 6) {
  128. this.terminals.push(room.terminal);
  129. }
  130. if (room.storage && room.storage.my && room.controller.level >= 4) {
  131. this.storages.push(room.storage);
  132. }
  133.  
  134. if (TradeNetwork.canTrade(room)) {
  135. this.analyzeResources(room);
  136. }
  137. }
  138. }
  139.  
  140. private registerPartnerRooms() {
  141. for (let room of this.map.tradeRooms) {
  142. if (TradeNetwork.canTrade(room)) {
  143. this.analyzeResources(room);
  144. }
  145. else {
  146. delete room.memory.nextTrade;
  147. }
  148. }
  149. }
  150.  
  151. public analyzeResources(room: Room) {
  152.  
  153. let tradeResourceTypes = TRADE_WITH_SELF;
  154. if (!room.controller.my) {
  155. tradeResourceTypes = TRADE_WITH_PARTNERS;
  156. }
  157.  
  158. let terminalFull = _.sum(room.terminal.store) > 270000;
  159. for (let resourceType of tradeResourceTypes) {
  160. if (resourceType === RESOURCE_ENERGY) {
  161. if (room.terminal.store.energy < 50000 && room.storage.store.energy < NEED_ENERGY_THRESHOLD
  162. && !terminalFull) {
  163. this.registerShortage(resourceType, room.terminal);
  164. }
  165. else if (room.storage.store.energy > SUPPLY_ENERGY_THRESHOLD) {
  166. this.registerSurplus(resourceType, room.terminal);
  167. }
  168. }
  169. else {
  170. let amount = room.terminal.store[resourceType] || 0;
  171. if (amount < RESERVE_AMOUNT && !terminalFull) {
  172. this.registerShortage(resourceType, room.terminal);
  173. }
  174. else if (amount >= SURPLUS_AMOUNT) {
  175. this.registerSurplus(resourceType, room.terminal);
  176. }
  177. }
  178. }
  179. }
  180.  
  181. private registerShortage(resourceType: string, terminal: StructureTerminal) {
  182. if (!this.shortages[resourceType]) { this.shortages[resourceType] = []}
  183. this.shortages[resourceType].push(terminal);
  184. }
  185.  
  186. private registerSurplus(resourceType: string, terminal: StructureTerminal) {
  187. if (!terminal.my) { return; } // we could erase this if we were all running the same code
  188. if (!this.surpluses[resourceType]) { this.surpluses[resourceType] = []}
  189. this.surpluses[resourceType].push(terminal);
  190. }
  191.  
  192. // connects shortages and surpluses
  193. private tradeMonkey() {
  194.  
  195. for (let resourceType in this.shortages) {
  196. let shortageTerminals = this.shortages[resourceType];
  197. let surplusTerminals = this.surpluses[resourceType];
  198. if (!surplusTerminals) { continue; }
  199. for (let shortage of shortageTerminals) {
  200. let bestSurplus;
  201. let bestDistance = Number.MAX_VALUE;
  202. for (let surplus of surplusTerminals) {
  203. let distance = Game.map.getRoomLinearDistance(shortage.room.name, surplus.room.name);
  204. if (distance > bestDistance) { continue; }
  205. if (distance > this.acceptableDistance(resourceType, surplus)) { continue; }
  206. bestDistance = distance;
  207. bestSurplus = surplus;
  208. }
  209. if (bestSurplus && !this.alreadyTraded[bestSurplus.room.name]) {
  210. let requiredEnergy = 10000;
  211. if (resourceType === RESOURCE_ENERGY) {
  212. requiredEnergy = 30000;
  213. }
  214. if (bestSurplus.store.energy >= requiredEnergy) {
  215. let amount = this.sendAmount(resourceType, shortage);
  216. this.sendResource(bestSurplus, resourceType, amount, shortage);
  217. }
  218. this.alreadyTraded[bestSurplus.room.name] = true;
  219. }
  220. }
  221. }
  222. }
  223.  
  224. private sendAmount(resourceType: string, shortage: StructureTerminal): number {
  225. if (resourceType === RESOURCE_ENERGY) { return TRADE_ENERGY_AMOUNT; }
  226. let amountStored = shortage.store[resourceType] || 0;
  227. return RESERVE_AMOUNT - amountStored;
  228. }
  229.  
  230. private acceptableDistance(resourceType: string, surplus: StructureTerminal): number {
  231. if (IGNORE_TRADE_DISTANCE[resourceType]) { return Number.MAX_VALUE; }
  232. else if (resourceType === RESOURCE_ENERGY) {
  233. if (_.sum(surplus.room.storage.store) >= 950000) {
  234. return Number.MAX_VALUE;
  235. } else {
  236. return TRADE_DISTANCE;
  237. }
  238. } else {
  239. if (surplus.room.storage.store[resourceType] > 80000) {
  240. return Number.MAX_VALUE;
  241. } else {
  242. return TRADE_DISTANCE;
  243. }
  244. }
  245. }
  246.  
  247. private sendResource(localTerminal: StructureTerminal, resourceType: string, amount: number, otherTerminal: StructureTerminal) {
  248.  
  249. if (amount < 100) {
  250. amount = 100;
  251. }
  252.  
  253. let outcome = localTerminal.send(resourceType, amount, otherTerminal.room.name);
  254. if (outcome !== OK) {
  255. console.log(`NETWORK: error sending resource in ${localTerminal.room.name}, outcome: ${outcome}`);
  256. console.log(`arguments used: ${resourceType}, ${amount}, ${otherTerminal.room.name}`);
  257. }
  258. }
  259.  
  260. public static canTrade(room: Room) {
  261. return room.controller && room.controller.level >= 6 && room.storage && room.terminal
  262. && (!room.controller.sign || room.controller.sign.text !== "noTrade");
  263. }
  264.  
  265. reportTransactions() {
  266.  
  267. if (Game.time % 10 !== 0) return;
  268.  
  269. let kFormatter = (num: number) => {
  270. return num > 999 ? (num/1000).toFixed(1) + 'k' : num
  271. };
  272.  
  273. let consoleReport = (item: Transaction) => {
  274. let distance = Game.map.getRoomLinearDistance(item.from, item.to);
  275. let cost = Game.market.calcTransactionCost(item.amount, item.from, item.to);
  276. console.log(
  277. `TRADE: ${_.padLeft(`${item.from} ${item.sender ? item.sender.username : "npc"}`.substr(0, 12), 12)} ` +
  278. `→ ${_.pad(`${kFormatter(item.amount)} ${item.resourceType}`.substr(0, 12), 12)} → ` +
  279. `${_.padRight(`${item.to} ${item.recipient ? item.recipient.username : "npc"}`.substr(0, 12), 12)} ` +
  280. `(dist: ${distance}, cost: ${kFormatter(cost)})`
  281. );
  282. };
  283.  
  284. for (let item of Game.market.incomingTransactions) {
  285. if (!item.sender) continue;
  286. if (item.time >= Game.time - 10) {
  287. let username = item.sender.username;
  288. if (!username) { username = "npc"; }
  289. if (!Memory.traders[username]) { Memory.traders[username] = {}; }
  290. if (Memory.traders[username][item.resourceType] === undefined) {
  291. Memory.traders[username][item.resourceType] = 0;
  292. }
  293. Memory.traders[username][item.resourceType] += item.amount;
  294. consoleReport(item);
  295. this.processTransaction(item);
  296. }
  297. else {
  298. break;
  299. }
  300. }
  301.  
  302. for (let item of Game.market.outgoingTransactions) {
  303. if (!item.recipient) continue;
  304. if (item.time >= Game.time - 10) {
  305. let username = item.recipient.username;
  306. if (!username) { username = "npc"; }
  307. if (!Memory.traders[username]) { Memory.traders[username] = {}; }
  308. if (Memory.traders[username][item.resourceType] === undefined) {
  309. Memory.traders[username][item.resourceType] = 0;
  310. }
  311. Memory.traders[item.recipient.username][item.resourceType] -= item.amount;
  312. if (item.recipient.username === this.terminals[0].owner.username) { continue; }
  313. consoleReport(item);
  314. }
  315. else {
  316. break;
  317. }
  318. }
  319. }
  320.  
  321. protected processTransaction(item: Transaction) { } // overridden in BonzaiNetwork
  322. }
  323.  
  324. // these are the constants that govern your energy balance
  325. // rooms below this will try to pull energy...
  326. export const NEED_ENERGY_THRESHOLD = 200000;
  327. // ...from rooms above this.
  328. export const SUPPLY_ENERGY_THRESHOLD = 250000;
  329. // rooms above this will start processing power
  330. export const POWER_PROCESS_THRESHOLD = 350000;
  331. // rooms above this will spawn a more powerful wall-builder to try to sink energy that way
  332. export const ENERGYSINK_THRESHOLD = 450000;
  333. export const RESERVE_AMOUNT = 5000;
  334. export const SURPLUS_AMOUNT = 10000;
  335. export const TRADE_DISTANCE = 6;
  336. export const IGNORE_TRADE_DISTANCE = {
  337. ["XUH2O"]: true,
  338. ["XLHO2"]: true,
  339. [RESOURCE_POWER]: true,
  340. };
  341. export const MINERALS_RAW = ["H", "O", "Z", "U", "K", "L", "X"];
  342. export const PRODUCT_LIST = ["XUH2O", "XLHO2", "XLH2O", "XKHO2", "XGHO2", "XZHO2", "XZH2O", "G", "XGH2O"];
  343. export const TRADE_WITH_SELF = [RESOURCE_ENERGY].concat(PRODUCT_LIST).concat(MINERALS_RAW).concat(RESOURCE_POWER);
  344. export const TRADE_WITH_PARTNERS = [RESOURCE_ENERGY].concat(PRODUCT_LIST).concat(MINERALS_RAW);
  345. export const TRADE_ENERGY_AMOUNT = 10000;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement