Word Damage Calculations

Today seems like a good day to talk about how word damage is calculated in Letter Quest, since it’s not nearly as simple as you might think. During our extensive prototyping phase (which is still going on!), we tried out several different systems for calculating word damage. Here are some of the ones that we tried but ultimately didn’t work out well: Simply making each letter in a word worth n damage – this had all kinds of problems, the biggest one was that longer words didn’t feel very strong compared to smaller words. Also, it didn’t take letter rarity into account, so spelling a word like “QUIZ” would do the same damage as a word like “TEST”, which should not be the case! Making each letter in a word worth n damage, and adding some bonus damage based on the word length – this felt a bit better than our first attempt. We simply did the previous calculation, ex: 5 letters in word x 3 damage = 15 damage, plus a bonus based on word length, 3 letter word = 0 bonus, 4 letter word = 3 bonus, 5 letter word = 6 bonus, etc. This felt better, but still had the issue of not taking letter rarity into account. Making each letter have a damage value based on letter rarity, according to the Scrabble letter values. Ex: an ‘E’ or ‘A’ is only worth 1 damage, a ‘B’ or ‘C’ is worth 3 damage, all the way up to a ‘Q’ or ‘Z’ which is worth 10 damage. Then we also applied the damage bonus based on word length. This felt much better, but it also felt extremely overpowered for rare and/or longer words. Ex: “SET” was worth 3 damage, and “ZOO” was worth 12 damage. Attempting to balance the game with this system was a nightmare – some players would defeat an enemy in one word, while other players would take up to 10 words to defeat that same enemy. We really didn’t want to implement a difficulty selection either, we wanted the game to be very “pick up and play”. Our final idea is the current system we’re using, and has both been received well by players, and has helped make balancing easier. It’s a combination of all of the above systems, with a few important tweaks. Current Damage System We decided to assign a value to each letter based on the letter’s rarity, using the Scrabble letter values as well as our own experience and play tests as a guide. We also compressed the range of values, so instead of going from 1 to 10 per letter, we only go from 1 to 3, in 0.25 increments. And we no longer use this as the damage value, instead we use it as a look-up into some “word length damage values”. Here’s what we ended up with for the letters A to F: [1, 1.25, 1.25, 1, 1, 1.25] Then we use these values to come up with how much the word is “worth”, and then we use that total word value to look up the damage in our damage look-up: [0, 5, 10, 20, 25, 30, 35, 41, 47, 53, 60, 67, 74, 82, 90, 98, 107, 116, 125, 135, 145, 155, 166, 177, 188, 200, 212, 224, 236] Here’s a couple of examples to help explain how it all works: Example: “FADE”: (F => 1.25) + (A => 1) + (D => 1) + (E => 1) = 4.25 We look up the 4th and 5th values in our damage look-up, and get...

read more

The Letter ‘Q’ and You

Most word-games tend to make the letter ‘Q’ tile actually be “Qu”, the idea being that usually a ‘Q’ is followed by a ‘U’. An avid word-game playing friend of mine pointed out to me that it’s always bothered her that most word-games do that, since it prevents players from spelling any ‘Q’ words that have a ‘Q’ followed by a letter other than ‘U’. Personally I didn’t even realize there were any words like that. So I ended up taking a look through the word list we’re using for Letter Quest, and found that my friend was correct – these are all words that have a ‘Q’ but not a “Qu”: QABALA QABALAH QABALAHS QABALAS QADI QADIS QAID QAIDS QANAT QANATS QAT QATS QINDAR QINDARKA QINDARS QINTAR QINTARS QIS QIVIUT QIVIUTS QOPH QOPHS QWERTY QWERTYS As more people played the game, I noticed that several people naturally just spelled “QAT” without even thinking about it. I didn’t even know that was a word until I saw people spelling it. So that’s why we don’t have a “Qu” tile in the game, in case any one was wondering or thought it strange. And there’s a good chance that, just like I did recently, you learned some new...

read more

Fun with letter tiles

As most of you that have been following this blog are aware, we’re making a game where you spell words using letter tiles to attack monsters. Things are going well so far, but several players have noticed something strange – sometimes it feels like certain letters are coming up too frequently or not nearly often enough. I noticed the same thing, but it seems to happen at random times. The previous system for selecting letter tiles was based on a weighted chance where each letter was assigned a weighted chance based on the English Scrabble letter distributions (I figured the makers of Scrabble probably knew what they were doing when they decided how many of each tile to include in the game). When we need to choose a new letter, we do a weighted chance selection from all letters and that’s that! Or so I thought. Turns out that doesn’t make for a very good experience. Here’s why: there are 12 letter Es, and only 1 letter Z, for example. So what was happening was that the board would end up with 5 or 6 Es on it sometimes, simply due to the randomness of the random selection (go figure!). There must be a better way. After a nice walk (never underestimate the usefulness of some time outside and some fresh air!), I thought “why don’t we just create all of the tiles that Scrabble has?”. In other words, why don’t we create a “bucket” of tiles at the start of a game, and add 9 As, 2 Bs, 2 Cs, 4 Ds, 12 Es, etc. according to the Scrabble letter distributions. This way we end up with 98 tiles (Scrabble has 100, but two of them are blank tiles – we don’t have blank tiles). So, we simply take all those tiles, shuffle them, and read them as a timeline one-by-one. When we go through all 98 tiles, we simply shuffle them again and away we go! After this was put in the game players have commented that the game just “feels better”, and “it’s easier to spell words now”, even though I never told them anything had changed....

