Last active
July 24, 2017 00:18
-
-
Save nolanfu/badb3ff3fb82428de070210c46ba2dc0 to your computer and use it in GitHub Desktop.
CowFisk merchant flyer implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Performs expensive market calls so you don't have to. | |
// Gets 3 cheapest sellers / 3 most expensive buyers for each of a list of compounds and saves them to a public segment. | |
// Ignores orders if they have less than 10 of the compound remaining. | |
var MerchantFlyer = function() { | |
this.defaultPublicSegment = 0 | |
this.targetSegment = 1 | |
this.version = "v1.0.0" | |
this.maxOrders = 3 | |
this.minAmount = 10 | |
this.description = "Market orders with highest " + this.maxOrders + " seller prices / lowest " + this.maxOrders + " buyer prices for a variety of compounds." | |
this.statsDataTtl = 100 | |
this.acceptedCompounds = [ | |
"power", | |
"U", "L", "Z", "K", "O", "H", "X", | |
"OH", "G", | |
"XGH2O", "XLH2O", | |
"XGHO2", "XUH2O", "XKHO2", "XLHO2", "XZH2O", "XZHO2" | |
] | |
} | |
MerchantFlyer.prototype.update = function() { | |
RawMemory.setActiveSegments([this.defaultPublicSegment, this.targetSegment]) // Careful with this if you run it yourself. You'll probably want to modify it. | |
RawMemory.setDefaultPublicSegment(this.defaultPublicSegment) | |
RawMemory.setPublicSegments([this.defaultPublicSegment, this.targetSegment]) | |
this.updateDefaultSegment() | |
this.updateMarketData() | |
} | |
MerchantFlyer.prototype.updateDefaultSegment = function() { | |
let hash = { | |
"api": { | |
"version": "v1.0.0", | |
"update": Game.time | |
}, | |
"channels": { | |
"CowFisk": { | |
"protocol": "marketdata", | |
"segments": [this.targetSegment], | |
"update": Game.time | |
} | |
} | |
} | |
RawMemory.segments[this.defaultPublicSegment] = JSON.stringify(hash) | |
} | |
MerchantFlyer.prototype.updateMarketData = function() { | |
let start = Game.cpu.getUsed() | |
let newData = {"update": Game.time, "version": this.version, "description": this.description} | |
let ordersHash = {} | |
let acceptedHash = {} | |
for (let compound of this.acceptedCompounds) { | |
acceptedHash[compound] = 1 | |
} | |
let allOrders = Game.market.getAllOrders((o) => (o.remainingAmount >= this.minAmount && acceptedHash[o.resourceType])) | |
let ordersByCompound = _.groupBy(allOrders, "resourceType") | |
for (let compound of Object.keys(ordersByCompound)) { | |
ordersHash[compound] = {} | |
let orders = ordersByCompound[compound] | |
let sellers = _.filter(orders, (o) => (o.type == ORDER_SELL)) | |
sellers.sort((a,b) => (a.price - b.price)) | |
ordersHash[compound].sellers = [] | |
for (let order of sellers.slice(0,this.maxOrders)) { | |
ordersHash[compound].sellers.push({"id": order.id, "price": order.price, "amount": order.amount, "roomName": order.roomName}) | |
} | |
let buyers = _.filter(orders, (o) => (o.type == ORDER_BUY)) | |
buyers.sort((a,b) => (b.price - a.price)) | |
ordersHash[compound].buyers = [] | |
for (let order of buyers.slice(0,this.maxOrders)) { | |
ordersHash[compound].buyers.push({"id": order.id, "price": order.price, "amount": order.amount, "roomName": order.roomName}) | |
} | |
ordersHash[compound].filters = { | |
"resources": [compound], | |
"minAmount": this.minAmount, | |
"maxOrders": this.maxOrders | |
} | |
} | |
newData.orders = ordersHash | |
let oldData | |
try { | |
oldData = JSON.parse(RawMemory.segments[this.targetSegment]) | |
} catch(err) { | |
oldData = {} | |
} | |
newData = this.recordStats(newData, oldData, ordersByCompound) | |
let string = JSON.stringify(newData) | |
if (string.length >= 100000) { | |
console.log("Warning: CowFisk merchant flyer is larger than 100k and won't fit in segment.") | |
} | |
RawMemory.segments[this.targetSegment] = string | |
let elapsed = Game.cpu.getUsed() - start | |
//console.log(string) | |
console.log(Game.time.toString() + ": CowFisk merchant flyer generated in " + elapsed.toFixed(2) + " CPU.") | |
} | |
MerchantFlyer.prototype.recordStats = function(newData, oldData, ordersByCompound) { | |
let staleTick = Game.time - this.statsDataTtl | |
if (!Memory.merchantFlyerStats) { | |
Memory.merchantFlyerStats = {} | |
} | |
if (!Memory.merchantFlyerStats.processed) { | |
Memory.merchantFlyerStats.processed = [] | |
} | |
Memory.merchantFlyerStats.processed = this.purgeOldStats(Memory.merchantFlyerStats.processed, staleTick) | |
Memory.merchantFlyerStats.processed.push(Game.time) | |
for (let compound of Object.keys(ordersByCompound)) { | |
newData.orders[compound].stats = {"processedCount": Memory.merchantFlyerStats.processed.length} | |
if (!Memory.merchantFlyerStats[compound]) { | |
Memory.merchantFlyerStats[compound] = {} | |
} | |
for (let type of ["sellers", "buyers"]) { | |
newData.orders[compound].stats[type] = {} | |
if (!Memory.merchantFlyerStats[compound][type]) { | |
Memory.merchantFlyerStats[compound][type] = {} | |
} | |
for (let key of ["different", "betterDeal", "missing"]) { | |
if (!Memory.merchantFlyerStats[compound][type][key]) { | |
Memory.merchantFlyerStats[compound][type][key] = [] | |
} | |
Memory.merchantFlyerStats[compound][type][key] = this.purgeOldStats(Memory.merchantFlyerStats[compound][type][key], staleTick) | |
} | |
let newIds = _.map(newData.orders[compound][type], (o) => (o.id)) | |
let oldIds | |
let haveOldData = (oldData && oldData.orders && oldData.orders[compound] && oldData.orders[compound][type]) | |
if (haveOldData) { | |
oldIds = _.map(oldData.orders[compound][type], (o) => (o.id)) | |
} else { | |
console.log("In MerchantFlyer.recordStats: No old data found for compound " + compound + ".") | |
oldIds = [] | |
} | |
// different | |
if (JSON.stringify(newIds) != JSON.stringify(oldIds)) { | |
console.log("MerchantFlyer.recordStats: Different list of order IDs for " + compound + ": " + JSON.stringify(oldIds) + " -> " + JSON.stringify(newIds) + ".") | |
Memory.merchantFlyerStats[compound][type].different.push(Game.time) | |
} | |
newData.orders[compound].stats[type].different = Memory.merchantFlyerStats[compound][type].different.length | |
// betterDeal | |
if (haveOldData && newData.orders[compound][type][0].length > 0) { | |
if ((type == "sellers" && newData.orders[compound][type][0].price < oldData.orders[compound][type][0].price) || (type == "buyers" && newData.orders[compound][type][0].price > oldData.orders[compound][type][0].price)) { | |
console.log("MerchantFlyer.recordStats: better deal for " + compound + ": " + JSON.stringify(oldData.orders[compound][type][0]) + " -> " + JSON.stringify(newData.orders[compound][type][0]) + ".") | |
Memory.merchantFlyerStats[compound][type].betterDeal.push(Game.time) | |
} | |
} | |
newData.orders[compound].stats[type].betterDeal = Memory.merchantFlyerStats[compound][type].betterDeal.length | |
// missing | |
if (haveOldData && newIds.indexOf(oldData.orders[compound][type][0].id) == -1) { | |
console.log("MerchantFlyer.recordStats: missing top order " + compound + ": " + JSON.stringify(oldData.orders[compound][type][0]) + ".") | |
Memory.merchantFlyerStats[compound][type].missing.push(Game.time) | |
} | |
newData.orders[compound].stats[type].missing = Memory.merchantFlyerStats[compound][type].missing.length | |
} | |
} | |
return newData | |
} | |
MerchantFlyer.prototype.purgeOldStats = function(list, staleTick) { | |
while (list.length > 0 && list[0] < staleTick) { | |
list.shift() | |
} | |
return list | |
} | |
global.MerchantFlyer = MerchantFlyer | |
module.exports = MerchantFlyer |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment