Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make (Bulk) Trading Profitable Again + Station Restocking #5291

Merged
merged 6 commits into from
Nov 6, 2021

Conversation

Web-eWorks
Copy link
Member

Bulk Trading Changes

This PR addresses a problem very astutely noticed by @jimishol in the economic and trading simulation portion of the game - once the player acquires a ship with ~80t of cargo space, the trading gameplay loop of Pioneer is effectively over. All commodities in the game have the exact same maximum profit for a single trade run, and the only variance is in how much hull space is required to make that exact same amount of money.

This means that once you have found an export/import run for Precious Metals, you have hit the maximum profit ceiling in trading. Sure, you can run a second commodity at the same time in a larger hull, but that costs more fuel, takes more real player time, usually requires a second chain of hyperjumps, and the profit margin is much lower than your primary commodity.

I promised charts, so charts you will have!

image
(Investment and Profit use left axis, Tonnage uses right axis.)

The current paradigm (which I will call "linear stocking") uses a simple formula to determine the amount of commodities that are present at a station when you enter the system. There is some variance in stock from station to station and from system to system based on the price modification from underlying system economy variables, but that variance has comparatively little effect on the primary viable trade route - buy a commodity at a major export station and sell at a minor import station, yielding approximately 30-35% profit.

With linear stocking, the "midgame" of trading ships (that is, medium haulers) is in fact the endgame of trading in Pioneer; you can spend more money on a larger ship, but you will never make any more money hauling a different cargo in a larger hold than you will hauling Precious Metals in a comparatively small hold.

This problem stems from the fact that the current stocking algorithm only takes into account a single factor - the price of the commodity - to cap the maximum profit that can be made in a single run. It ignores the other implicit factors of cargo space (price to acquire the ship), fuel costs (often 3-10x higher in a large bulk ship), and hyperjump range needed to haul larger cargoes.

This PR addresses that problem by introducing a new station stocking algorithm (which I call "exponential stocking"). It's based on the core of #5272, and uses an exponential term in the function that calculates a station's maximum stock to better model the factors of commodity rarity and time-and-resource cost to the player to haul bulk commodities, resulting in a higher credit-value profit from hauling bulk cargo while leaving the actual profit margin of 30-35% entirely untouched.

image
(Investment and Profit use left axis, Tonnage uses right axis.)

This new algorithm provides a better and more balanced economy that gives both large bulk ships and smaller cargo/courier ships like the Amphiesma and the Sinonatrix a role to play in trading and a natural progression through the game. Precious Metals is now a rare, low-volume commodity, being the best good to haul in a small ship but dropping off in absolute profit once the player has worked their way to a Mola Mola or similar entry-level medium hauler. By the time the player has purchased a Skipjack or larger ship, Robots or even Medicines are the better cargo to haul, giving the player an incentive to change it up and not just run the same route between two systems for the rest of the game until they get bored.

image
You can see the source data for all of these charts here.

Dynamic Station Restocking

Now, merely that change alone would solve the stated problem by giving bulk haulers a reason to haul textiles in their massive holds instead of being a glorified courier for Precious Metals, but there's something else that can be done to improve the trading gameplay as well, and something that lays the groundwork for a dynamic, simulated economy.

Stations now no longer re-roll their stock values for every commodity each time you re-enter the system, and instead persistently track the player's purchases and re-stock to a deterministic equilibrium over time. This means that when you've bought a station entirely out of one commodity stock, you can't just jump out and jump back in to refresh that station; you'll have to find another station in the system and buy from that station instead.

Stations will over time recover back to their "equilibrium" stock amounts (being a combination of a very lightweight approximation of NPC trade and commodity production happening in the background and the station interface being a front for multiple trading companies with their own exclusivity contracts and stock reserves) as you jump between systems and trade with other stations, eventually becoming a viable part of the player's trade route again.