read more

Efficient array shuffling in AS3

Shuffling arrays is something that I end up doing quite frequently, especially since I tend to recycle objects whenever possible, so it’s better to shuffle an array instead of creating a new one. Unfortunately array shuffling can be really inefficient in AS3, which can be problematic on mobile devices and slower CPUs. There have been several tutorials written on how to randomly shuffle an array in AS3. Most of them are quite slow, especially the ones that rely on calling Array.splice(), Array.sort(), or Array.slice(), all of which are known to cause hidden object allocations. I need something efficient, and preferably one that works in-place so that memory allocations can be avoided. I happened upon a post explaining the Fisher-Yates Shuffle, and it was exactly what I was looking for! It’s an efficient, in-place way to shuffle a set of data. After a few minutes I adapted a version of it to fit what I needed, which was to sort an array of integers (which are representing letters of the alphabet on tiles on the in-game tileboard). public static function arrayShuffleFisherYates(array:Array):Array { var m:int = array.length; var i:int; var temp:int; // while there are still elements to shuffle while (m) { i = int(Math.random() * m--); // swap it with the current element temp = array[m]; array[m] = array[i]; array[i] = temp; } return array; } This ended up being so efficient that I actually changed several places in the game’s code where things needed to be shuffled to this new method. One of those was handling the letter tile selection for the game. As a bonus, I happened upon finding the fastest way to clear an array or vector in AS3: vec.length =...

read more

Weighted Chances

Random selection just doesn’t cut it when it comes to several of the random choices we need the game to make. So I implemented a weighted chance system that gives us a lot more control. Say you have a set of possible enemy types to choose from. The naive approach is to just randomly select from amongst all enemy types available. But that gives us no control over the selection. What if we want to have an enemy type that is much more likely to be selected compared to all others? Or an enemy type that we would like to select very seldomly? Those are the requirements we decided upon. So that’s where weighted chance comes in. It’s pretty straightforward: you simply assign a weighted chance to each enemy type. That weighted chance is used to calculate how likely a given enemy type is to be selected relative to all other enemy types. For example, an enemy type with a weighted chance of 100 is 4 times more likely to be selected than one with a weighted chance of 25. The beauty of this is that we don’t have to make sure that the weighted chances of all enemy types add up to some number, say 100 (which is what some simple implementations of a weighted chance system do). That would be a pain because what if there are 4 enemy types, each with a weighted chance of 25. Then we add a fifth enemy type…are we supposed to go and change all 5 of them to a weighted chance of 20 now? Gross! So instead we use the weighted chance as a relative value. Because we’re using AS3, where using the variable type of * is much slower than using a strongly-typed variable, we can’t make a generic weighted chance function that accepts arrays of any type (well of course we can, but it would be slow). Instead, for efficiency, we have a separate function for each type of data that we need to randomly select from. In the whole game we’ll likely only end up with half a dozen of these, so it’s not so bad. Here’s an example where we want to select a random enemy def from an array, where each element is an instance of DefEnemy and has the property “WeightedChance”: private function weightedChanceEnemyDefSelect(enemyDefs:Array):DefEnemy { var selectedItem:DefEnemy = null; var sumOfChances:int = 0; var defs:Array = enemyDefs; var numItems:int = defs.length; for (var i:int = 0; i < numItems; ++i) { selectedItem = defs[i]; if (selectedItem.WeightedChance > 0) { sumOfChances += selectedItem.WeightedChance; } } var selectionChance:int = Utils.randomIntFromRange(0, sumOfChances); for (i = 0; i < numItems; ++i) { selectedItem = defs[i]; if (selectedItem.WeightedChance <= 0) { continue; } // is this the right item? if (selectionChance <= selectedItem.WeightedChance) { break; } // keep going, haven't found the right item yet selectionChance -= selectedItem.WeightedChance; } return selectedItem; } In case you’re curious as to what the Utils.randomIntFromRange function looks like, it’s pretty simple: public static function randomIntFromRange(minValue:int, maxValue:int):int { if (maxValue <= minValue) { return minValue; } return (Math.random() * (maxValue - minValue)) + minValue; } And that’s it – things are efficient and we get exactly what we need – everyone is super-happy! 🙂 One last thing – if  you’re wondering why we cache the array that is passed in as a local variable, it’s because this code is run several times during a few parts of the game, and it’s known that accessing local variables is faster for local variables than it is for member...

read more