Skip to content

Instantly share code, notes, and snippets.

@nolanfu
Last active July 24, 2017 00:18
Show Gist options
  • Save nolanfu/badb3ff3fb82428de070210c46ba2dc0 to your computer and use it in GitHub Desktop.
Save nolanfu/badb3ff3fb82428de070210c46ba2dc0 to your computer and use it in GitHub Desktop.
CowFisk merchant flyer implementation
// 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