The constants behind these two algorithms are currently tuned to allow the player to make one "max profit" run from a station before needing to use another station in the same system, with the station restocking after 3-6 such trade runs. This could be further changed after merge in several directions; either to use a lower equilibrium stock and restock much faster (while the player is on the pad, potentially, though I don't think this has much gameplay utility), or to have a higher equilibrium stock and restock much slower, potentially taking a year or more to get back to equilibrium stock after heavy bulk trading.

However, I don't think tuning in either direction is actually the best way to go, as I've neglected to mention how these new features interact with other potential future changes to the economy - one of the biggest is giving each station its own economy type and making export/import price modifications be relative to a specific station or planet rather than system-wide. This would greatly amplify the impact of limited station stocks as instead of N stations in the system to buy that stock from, you could have as little as 1-2 stations that sell the commodity you want at the right price.

Other changes that could impact and provide even more depth would be dynamic commodity prices based on available stock (buy from the highest-stock station and sell to high-demand); or basing station stock on station population and industry/agriculture production of the station and parent bodies during system generation - these values are currently too random and unpredictable to be used directly.

General Improvements

And of course, I've saved the best for last - I've rewritten the way SpaceStation.lua caches data about visited stations, adding an actual visited cache instead of using the advertisement register. This wound up resolving (or at least mitigating the cause) of the GetEquipmentStock() bug that's been causing many crashes and issues over the past few months. This supersedes and will close #5284.

I've also added a Game.GetStartTime() function for modules (e.g. hyperdrive breakdown) to use to determine when the loaded game was started; unfortunately it's inaccurate for old saves, but going forward that data will be available.

Testing Notes

I'd appreciate some eyes on this PR with regards to longer-running games; both to confirm the fix for the GetEquipmentStock() bugs and also to test the progression from Amphiesma to Sinonatrix to Mola Mola and then the "midgame". I don't expect to see any bugs shaking out of the woodwork, but I would like feedback about the experience to see if I need to tweak any of the constants further to make the design more apparent.


Fixes #5149, fixes #5233. Closes #5272, closes #5284.

Station commodity stocks are now stored in a persistent table.
- Buying from a station depletes the stock of commodities.
- Station stocks recover over time, currently appx. 12 weeks.
- Leaving a system and coming back no longer resets station stocks.
Station stock target algorithm has been changed to be exponential instead of linear.
- Larger ships now have a reason to ship bulk cargos.
- Precious Metals is no longer the only commodity worth shipping.

Hopefully resolve bug with equipmentStock table not being created for stations when docking.

Use visited[station] table for checking whether we need to create station data rather than advertisments.
Haulaway (negative price) commodities are considered transient and have a different stock algorithm.

Incorporate feedback from jimishol.
Use a normal distribution for station restocking based on feedback from jimishol.
Fix remaining bugs resulting in zero h2 and rubbish stocks.
Add error logging in case of station being created twice.
Add documentation, refactor return values from GetStationTargetStock
@jimishol
Copy link
Contributor

jimishol commented Oct 3, 2021

Well done!

by a player.

The GetMaxStockForPrice(price) function means that investment equals to Price * Stock = constant / Price^(a-1).

That gives a huge tool in developers hands. Developers should really find time to play this PR for real and feel the difference it brings in game play. Initial game play is not affected at all. So, take amphiesma SetMoney to 100000, to save time, and try to acquire dsminer as soon as possible. It would be needed about 125 trades or about 12.5 hours of play time. A liitle harder than in current state where it is needed 9.7 hours. (Download Calibration.lua, ship_table.lua, FloydWarshall.lua in same folder and run the last one if you are curious about how i estimated this. This is another example of what can be achieved by this PR). Duration of 12.5 hours seems still small for me as player. From personal experience, i propose to try to double by design that duration.

By playing this PR, i welcome the feature of not being profitable to visit same station in row but, i would like motivation to change systems, not just stations in same system. So, i welcome any thought to increase duration of restock. But even that has problem in near Sol systems. Near Sol i find many systems with huge number of stations. So, there is always enough stations to do A1,B1,A2,B2,A3,B3, A4... and so on trips between same pair of systems A, B. For this matter, take a thought if possible to limit number of stations a system can have.

Very Well Done!!
Accept this merge and the game will be greatly improved in future, by ways that maybe can not be clearly estimated today.

P.S. Constant and exponential a, of GetMaxStockForPrice function, are constants of an economy universe. I propose, those two, to be available at least to all lua files in libs and modules folders.

Copy link
Member

@impaktor impaktor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precious Metals is now a rare, low-volume commodity, being the best good to haul in a small ship but dropping off in absolute profit once the player has worked their way to a Mola Mola or similar entry-level medium hauler. By the time the player has purchased a Skipjack or larger ship, Robots or even Medicines are the better cargo to haul, giving the player an incentive to change it up and not just run the same route between two systems for the rest of the game until they get bored.

Sounds very good, and this certainly is an improvement, so it's a step in the right direction. I don't see any downsides with this.

@jimishol & others: some things to playtest:

  • Are we still getting "sold out" adverts some times? There's a 50% chance that if market has 0 in stock of some item, then there'll be a "sold out" advert on the BBS.
  • That H2 never sold out
  • You could increase the eventProbability probability and set minTime=0 in the NewsEvent module, and check that these still work:
    • commodity hinted at in system reported, has either zero or very much in stock, and price is either much higher or much lower, respectively

-- Function: GetStartTime()
--
-- Returns the zero-offset time in seconds since Jan 1 3200 at which the
-- current game began
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should one explain why player might need it? I.e. a new game doesn't start the timer on 0? Maybe implicitly understood, but my lazy brain would miss it. I.e. an example perhaps?

--
-- > local seconds_since_start = Game.time() - Game.GetStartTime()
--

Should one also mention that it's in game time?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't consider it high-priority as the name is relatively self-documenting, but I'll add that usage line.

jimishol pushed a commit to jimishol/pioneer that referenced this pull request Oct 6, 2021
 Author:    Webster Sheets <[email protected]>
 Date:      Fri Sep 24 04:52:21 2021 -0400

 On branch NEWS_game_start_time

 Changes to commit:
	add:     Game.lua
		cherry picked from pioneerspacesim#5291 PR
	edit:     ../modules/NewsEventCommodity/NewsEventCommodity.lua
		uses Game.GetStartTime() function from pioneerspacesim#5291 PR
@impaktor
Copy link
Member

impaktor commented Oct 6, 2021

As pointed out on IRC the other day, can a market spawn with 0 in stock for some commodity? I.e. have that equilibrium state, or is only possibility for 0t if player buys up the market?

Asking, since SoldOut advert module doesn't set commodity supply to 0, but instead monitors the market and then spawns advert (with 50% probability) to buy the sold out commodity.

Could the player use this? Buy all available precious metal. Jump out, jump back, and now sell back to someone offering x2 price?

@Web-eWorks
Copy link
Member Author

As pointed out on IRC the other day, can a market spawn with 0 in stock for some commodity? I.e. have that equilibrium state, or is only possibility for 0t if player buys up the market?

Yes. The chance of a commodity spawning at 0 is the same as before, but you get fewer "tries" at it as stock values aren't rerolled every time the player re-enters the system.

Asking, since SoldOut advert module doesn't set commodity supply to 0, but instead monitors the market and then spawns advert (with 50% probability) to buy the sold out commodity.

You probably won't see as many SoldOuts with these changes, but this does open up the opportunity for a more dynamic market ecosystem (among other things I want to add after this is merged is a separate "demand" tracker in addition to the current supply tracker) that SoldOut can make better use of then just how much of a commodity is in stock at the station.

Could the player use this? Buy all available precious metal. Jump out, jump back, and now sell back to someone offering x2 price?

They theoretically could, although it's more probable that the commodity would restock to be back above zero when they jump back in. In either case, I consider this internally consistent behavior (SoldOut triggers on zero stock, doesn't care about how it got there); this is something where changing it would require tracking both supply and demand of a given commodity.

@Web-eWorks Web-eWorks merged commit 50c081d into pioneerspacesim:master Nov 6, 2021
@Web-eWorks Web-eWorks deleted the station-restock branch May 14, 2022 00:03
Web-eWorks pushed a commit to jimishol/pioneer that referenced this pull request May 20, 2022
Web-eWorks pushed a commit that referenced this pull request May 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Spacestation.lua attempt to index field '?' (a nil value) Station Menus Ocassionally Unusable
4 participants