Having decided to declare my latest project a prototype, and further to declare that it has outlived its usefulness (see my prior post), it’s time for a prototype retrospective.
As I described in my previous post, I failed to outline ahead of time the questions that the prototype would seek to answer, so it’s hard to say whether the prototype achieved its goals. But I can at least go back and infer what those questions might have been, given the answers I have acquired.
In the order that I recall them (recency and significance thus taking priority):
Question #1: Can I utilize the economic simulation model designed by Jonathon Doran and described by Lars Doucet for all economic activity in my city simulator?
Answer: Nope, it doesn’t seem feasible. The core model is completely oblivious to buyer/seller locations and travel costs, and I’m pretty sure that without locational awareness, this model would would flatten the economy of the city. Neighborhoods would have no economic distinction from each other, and traffic patterns would not be strongly or believably impacted by economic patterns. I may still be able to utilize the model for some resources, or for different levels of economic simulation, but I cannot come up with an efficient way to integrate travel costs into the double auction component of the economic model, since travel costs are highly relative to the specific buyer/seller pair, and cannot simply be calculated per buyer or per seller independently. Even when attempting to simply estimate travel costs per buyer or per seller, I ran into serious problems. One of the more notable ones involved one-way streets, and I absolutely do not want to give up one-way streets in order to make a particular economy model function efficiently; I’d rather find a new economy model.
Lars did outline some ideas for integrating multiple markets with travel costs getting involved for traveling merchants. It’s not immediately clear if I could take advantage of these ideas, but I’ll continue to revisit this subject in the future. Maybe something will click in my brain next time I attempt to tackle the problem.
Question #2: What resources should the player manage directly?
Answer: Money, nothing else. I initially started with the full set of resources being directly available to the player. Since this was a very early prototype, I was keeping it simple, so it started off only as money and an arbitrary physical commodity (I called it coal). Land ownership also had a fuzzy involvement, though it was never visibly presented to the player. Ultimately I decided that this would get out of hand fast. I also do not want this to become a resource management game, as that will naturally encourage minimaxing strategies, something I’d rather not encourage for this particular game. It’s core appeal should be creativity and the production of material for player-generated stories.
Fortunately, I was able to devise a solution and test a simple implementation that involved a hidden game agent that served as a proxy between the player and the game world. Sort of the city’s administrative body. As far as the game simulation was concerned, this agent was just another participant in the economy. It had price beliefs about various resources, had its own ownership of resources and land, and made its own trade requests like any other economic participant. As far as the player would be concerned, this agent was the window through which the player would interact with the economy. For any project the player was designing, the proxy agent would consult its own price beliefs about resources, and provide a monetary estimate of the project’s cost to the player. The player could then approve the project, handing over the requisite money to the proxy agent. The agent would in turn use the money to purchase all required resources to complete the project (such as building roads or city-owned buildings). The agent could also transparently go into debt if it underestimated prices, and this debt would simply influence its future price beliefs and thus the price estimates that it would provide to the player, thereby keeping the agent’s average account value near zero.
This appeared to work well, and kept the details that the player needed to worry about to a minimum. Interface elements could always be added so that if desired, a player could look at the details of the project costs, and the price estimates of various resources, trying to gauge the market. (Maybe steel will drop in price in a few years once the new smelter goes into operation; might be wise to build the new bridge after this happens.) But if a player just wants to jump in a start playing, having a single numerical value to worry about would be sufficient and welcome.
Question #3: Can I get away with using just integers in my simulation code, avoiding the reproducibility issues with floating point numbers?
Answer: Not quite. Integers ended up having far too many rounding errors, making it a pain to work with them. It was a chore always having to think about the various rounding strategies every time I did a division. And in numerous cases I was making code particularly convoluted so that I could accumulate many values and just do my divisions at the end, in an attempt to reduce rounding errors. For those situations where I still had loss of precision issues, the result was often simulation behavior that was hard to comprehend, leading to slow game balancing sessions.
I ultimately ended up quickly writing my own basic fixed point library, and usually just accepting the default round-toward-zero policy of integer division, since rounding errors should be insignificant on overall simulation behavior. As long as the rounding error is absolutely consistent across all CPUs and OSes, and doesn’t interfere with the large-scale patterns of the simulation, I’m happy. (I chose not to use an existing implementation because I couldn’t find one that worked with 64-bit values, since they require 128 bits in order to do precision-preserving multiplications and divisions. For now, I’m instead living with precision loss for those operations.)
Question #4: Can I use database normalization rules to help guide me in choosing my data layouts?
Answer: NO! Highly normalized data might be good when use of a flexible query language for unpredictable queries is valuable. Simulation code is going to be highly controlled, however, and that means that I can and should strongly organize my data according to the ways in which I access and manipulate it.
I’m still uncertain how I’ll combine this approach with flexible modding capabilities. I’ll need to keep an eye on this concern as I proceed, as I have a gut feeling that with the right design, proper flexibility can be maintained for modders, even if general flexibility is lost. This may involve giving modders working on simulation-specific mods the control necessary to also choose their own data structures and access patterns, and would thus require a certain degree of technical proficiency from them, as well as a good explanation of the core simulation’s model.
In addition to Jesse Schell’s tip #1 that a prototype should answer a question, he provided some other tips in The Art of Game Design that I’d like to reflect upon.
Tip #7: Pick a “Fast Loop” Game Engine. I’m using C++, not currently using any game engine, and do not yet have any scripting language integration, nor do I yet know how I’d like to eventually utilize scripting. As you can imagine, quick iteration times are a challenge. But thanks to the article “Why YOU Should Embed a Web Server in Your Game Engine” by ApochPiQ on Gamedev.net, I was motivated to do just that, and the results are very promising. Right now I only supported read-only integration for specific data, but it should be easy to integrate some write access directly into the game through the web API. I expect this to have a radically positive effect on iteration times, especially when I get deeper into game balancing.
Tip #6: It Doesn’t Have to be Digital. Unless you count my use of a whiteboard during my attempts to design various simulation algorithms, I haven’t yet directly taken advantage of this tip for my city builder project. But I did begin something of a side project with LEGO to exercise my creativity in building miniature cities. I started with a small suburban environment, doing one house or other element each morning. There’s no telling what miscellaneous inspirations that might provide going forward.
Tip #2: Forget Quality. I described in my prior post how I combined this latest prototype with some research and experimentation into Data Oriented Design, but how it might be useful to keep in mind that these two aspects are distinct (prototyping versus learning new technologies/skills). Reflecting on that further, it might be useful to truly keep them separate. Because I had iterated multiple times within my single prototype, trying to get the data layout figured out as best as I could, I developed a bit of attachment to the prototype. But that violates Tip #3: Don’t Get Attached. Oops. (I’m sure that wasn’t the only reason for developing the attachment, but it contributed.) Perhaps in the future I should almost go out of my way to keep the quality low, since it’s much harder to become attached to something that you know has poor quality components. As long as the prototype provides quality answers to the appropriate questions, all other quality can be dropped, and maybe even actively avoided.
It’s time to push on with the next prototype. This week I’ll be making sure that I understand precisely what questions this new prototype is intended to answer, and will be carefully considering what minimal amount of implementation will be necessary for providing those answers. I expect it will be at a much higher level, focusing not on simulation details, but on a much bigger picture of game play. It might not even be graphical. At all. Hopefully this will make the prototype process go much faster, and that can only be a good thing. Jesse Schell asserts as absolute truth “The Rule of the Loop: The more times you test and improve your design, the better your game will be.” Here’s to my efforts to iterate frequently and efficiently!