Learning from History: Avoiding the Mistakes of the Past
This is an essay in the Herodotus series. Please read at least Introducing Herodotus before proceeding.
Regaining perspective
This essay takes a higher-level view than the previous article, A Geological Inquiry. Where the last essay focused on the implementation of a particular traditional simulation in Herodotus, here we examine further the contexts in which this kind of history storage and retrieval can be useful. In particular, this essay will point out some ways in which a game can learn from player history, rather than force the player to repeat his mistakes.
Here are a few ways, inspired by Koster, Perko, others, and my own research, in which collecting player actions can provide useful simulation and gameplay information:
- Difficulty scaling
- Record a player's deaths or kills, or time spent trying to complete an objective. If it passes some threshold, tone up or down the content to match the player's level. If they need to meet a certain NPC to progress, let the NPC's path cross theirs, or drop more hints. If the player is exploiting a "cheap" tactic, you can weaken the tactic.
- Economics
- Record a player's purchases and sales, or their drain on particular resources such as certain monster types. Modify local shop prices, spawn rates, etc to reflect the player's influence.
- Personalized challenges
- Record a player's preferred obstacles and solutions. Tailor the challenges to these types. If they like dungeon crawls, give them more dungeon missions; if they like diplomacy, give them more diplomatic missions. If they solve their jumping puzzles with long jumps more than high jumps, make future platform problems use more long jumps.
- Personalized rewards
- Record a player's preferred or needed skills and activities. Tailor the rewards to these types. If they like using swords or fire spells, give them more of those where possible in the drop tables. If they've been killing Yetis for two hours looking for a single hide, increase their drop chance. If they linger on the beautiful scenery, give them more chances to see it.
If they all seem a bit similar, that's because they all reflect the basic principle of player-driven content: Record enough information to give players meaningful experiences.
If they all seem a bit similar, that's because they all reflect the basic principle of player-driven content: Record enough information to give players meaningful experiences. Those four categories contain most of the really thrilling features that a system like Herodotus can help provide. Even if a game doesn't use Herodotus for any of the "hard" simulation like politics or economics, employing simple player-tracking metrics can be extremely useful. Imagine: the designer can detect and solve player boredom at runtime! Whether this is an online game, where the data collection is viewed by a human, or an offline game, where the data is acted on by the system, Herodotus provides a unified interface for player data collection, and expresses it in the same way as the simulation's time-based data storage. You can track popular and unpopular items or skills or quests, easy and hard dungeons and monsters, all in a straightforward and consistent way.
Scales of Justice
Easy and Hard settings just don't do it for me anymore. I want to be "Hard", but too often "Hard" just means "we turned up the numbers so it takes five minutes to kill the first enemy." I want to experience content, but "Easy" doesn't feel like a challenge - it doesn't get me involved. Even worse, sometimes I'm in the mood for a masochistic old-school challenge, and sometimes I just want the game equivalent of knitting to pass the time. It's about time to drop the traditional idea of static difficulty levels.
While Herodotus can't scale your game's difficulty for you, it can provide a framework for your own difficulty scaling algorithm. For a platformer such as Super Mario Bros., you might define a kills fluent to keep track of the deaths and killers of various creatures, including the player. The context of such fluents might be something like {victim, killer}, and killers can be things like "Third Goomba collision of World 1-1 at x=14", "Pit fall in World 2-3 at x=30", "Player jumping on head in World 3-1 at x=17", "Shell of Koopa Troopa 2 kicked by Player in World 4-1 at x=9". In other words, a tuple like {actor, action, area, location}. It's easy enough to add a little Incident that sets Fluents like these every time an entity dies. Through the course of a Mario game, that's surely under a thousand Incidents, which is hardly many at all. And once your game has collected this information, it can act on it.
For the Mario example above, we could insert some logic like the following on each player death: "If the player died at roughly the same spot as last time, drop a One-Up out of the sky so that he might find it. If the player died at roughly the same spot for the last five times, see what the most common cause of death was. If it was an enemy, remove or slow down that enemy. If it was a pit, provide a Koopa Troopa flying over that pit to jump on." And so on. Similarly, if we checked Goomba deaths and saw that every Goomba died within a few x-units of its starting position, we might make Goombas move more quickly or change directions more often, or try some other simple machine learning trick. These small modifications would have significant consequences on the difficulty curve of Super Mario Bros., of course, and would have to be carefully tuned to avoid exploitation.
It's worth noting that Gradius has always used a Rank system whereby players with high point totals or powerful weapon loadouts would be presented with tougher challenges at a given point in the stage, or tougher stages, than a weaker player. Zanac, too, gave players with better powerups and more violent approaches a harder time. These are proof that automatic difficulty scaling can still provide an extremely difficult game, and also that difficulty scaling doesn't need to take up much in the way of processing power: both games had strong showings even on the NES.
Where the hard part of automatic difficulty scaling comes in is the way challenges are scaled. Some game systems will need to include data with the challenge itself on how to scale it - for instance, varying difficulties of puzzle for a particular "puzzle" challenge. Others will be able to use algorithmic scaling techniques as described in the Mario example above. Most will fall into a hybrid class where many aspects of difficulty can be modified invisibly and algorithmically, and the rest can be handled as special cases. In any event, Herodotus can provide the information those systems need to do their work, and can receive feedback from those very systems to improve the accuracy of their difficulty predictions.
Supply and demand
In role-playing games such as Suikoden, where a player assembles a fairly large army and outfits them, local economic conditions stand a chance of being strongly impacted by player purchases and sales. But the games don't seem to collect or use much information about player economic activity. Might it not be fun to be able to play a local economy by introducing shortages or surpluses or substitutable goods, and profiting from that?
Herodotus won't simulate your economy, but it can provide you a framework for your own economics algorithms. In the case of sales to and purchases from a merchant, we could provide a trade fluent with a context like {purchaser, seller, good}, as well as a stock fluent with a context like {good, owner, location, amount}, along with a value fluent with context {good, seller, value}. Finally, we want sale-markup and purchase-markup fluents with context {good, buyer, seller} The trade and stock fluents are easy to update on purchases, acquisitions, and sales.
The algorithm for value could look a bit like this: "Start with the good's base value; If we have fewer in stock in this location than a certain threshold, increase the price by a function of that difference; If we have other, similar goods, and a shortage of this good, increase the value of this; If we have a shortage of another similar good, but a surplus of this, reduce the value of this;" and so on. the two -markup fluents could be a function of the relationship between the seller and buyer, the availability of other shops in the area, and so on. In other words, value is the value that the merchant believes an item has, and the -markup fluents are the multipliers he's willing to sell or buy at. For instance, a weapon shop owner might not have interest in a particular herb, or you might have learned that this shop owner really likes that herb; it gives a lot of depth to the choice of whom to sell to or buy from. We should banish the days of selling pounds of zombie flesh at great expense to pie makers, unless it makes narrative sense!
This is a pretty simple strategy, but I think that it could add a lot of depth to what's normally an extremely boring and braindead aspect of a role-playing game. It also provides room for a lot of related systems like bribery, gifts, friendship with and between NPCs, inter-store trades, and so on. I'm reminded of the third chapter of Dragon Quest IV in which the player must tend a weapon shop while his boss is out. Later in that chapter, in a different town, you acquire weapons and give them to your wife, the keeper of your own shop, to sell at a markup. I would have loved to play an entire game of this.
Fight or flight
In Super Mario Bros. 3, there are two main solutions to most levels: Run along the ground or fly through the air. Generally, there are enough choices of level to pick a mainly sky-friendly or a mainly ground-friendly path through a stage, but what if I just want to make a game of flying? Wouldn't it be nice if the game could pick up on my love of flight and provide more flight-surmountable obstacles, and more challenging flight paths?
Herodotus can't redesign your platforms, but - and this will sound familiar - it can provide you with the data your system needs to pick appropriate challenges to your player. By recording takeoff and land fluents, and measuring the time between them, as well as the number of takeoffs, you can get a reasonable idea of the number of times flight was used and for how long.
After collecting various data on this, and comparing it to the expected values, you can start to give more long runway platforms, wider gaps between platforms, more midair obstacles and enemies, and more rewards available only to flyers, as well as more flight powerups. We should let the player play the game he likes! Conversely, if the player never uses flight, we can either encourage him to use it (by providing a well-scaled set of flight challenges) or we can give him stronger platform-jumping and enemy-stomping experiences. Playtest and tune your game forever.
Give the people what they want
Most players of a class-based RPG have had the experience, as a mage, of finding the game's most incredible and overpowered and ridiculous broadsword - and being utterly unable to make anything of it besides a trophy, while your own staff and spellbook are showing the nicks and wear of the last twelve character levels. By weighting loot tables according to what players actually use, we can get rid of a lot of the irksome junk that just gets vendored. We don't need to do this by letting Barbarian enemies drop superior spells; we can just make the items that were going to drop anyway more likely if a party member can use it. That's an old trick, but Herodotus can take it one step further: don't go by player ability only, but also by player preference.
Herodotus can't weight your loot tables, but - all together now - it can provide your game with the data it needs to do so. Or to remove its loot tables entirely, though that's a topic for another article. For now, we can create a preference fluent with a context {item, timesUsed}. Further, an item could be a tuple like {itemID, category, ...}. Whenever an item is used, whenever an enemy is attacked with a skill, &c, an Incident can be triggered (or, if you so desire, several such events can be coalesced into a single Incident) to update the corresponding preference fluent.
When a treasure is opened, or an enemy is slain, or a shopkeeper offers a rare item, this preference fluent can be checked to try and find a suitable item for maximizing player thrill and enjoyment. Or, if you feel like the player should try other things once in a while, you can give him something entirely different. Herodotus doesn't make the rules! It just provides them with the data.
But wait, there's more...
These are just a few techniques for using player data to customize and improve your gameplay. Furthermore, player data collection is just a small subset of the data collection and historically-driven simulation possible with Herodotus. Designing standard narrative systems as interactive simulations - or movements between interactive simulations, as in a string-of-pearls narrative - can vastly improve the narrative structure, and make the act of playing much more natural, personalized and communicative.
One goal of Herodotus is to remove lazy randomness from games.
Joe Osborn 2008-01-30
0 Comments:
Post a Comment
<< Home