Thursday, May 23, 2019
3 Types Of Game Masters
As I mentioned towards the end of last week's Inappropriate Characters episode, I've broken GM differences into 3 basic types...
The first I'll describe is the Dungeon Master from beloved Saturday morning cartoon Dungeons & Dragons.
The Dungeon Master presents the facts, sets things in motion, and then pretty much disappears into the background until his presence is required down the road. The RPG Pundit mentioned the "clock-maker God" as an example of this sort of GM. Once he creates a world, his hands are off the wheel and things take their own course, according to the rules or the scenario or the GM's notes.
He's impartial, objective, and about as neutral as a GM can get. Total free market... no matter what the outcome. Is it discipline or ambivalence that keeps the Dungeon Master type GM from subtly nudging reality here or there?
The second GM type is Zeus from the 1981 sword & sandals film Clash of the Titans.
Zeus is more subjective, the campaign world filters through him, his perspective. His hands aren't always on, but also never far from the wheel. Yet, he still believes in letting go, allowing fate or free will decide which path is taken. Zeus doesn't have a specific outcome in mind, but tries to maintain a fairness or equality of opportunity, according to his personal standards of ethics, morality, and aesthetics.
If you remember, Zeus has a few tricks up his sleeve. While balancing the campaign world and all the characters in it, he feels justified tweaking the chances of a result or two in the name of either fair play or compelling story. Occasionally fudging a die roll for the greater good.
However, Zeus is not a story-gamer. He doesn't know how things will end up. He hopes things will turn out well for those proving themselves worthy, occasionally exerting a bit of influence behind the scenes, but stopping short of pre-determining adventure's conclusion.
The third and last type of GM is the Wizard of Oz.
The Wizard of Oz is a showman and a conman. He uses slight of hand, misdirection and railroading in order to get the end result he's looking for. His GMing creates the illusion of free will and randomness, when in reality, he pulls all the strings.
The Wizard of Oz is the most likely to be a story-gamer, since that style of GMing is directly opposed to the traditional, old school approach (though many GMs in the 70's and 80's aspired to that standard).
Another aspect of this 3rd type is bluster, pomp, and pageantry. Anything to build himself up, to make the all-powerful GM appear greater, wiser, and stronger than he really is. His will is mighty and players should not test it, or else face his wrath. Also, be prepared to confront several self-important NPCs (stand-ins for the GM himself) who shall not be fucked with. You'd have better luck wrassling an ancient black dragon.
Please feel free to comment with your experiences, your opinion of the 3 types, etc. Where do you fit in all this?
VS
Super Panda Adventures (PC) - Guest Post
This week on Super Adventures, guest poster mecha-neko has returned to write about a game with the best title. It's literally 'Super Adventures', except with a panda in it! Now I just need to find a science fiction movie called Ray Hardgrit's Sci-Fi Cat Adventures so I can manipulate him into to writing my other site for me as well.
Also I realise that this was published on a Wednesday, not a Monday, but mecha-neko has a fondness for his banner and I didn't have the heart to tell him he couldn't use it. I guess you could read it on a Monday if that works better for you.
Hello everyone! I hope the new year is treating you well!
Hmm... hands up who'd like to see a brightly coloured platform game about a loveable panda!
With a title like that, how could I possibly resist?
Read on »
Also I realise that this was published on a Wednesday, not a Monday, but mecha-neko has a fondness for his banner and I didn't have the heart to tell him he couldn't use it. I guess you could read it on a Monday if that works better for you.
Hello everyone! I hope the new year is treating you well!
Hmm... hands up who'd like to see a brightly coloured platform game about a loveable panda!
| Developer: | Paul Schneider, BlueEagle Productions | | | Release Date: | 23rd April 2013 | | | Systems: | Windows |
With a title like that, how could I possibly resist?
Read on »
Broken Sword: The Smoking Mirror - Remastered (PC)
| Developer: | Revolution | | | Release Date: | 1997 | | | Systems: | Win, OS X, iOS, Android |
Looking back over the games I've played for my site over the last seven years, I've noticed a pattern forming: I've played a horror game for Halloween on every odd-numbered year, while even-numbered years have gotten games like Snake's Revenge and Saints Row IV at the end of October instead. Seems to me that I've got a bit of a tradition forming here... but I despise pointless traditions, so this year on Super Adventures I'm breaking the pattern by playing Broken Sword: The Smoking Mirror - Remastered! I don't think I'll be seeing any ghosts or zombies in this one.
I'm torn here, because I really want to call it Broken Sword II, but there's clearly no 'II' in that logo up there. There was in the original game's logo, but they've taken it away for the Remastered version, because I guess knowing what order things go in only confuses modern audiences. Personally, I'm more confused by the subtitle. Is it referring to a mirror that's recently been used as a murder weapon? A mirror people that go out to when they fancy a quick smoke?
Wait, I forgot to mention that I'm playing the game on Halloween because it was released on October 31st, 1997, the same day as Curse of Monkey Island and the Blade Runner adventure game, so all three are twenty years old today! Well okay, technically this is only seven years old because I'm playing the Remastered version on Steam. I would've played my original CD version but I've lost it. I've checked shelves, I've checked boxes, I've checked other boxes buried underneath boxes, and it seems like the bloody game's just vanished.
Funny thing is, when I bought the game the discs were already damaged and some of the video files wouldn't copy when I installed it. Fortunately, when ScummVM came out there was a compatibility problem with the video codec so Revolution put revised cutscene files up for free and I could finally play this game I'd owned for years! I actually still have that install on my hard drive ready to go, but I lost half the files in a hard drive failure and now ScummVM won't even recognise it.
It's like fate doesn't want me to play this game, and it doesn't care how much physical storage media it has to break to keep me from it.
Read on »
Apologies For Lapses In Posts - Resolutions! (Monday Musings 72)
It's been so very, very long since I last posted, so I want to apologize. This is a good time to review my New Year's Resolutions, and definitely one of them is to post weekly - which I was pretty consistent with last year.
The reasons are that I enjoy blogging, and it'll "force" me to think about pertinent topics to write about, which can also help with figuring out streaming topics.
Resolutions can be kept if we troubleshoot and remove foreseeable obstacles that can get in the way of resolutions.
The major obstacle with weekly postings, is not having a set schedule to sit down and write. As I'm not working on Mondays, it makes the most sense, after exercising, chores, and breakfast, to write my post on Mondays.
There's a possibility that I'll be working on Mondays, so the other free day I have is Friday, as the alternative option. I didn't know that Blogspot can automate the exact time to post, so Friday is a perfectly viable alternative.
My other resolution, I've been rather consistent, which is getting up as soon as the alarm clock rings, which I was never able to do since day 1, until recently.
I changed my alarm clock to an earlier time (from 7:00 AM to 6:30 AM) which will give me the time to exercise before work. I noticed my mind shuts down around 8 PM (I'm not a night owl), so why not go to bed earlier and get up earlier in the morning, during the hours where I truly enjoy activities?
Thanks to my Twitch buddy, who inspired me to pop out of bed as soon as the alarm clock rings, I've been more or less successful in doing so. He mentioned that he'd get up at 5:00 AM, exercise and do his chores, and has a couple of hours before he heads off to work to have fun. I was very motivated, because I would like to get everything out of the way as soon as possible, so the rest of the day, after work, I'll have free time until heading to bed!
I was surprised because last week, I actually exercised before work (the first time in decades), and today, despite not streaming, I also exercised!
In other words, I've been streaming in part because it forced me to exercise, but now I have internal motivation to exercise thanks to my Twitch friend! I very much enjoy streaming, but I now have more freedom to chose to stream or not (in the past, the major reason to stream is because it forces me to exercise). If I get burned out from streaming (which can very well happen), I won't feel guilt that I'm not taking care of my health.
In this way, I can truly evaluate Twitch streaming as a hobby I want to keep or not, and if I no longer enjoy it, I can leave streaming behind without feeling remorse.
What are your resolutions, and have you kept them? If you have, I'd love to hear about your troubleshooting.
The How of Happiness Review
The reasons are that I enjoy blogging, and it'll "force" me to think about pertinent topics to write about, which can also help with figuring out streaming topics.
Resolutions can be kept if we troubleshoot and remove foreseeable obstacles that can get in the way of resolutions.
The major obstacle with weekly postings, is not having a set schedule to sit down and write. As I'm not working on Mondays, it makes the most sense, after exercising, chores, and breakfast, to write my post on Mondays.
There's a possibility that I'll be working on Mondays, so the other free day I have is Friday, as the alternative option. I didn't know that Blogspot can automate the exact time to post, so Friday is a perfectly viable alternative.
My other resolution, I've been rather consistent, which is getting up as soon as the alarm clock rings, which I was never able to do since day 1, until recently.
I changed my alarm clock to an earlier time (from 7:00 AM to 6:30 AM) which will give me the time to exercise before work. I noticed my mind shuts down around 8 PM (I'm not a night owl), so why not go to bed earlier and get up earlier in the morning, during the hours where I truly enjoy activities?
Thanks to my Twitch buddy, who inspired me to pop out of bed as soon as the alarm clock rings, I've been more or less successful in doing so. He mentioned that he'd get up at 5:00 AM, exercise and do his chores, and has a couple of hours before he heads off to work to have fun. I was very motivated, because I would like to get everything out of the way as soon as possible, so the rest of the day, after work, I'll have free time until heading to bed!
I was surprised because last week, I actually exercised before work (the first time in decades), and today, despite not streaming, I also exercised!
In other words, I've been streaming in part because it forced me to exercise, but now I have internal motivation to exercise thanks to my Twitch friend! I very much enjoy streaming, but I now have more freedom to chose to stream or not (in the past, the major reason to stream is because it forces me to exercise). If I get burned out from streaming (which can very well happen), I won't feel guilt that I'm not taking care of my health.
In this way, I can truly evaluate Twitch streaming as a hobby I want to keep or not, and if I no longer enjoy it, I can leave streaming behind without feeling remorse.
What are your resolutions, and have you kept them? If you have, I'd love to hear about your troubleshooting.
The How of Happiness Review
Tuesday, April 2, 2019
Explore Simple Game Algorithms With Color Walk: Part 11
In this installment of exploring game algorithms using the simple game Color Walk, we're going to do something a little different. Last time we explored a number of variations and hybrids of Dijkstra's algorithm—the classic, efficient graph algorithm for finding shortest paths—and found that pairing it with a pre-run of Greedy Look-Ahead (GLA) performed better than any other algorithm we've seen so far. This time we're not going to explore any new algorithms. Instead, we're going to look into what makes Dijkstra's algorithm tick: the priority queue. Save for this variant of a standard queue, Dijkstra's algorithm is conceptually the same as Breadth-First Search (BFS), so we want to see what makes this priority queue so special and how we can implement one efficiently with a binary heap.
The idea behind a priority queue is simple. It mostly acts like a standard queue, where items are put into the queue, and then items are taken out of the queue. The difference comes from which items are taken out of the queue first. In a standard queue, the first item put in will be the first item taken out. This type of queue is also referred to as a FIFO or First-In First-Out queue, as opposed to a LIFO, or Last-In First-Out queue, also known as a stack. In a priority queue, each item is assigned a key value, and the item with the lowest (or highest) key value is taken out of the queue first, regardless of the order that the items were put into the queue.
The key values can be anything useful for maintaining the priority queue. In our case it was advantageous for Dijkstra's algorithm to use a key value that was some heuristic of how close the game was to an empty board. That way the next closest move to the end-of-game condition could be taken from the queue and checked. If we can pull the next closest move out of the queue faster than we can search all of the moves up to that move, then we will easily win out over BFS.
The trick is to structure the priority queue in a way that makes it fast to extract the item with the minimum key value. We can't go searching through the entire queue for the minimum each time because we're letting the queue grow to 250,000 items. It would take way too long to search through such a big queue every time through the search loop, looking for the minimum key value. Similarly, it would take too long to keep the array sorted as new items were inserted because inserting items in the middle of the array would require shifting all of the items after the inserted item, taking linear time with the number of items in the queue.
Another option is to use a Binary Search Tree (BST), where each node has a left and a right child, and all children to the left of a node will have keys that are less than that node, and all children to the right of a node will have keys greater than that node. A BST can be searched and nodes can be inserted in O(lg n) time, but because we're continually removing the minimum node, the tree can easily become unbalanced. Operations on an unbalanced BST have worse running times. This issue could be alleviated by using a special BST, like a red-black tree, that maintains a balanced structure, but we would still need to deal with the added size of a more complex data structure. Red-black trees have a fair amount of overhead compared to an array, and that would mean less room in memory for the 250,000+ moves we want to keep in the priority queue.
Numerous other options exist for priority queue data structures, but one of the most elegant and simple structures that has everything we need is the binary heap. It can be implemented cleanly using an array, so it doesn't have much overhead, and the operations for inserting items and extracting the minimum key value have the desired O(lg n) running time. That means pulling out the next move from a queue of 250,000 moves will only take about 18 times longer than a queue of 1 move, instead of 250,000 times longer if extracting the minimum had a linear running time of O(n). The JavaScript priority queue that we used when implementing Dijkstra's algorithm implements the binary heap as one of its choices of data structures behind the priority queue. Let's take a deeper look at how this data structure works.
While the binary heap is implemented on top of an array, adding a specific way of managing the array elements and a couple new operations to the normal array operations, it is easier to think of it as a binary tree (but not a binary search tree, that's different). The tree is filled from left to right on each level of the tree before advancing to the next level and filling from left to right again. This method of filling the tree allows for easy storage in an array because new items can be added to the array by simply incrementing the index. The following picture shows the correspondence between the array behind the heap and the tree with nodes labeled by their index in the array.
The tree is a heap when it maintains a specific property among all of its nodes. The key value for each node is the minimum key value among all nodes in its sub-tree. The above picture also satisfies this heap property if we assume that the numbers in the nodes are key values. We'll see shortly how this property can be maintained during insertion operations, but look at what we get with it. By definition, the minimum key value is at the root of the tree, which means it is the first element in the array, so we can always find it immediately.
In order to implement insertion, we'll need to be able to quickly find the parent of a node. Due to how the heap is stored in the array, the parent of a node is simply the node's index divided by two. But arrays are zero-indexed, so we need to adjust slightly for that, giving us the following formula for finding a node's parent:
parent = (node - 1) / 2
Now, when inserting a node, we can stick it at the end of the array, and keep comparing it with its parent. If the node is less than its parent, swap them and compare the node with its new parent. We are guaranteed to maintain the heap property for every parent that's swapped because that parent was less than its other child before the swap, and we're replacing it with a node that's less than the parent after the swap. Therefore, the new parent is less than both of its children. The code is straightforward:
Notice that a node inserted into the binary heap will start at a leaf node and at most swap with all of its parents on its way up to the root node, if it happens to have the smallest key in the heap. Since the heap is a full binary tree except for the last, possibly partial, level and the height of a full binary tree with n nodes is lg n, the running time of this function is O(lg n).
That's basically all there is to inserting into a binary heap. This gif shows what happens when building up a heap by inserting somewhat random elements:
Extracting the minimum key value from the heap is a two step process. First, we need to find the minimum key, which is easy because it's the root of the heap and the root is the first item in the array. Second, we need to fill in the root with the new minimum key value in the heap. This step is a little trickier, but still not too bad.
To fill in the root, the easiest item to use is the one at the end of the array. We can take that item and put it in for the root, reducing the size of the array by one in the process. Now we most likely don't have the minimum key at the root, so we have to fix that. We can fix the heap by comparing the root with both of its children and swapping it with the child with the smaller key. This swap will restore the heap property between the root and its children because it now has the smallest key of the three nodes.
The entire sub-tree of the child that was not swapped also still satisfies the heap property because it hasn't changed from before the operation, except possibly by removing one of its leaves if the last item in the array came from its sub-tree. The sub-tree with the swapped child will need to be fixed, so we can repeat the same comparison and swap with it and its children as was done with the root. We can continue moving the leaf node that came from the end of the array down the heap until it's key is less than both of its children. At that point the heap property has been fully restored, and the extraction is complete. The code reflects this description quite closely:
left_child = node*2
right_child = node*2 + 1
You can check the picture of the binary heap and its corresponding array to convince yourself that this is true:
Since the real code has the array index starting at zero, we need to add one to the left child index to get the right index. After that, we assume the current node has the minimum key, compare it to both of its children, and if the minimum key has changed after the comparisons, we swap the current node with the node with the minimum key. Otherwise, we're done and we can return the original minimum node that we had saved off before fixing the heap.
It is easy to see that this function runs in O(lg n) time for the same reason that insertion did, except the node is moving down instead of up the tree. The new root will at most travel down from the root to a leaf node, and the height of the tree is lg n for n nodes. Take a look at what it looks like for items to be extracted from the binary heap:
That's about all there is to a binary heap. It's a neat little data structure that elegantly solves the problem of implementing a priority queue. It has the nice property of using an array for efficient storage of items in the queue, and the heap property can easily be maintained with efficient insert and extract operations. It's a great data structure to have in your tool set.
We've just about wrapped up our exploration of simple game algorithms with Color Walk. We've covered a lot of ground through all of these posts, so next time I'll do a summary of all of the strategies we've looked at and how far we've come in solving this simple game.
Article Index
Part 1: Introduction & Setup
Part 2: Tooling & Round-Robin
Part 3: Random & Skipping
Part 4: The Greedy Algorithm
Part 5: Greedy Look Ahead
Part 6: Heuristics & Hybrids
Part 7: Breadth-First Search
Part 8: Depth-First Search
Part 9: Dijkstra's Algorithm
Part 10: Dijkstra's Hybrids
Part 11: Priority Queues
Part 12: Summary
Priority Queues in Theory
The idea behind a priority queue is simple. It mostly acts like a standard queue, where items are put into the queue, and then items are taken out of the queue. The difference comes from which items are taken out of the queue first. In a standard queue, the first item put in will be the first item taken out. This type of queue is also referred to as a FIFO or First-In First-Out queue, as opposed to a LIFO, or Last-In First-Out queue, also known as a stack. In a priority queue, each item is assigned a key value, and the item with the lowest (or highest) key value is taken out of the queue first, regardless of the order that the items were put into the queue.
The key values can be anything useful for maintaining the priority queue. In our case it was advantageous for Dijkstra's algorithm to use a key value that was some heuristic of how close the game was to an empty board. That way the next closest move to the end-of-game condition could be taken from the queue and checked. If we can pull the next closest move out of the queue faster than we can search all of the moves up to that move, then we will easily win out over BFS.
The trick is to structure the priority queue in a way that makes it fast to extract the item with the minimum key value. We can't go searching through the entire queue for the minimum each time because we're letting the queue grow to 250,000 items. It would take way too long to search through such a big queue every time through the search loop, looking for the minimum key value. Similarly, it would take too long to keep the array sorted as new items were inserted because inserting items in the middle of the array would require shifting all of the items after the inserted item, taking linear time with the number of items in the queue.
Another option is to use a Binary Search Tree (BST), where each node has a left and a right child, and all children to the left of a node will have keys that are less than that node, and all children to the right of a node will have keys greater than that node. A BST can be searched and nodes can be inserted in O(lg n) time, but because we're continually removing the minimum node, the tree can easily become unbalanced. Operations on an unbalanced BST have worse running times. This issue could be alleviated by using a special BST, like a red-black tree, that maintains a balanced structure, but we would still need to deal with the added size of a more complex data structure. Red-black trees have a fair amount of overhead compared to an array, and that would mean less room in memory for the 250,000+ moves we want to keep in the priority queue.
Numerous other options exist for priority queue data structures, but one of the most elegant and simple structures that has everything we need is the binary heap. It can be implemented cleanly using an array, so it doesn't have much overhead, and the operations for inserting items and extracting the minimum key value have the desired O(lg n) running time. That means pulling out the next move from a queue of 250,000 moves will only take about 18 times longer than a queue of 1 move, instead of 250,000 times longer if extracting the minimum had a linear running time of O(n). The JavaScript priority queue that we used when implementing Dijkstra's algorithm implements the binary heap as one of its choices of data structures behind the priority queue. Let's take a deeper look at how this data structure works.
Binary Heap Insertion (Enqueue)
While the binary heap is implemented on top of an array, adding a specific way of managing the array elements and a couple new operations to the normal array operations, it is easier to think of it as a binary tree (but not a binary search tree, that's different). The tree is filled from left to right on each level of the tree before advancing to the next level and filling from left to right again. This method of filling the tree allows for easy storage in an array because new items can be added to the array by simply incrementing the index. The following picture shows the correspondence between the array behind the heap and the tree with nodes labeled by their index in the array.
The tree is a heap when it maintains a specific property among all of its nodes. The key value for each node is the minimum key value among all nodes in its sub-tree. The above picture also satisfies this heap property if we assume that the numbers in the nodes are key values. We'll see shortly how this property can be maintained during insertion operations, but look at what we get with it. By definition, the minimum key value is at the root of the tree, which means it is the first element in the array, so we can always find it immediately.
In order to implement insertion, we'll need to be able to quickly find the parent of a node. Due to how the heap is stored in the array, the parent of a node is simply the node's index divided by two. But arrays are zero-indexed, so we need to adjust slightly for that, giving us the following formula for finding a node's parent:
parent = (node - 1) / 2
Now, when inserting a node, we can stick it at the end of the array, and keep comparing it with its parent. If the node is less than its parent, swap them and compare the node with its new parent. We are guaranteed to maintain the heap property for every parent that's swapped because that parent was less than its other child before the swap, and we're replacing it with a node that's less than the parent after the swap. Therefore, the new parent is less than both of its children. The code is straightforward:
function BinaryHeap() {
this.data = [];
}
BinaryHeap.prototype.enqueue(value) {
this.data.push(value);
var i = this.data.length - 1;
while (i > 0) {
var parent = (i - 1) >>> 1;
if (this.data[i].key < this.data[parent].key) {
var x = this.data[parent];
this.data[parent] = this.data[i];
this.data[i] = x;
i = parent;
} else {
break;
}
}
}We initialize an empty heap in the constructor, and we assume that each element added will have a key property in addition to whatever object information comes along for the ride. A real implementation would handle things slightly differently so that the heap structure could use a comparator function defined by the caller, and the value passed into enqueue() would be a generic object. I simplified things here slightly to make the code more clear. Also notice that the divide is implemented as the unsigned shift right operator >>> to divide by two and truncate the result to an integer.Notice that a node inserted into the binary heap will start at a leaf node and at most swap with all of its parents on its way up to the root node, if it happens to have the smallest key in the heap. Since the heap is a full binary tree except for the last, possibly partial, level and the height of a full binary tree with n nodes is lg n, the running time of this function is O(lg n).
That's basically all there is to inserting into a binary heap. This gif shows what happens when building up a heap by inserting somewhat random elements:
Binary Heap Extraction (Dequeue)
Extracting the minimum key value from the heap is a two step process. First, we need to find the minimum key, which is easy because it's the root of the heap and the root is the first item in the array. Second, we need to fill in the root with the new minimum key value in the heap. This step is a little trickier, but still not too bad.
To fill in the root, the easiest item to use is the one at the end of the array. We can take that item and put it in for the root, reducing the size of the array by one in the process. Now we most likely don't have the minimum key at the root, so we have to fix that. We can fix the heap by comparing the root with both of its children and swapping it with the child with the smaller key. This swap will restore the heap property between the root and its children because it now has the smallest key of the three nodes.
The entire sub-tree of the child that was not swapped also still satisfies the heap property because it hasn't changed from before the operation, except possibly by removing one of its leaves if the last item in the array came from its sub-tree. The sub-tree with the swapped child will need to be fixed, so we can repeat the same comparison and swap with it and its children as was done with the root. We can continue moving the leaf node that came from the end of the array down the heap until it's key is less than both of its children. At that point the heap property has been fully restored, and the extraction is complete. The code reflects this description quite closely:
BinaryHeap.prototype.dequeue() {
if (this.data.length === 0) {
throw 'Empty queue';
}
var min = this.data[0];
var last = this.data.pop();
if (this.data.length > 0) {
this.data[0] = last;
var pos = 0;
var last_idx = this.data.length - 1;
while (true) {
var left = (pos << 1) + 1;
var right = left + 1;
var min_idx = pos;
if (left <= last_idx &&
this.data[left].key < this.data[min_idx].key) {
min_idx = left;
}
if (right <= last_idx &&
this.data[right].key < this.data[min_idx].key) {
min_idx = right;
}
if (min_idx !== pos) {
var x = this.data[min_idx];
this.data[min_idx] = this.data[pos];
this.data[pos] = x;
pos = min_idx;
} else {
break;
}
}
}
return min;
}We needed some error checking at the beginning to make sure we don't try to extract an item from an empty queue. Then we save off the root, pop the last item off the array, and make it the new root. Then we start comparing the root node with its children. To find the children's indexes in the array is pretty straightforward. For whichever node we're at, the children will simply be the indexes that are double the current index and the index immediately following that one.left_child = node*2
right_child = node*2 + 1
You can check the picture of the binary heap and its corresponding array to convince yourself that this is true:
Since the real code has the array index starting at zero, we need to add one to the left child index to get the right index. After that, we assume the current node has the minimum key, compare it to both of its children, and if the minimum key has changed after the comparisons, we swap the current node with the node with the minimum key. Otherwise, we're done and we can return the original minimum node that we had saved off before fixing the heap.
It is easy to see that this function runs in O(lg n) time for the same reason that insertion did, except the node is moving down instead of up the tree. The new root will at most travel down from the root to a leaf node, and the height of the tree is lg n for n nodes. Take a look at what it looks like for items to be extracted from the binary heap:
That's about all there is to a binary heap. It's a neat little data structure that elegantly solves the problem of implementing a priority queue. It has the nice property of using an array for efficient storage of items in the queue, and the heap property can easily be maintained with efficient insert and extract operations. It's a great data structure to have in your tool set.
We've just about wrapped up our exploration of simple game algorithms with Color Walk. We've covered a lot of ground through all of these posts, so next time I'll do a summary of all of the strategies we've looked at and how far we've come in solving this simple game.
Article Index
Part 1: Introduction & Setup
Part 2: Tooling & Round-Robin
Part 3: Random & Skipping
Part 4: The Greedy Algorithm
Part 5: Greedy Look Ahead
Part 6: Heuristics & Hybrids
Part 7: Breadth-First Search
Part 8: Depth-First Search
Part 9: Dijkstra's Algorithm
Part 10: Dijkstra's Hybrids
Part 11: Priority Queues
Part 12: Summary
Op Compass - Game 5 Same Day, Different Fort
We are back in the Desert this week for the 5th Game in our ongoing Campaign at the start of the war. Today sees the plucky Brits led astray by a bunch of Kiwi Truck Drivers whilst the brave Italian Forces try to hold out.
If you want more information on the Campaign I have set up a separate page which is updated regularly with updates on rules along with links to all the previous games,
https://yarkshiregamer.blogspot.co.uk/2018/04/opcompass-1940-resource-page.html
The games are based on an excellent book by Robert Avery which is available from The Toofatlardies, there is a direct link to purchase the book on the Resource Page.
We use 28mm figures with this scenario taking place on an 8 x 6 table using a home brew set of rules, based on Iron Ivans Disposable Hero's.
![]() |
| Desert Rats advancing into the Fort |
Our last game was set on the morning of the 9th December 1940 in the Fort of Nibeiwa, this game is set on the afternoon of the same day just down the road (ok track) at the Fort of Tummar West.
This was to be a simultaneous attack with a similar adventure down at Tummar East, Infantry would lead the attack, supported by the Matildas of 7 RTR who were rushing (If a Matilda can rush) over from Nibeiwa.
The attack was lead by the New Zealanders of the No4 Reserve Motor Transport Company, instead of dropping off their Infantry cargo at the standard 500 yards from the target they raced forwards to within 150 yards and having stolen bayonets from the Infantry Bren Gun Teams they yelled "Come on you Pommie Bastards" abandoned their trucks and raced headlong into the Italian Defences.
![]() |
| Kiwi Truckers to the Fore |
Table Set Up and Terrain
Above is a picture of the table used for the game, as in the previous match up I have only used 8 x 6 of our avaliable space. Everything is as you see it, the road is just a track and gives no movement bonus.
The crates are Ammo Dumps
The following Special Rules are in force,
Kiwi Truckers
The 2 sections will not have an activation card in the deck but will always activate first, after the Ammo Dumps have been rolled for and before the first card is turned. They are not subject to morale tests.
Ammo Dumps
These rules also apply to buildings however they can only be set alight during the initial Artillery Barrage (see British Briefing below).
When stuck by Artillery or Mortar Fire an Ammo Dump will set on fire and have a chance of further explosions.
As soon as fire is set and once at the start of every turn follow the following procedure,
1. Roll 2d6 one plus and one minus, any minus and the wind direction changes (on the first turn of the fire ignore this), then roll 1d12 and 1d6 to indicate smoke direction from the fire the d12 indicates the clock direction and the d6 the number of 60 x 50mm smoke markers. So a roll of 7 and 4 would mean 4 markers at 7 o'clock from the dump. The dumps will burn all game once lit.
2. Roll 2d6 one plus and one minus, on any minus an additional explosion has occurred, roll 1 d 12 and a random direction indicator (the Spin for It App is ideal), the d12 is the distance in inches. At that location place a standard Mortar round and calculate any casualties as normal or if you are really good, set another Ammo Dump off ! Any double rolled on the initial pair of dice mean the explosions have stopped (the fire and smoke continues).
![]() |
| Ammo Dumps burn and beltch fire ! |
As in your previous game your brief is simple, clear all the active resistance from the Fort whilst minimising your losses.
Your Forces are as follows, all arrive anywhere on the far edge of the table as shown in the photo,
Kiwi Truckers - 2 x 8 man Sections no LMG (the Kiwis are immune to all morale checks and are removed when they reach one remaining figure)
2 Companies of Infantry each with
1 HQ of 3 Figs, Officer, Radio Op and Sgt
3 Units of 10 men each with 1 Sgt with SMG, 2 man Bren Team and 7 Rifles
After turn 5 place the British Reinforcements card into the deck and roll 1d4 on its first appearance, that is the number of turns until the following arrive,
1 x Troop 7RTR 3 x Matilda Tanks (I used a rogue Valentine for a change)
![]() |
| Bren Carriers Charge ! |
2 x HMG and crew each in a Bren Carrier
4 x Bren Carrier each with 1 x 8 man Punjabi Infantry Squad of 1 Sgt with SMG and 7 Rifles. I know 8 men is a squeeze in a Bren but I haven't got any more !
Italian Briefing
Once again you are faced with an onslaught of seemingly unstoppable Infantry and Tanks (although you still can't quite believe how slow those tanks are !), smoke and flame is all around, hang on as long as you can.
The Italians have a secret set up, I marked each building with a letter and put 3 letters along each of the trenches, each one can hold either 2 support weapons or 1 support weapon and a section of Infantry. Normal spotting rules apply.
![]() |
| Italian Mortar lays down fire |
Italian Forces are
2 x HQ with Officer and Radio Operator
3 x Rifle Sections with 1 Sgt with SMG and 10 Rifles
3 LMG Sections with 1 Sgt, 2 3 man LMG teams and 2 Rifles
2 x HMG with 3 crew
1 x Medium Mortar with 3 crew
1 x Light Mortar with 2 crew
2 x 75mm Field Gun with 4 crew
2 x 47mm Anti Tank Gun with 3 crew
1 x Anti Tank Rifle with 2 crew
![]() |
| Italian LMG Section defend a building |
Once the Italians have set up randomly select 4 buildings and or Ammo Dumps and these are declared as being destroyed by the strike, any troops in those buildings are lost. The areas hit will now be subject to the Ammo Dump rules above (note this is the only time in this game that buildings will set ablaze).
![]() |
| The initial Artillery Barrage causes chaos |
Those of you who are following the campaign will be aware of the streak of poor luck the Italians are currently suffering. This would prove to be the case once again. Table Set Up ready to go, in comes the Artillery Barrage..... and takes out the Italian HQ in turn 1 !
The British decided to push all their troops along the left side (as viewed in the set up photo) using the small walled area and then the tented area for a bit of cover.
![]() |
| Charge ! |
![]() |
| Fat Badger is so fat he gets stuck. |
![]() |
| Allies advance, Kiwis to the fore |
![]() |
| British Infantry working hard to push through the centre of the fort |
As in the real battle, the arrival of the Bren mounted Infantry swung the balance of the game in favour of the Allies, the commander taking full advantage of the high speed of the Brens to get the reinforcements to the right place quickly.
The last of the Italian Forces fell back on one of the trenches and stood ready for a final stand, things went well initially as a British section charged the southern end of the trench and was seen off with a bloody nose. However with concentrated fire the last of the ranged Italian Weapons was silenced leaving them with only Rifle armed troops. As the British had the ability to stand off with their HMGs and Lt Mortars we declared that the remaining Italians surrendered ending the game.
![]() |
| Allied Casualties Top, Italian bottom |
Losses Allied 36 Infantry
Losses Italian 39 Infanty plus 9 Support Weapons and crews
Result wise I have declared this as a draw, 2pts each , yes the British took the Fort but let's be honest there isn't any other possible outcome, the 36 Infantry losses for the Allies was by far the highest of the Campaign so far (they lost two in one game !)
So 5 games in the British lead 13 points to 7. Game 6, due early 2019, will be a bit of a change as we see a clash of forces in the open as they manoeuvre around the forts.
Monday, April 1, 2019
The Seventh Link: Summary And Rating
| The game manual featured some fairly modest hand-drawn art. |
The Seventh Link
Canada
Oblique Triad (developer and publisher)
Released 1989 for Tandy Color Computer 3Date Started: 16 December 2018
Date Ended: 16 March 2019
Total Hours: 22
Difficulty: Medium-Hard (3.5/5)
Final Rating: (to come later)
Ranking at Time of Posting: (to come later)Summary:
Inspired graphically and thematically by the Ultima series, The Seventh Link is probably the most extensive and full-featured RPG for the TRS-80 Color Computer. A single starting character ultimately enlists a group of allies of different races and classes on a quest to save their planet from a black hole at its core, about to break its containment. Solving the quest will take the party through dozens of towns across multiple planets and through multiple large, multi-leveled dungeons. Although the game gets off to a slow, grindy start, character development is rewarding and the tactical combat system (drawn from Ultima III) is the most advanced seen on this platform. The problem is that the game's content is not up to its size, and not enough interesting stuff happens while exploring the enormous world.
****
I never like giving up on games, and I particularly don't like when I know the author is reading (I'm frankly not sure it's ever happened before). But in several months of trying, I simply haven't been able to make any decent progress in The Seventh Link. That doesn't necessarily mean I don't like it. If I was a Tandy Color Computer 3 owner, I'm sure I'd prize the game and play to the very end. The problem is that as a blogger, I have to be able to justify my playing time with material. If I spend four hours in a dungeon and all I can say is I killed a bunch of enemies (showing the same combat screens I've shown before) and gathered some gold, it's hard to countenance that time.
In some ways, The Seventh Link is the quintessential 1980s RPG. It offers a framing story with more detail than appears in the game itself, sticks the player in a large world that the player has to map if he's to make any progress, and features a lot of combat. In mechanics, it's as good as any of the early Wizardries or Ultimas.
Unfortunately, Link was the last game I encountered before leaving the 1980s, and I'd just spent a decade mapping featureless dungeon corridors. It's not its fault that it's last; that's just the way it happened. And by the time I got to Link, I just couldn't do it anymore. I couldn't--I can't--play a game that's just a few dozen 20 x 20 dungeon levels full of combats. The Bard's Tale and its derivatives drained that battery.
This is the 90s, and gamers are demanding more interesting content in their game worlds. We want NPCs, special encounters, puzzles, and other features in those dungeons, at regular intervals. We've decimated forests in our consumption of graph paper; we're ready for automaps. Ones that don't require us to find a spell first.
Despite investing a fair number of hours into the game, I really didn't accomplish much. I explored the surface of Elira, visited each of its towns to assemble a party, and mapped 4 of 13 levels of one dungeon. There were at least 9 more dungeon entrances on Elira alone, some of which would have taken me to teleporters to three other planets and their own towns and dungeons. I would have found a final party member, a female ranger named Starwind, on the planet Dulfin. Others dungeons would have led me to power packs and the places where I needed to install them to save the planet. I still don't know where I was to find the other spells. From hints in an old disk magazine, I learned that the maximum character level is 25 (my main character reached 8) and that one of the planets has a store where you can buy potions that increase attributes, serving in the role of Ambrosia from Ultima III.
My GIMLET is naturally based on an incomplete picture of the game:
That gives is a final score of 32, which is hardly awful for the era. It's actually the highest score that I've given to the platform. The only things that stop me from finishing it are the number of hours it will take and the number of other games on my list.
The Georgetown, Ontario-based Oblique Triad was a mail-order developer and publisher, co-founded by Jeff Noyle and Dave Triggerson. The name referred to the decorative bars on the top of a Color Computer. Mr. Noyle used to host a page (available now only on the Internet Archive) with links to their games, which included a pair of graphical adventures called Caladuril: Flame of Light (1987) and Caladuril 2: Weatherstone's End (1988); a strategy game called Overlord (1990); an arcade game called Those Darn Marbles! (1990); and a sound recording and editing package called Studio Works.
With the Color Computer in serious decline by 1990, Oblique Triad shifted its focus to specializing in sound programming, and both Noyle and Triggerson have associated credits on Wizardry VI: Bane of the Cosmic Forge (1990) and Wizardry: Crusaders of the Dark Savant (1992). I haven't been able to trace Triggerson from there, but Noyle got a job at Microsoft in 1995 working on Direct3D, DirectX, and DirectDraw and remains (at least according to his LinkedIn profile) there today. He also has a voice credit for a Skyrim mod called Enderal: The Shards of Order (2016).
Mr. Noyle was kind enough to not only comment on one of my entries, but to take the time to create overworld maps to speed things along. I'm sorry that it wasn't quite enough, but every game that I abandon stands a chance of coming back when circumstances are different, and I'll consider trying this one again when I feel like I'm making better progress through the 1990s.
In some ways, The Seventh Link is the quintessential 1980s RPG. It offers a framing story with more detail than appears in the game itself, sticks the player in a large world that the player has to map if he's to make any progress, and features a lot of combat. In mechanics, it's as good as any of the early Wizardries or Ultimas.
Unfortunately, Link was the last game I encountered before leaving the 1980s, and I'd just spent a decade mapping featureless dungeon corridors. It's not its fault that it's last; that's just the way it happened. And by the time I got to Link, I just couldn't do it anymore. I couldn't--I can't--play a game that's just a few dozen 20 x 20 dungeon levels full of combats. The Bard's Tale and its derivatives drained that battery.
| I never figured out anything to do with the pillars. |
This is the 90s, and gamers are demanding more interesting content in their game worlds. We want NPCs, special encounters, puzzles, and other features in those dungeons, at regular intervals. We've decimated forests in our consumption of graph paper; we're ready for automaps. Ones that don't require us to find a spell first.
Despite investing a fair number of hours into the game, I really didn't accomplish much. I explored the surface of Elira, visited each of its towns to assemble a party, and mapped 4 of 13 levels of one dungeon. There were at least 9 more dungeon entrances on Elira alone, some of which would have taken me to teleporters to three other planets and their own towns and dungeons. I would have found a final party member, a female ranger named Starwind, on the planet Dulfin. Others dungeons would have led me to power packs and the places where I needed to install them to save the planet. I still don't know where I was to find the other spells. From hints in an old disk magazine, I learned that the maximum character level is 25 (my main character reached 8) and that one of the planets has a store where you can buy potions that increase attributes, serving in the role of Ambrosia from Ultima III.
| One of the few lines from an NPC. Alas, I will probably never explore Selenia. |
My GIMLET is naturally based on an incomplete picture of the game:
- 4 points for the game world. The sci-fi origin story is fairly original, and well-told in epistolatory fashion, although it fails to explain a number of aspects of the world (e.g., why are there settlements on other planets). While the player's role is somewhat clear, it's less clear where he came from, how he got started on this path, and whether he understands his role.
- 3 points for character creation and development. The selection of races and classes is familiar but not entirely derivative. There's nothing special about character creation or the development and leveling process, but they're reasonably rewarding. I don't know if the level cap would have caused any issues or if you finish the game well before reaching it.
- 3 points for NPC interaction. The game has a better system than it uses. You learn a few things from NPCs, but there are hardly any NPCs that say anything to you. Expanding that number would have resulted in a richer, more engaging world. I do like the Ultima IV approach to assembling your party by finding members in the towns.
- 2 points for encounters and foes. The monsters are mostly derivative of other games (though I like the explanations for their names here: the ship that populated the planet had Tolkien fans on it), and I didn't really experience other types of encounters.
- 4 points for magic and combat. The tactical combat screen is about as good as Ultima III, but with fewer spells.
| On Level 3 of the dungeon, I met an enemy called "Floating Stars." |
- 3 points for equipment. You can get melee weapons, missile weapons, armor, and adventuring equipment like torches and keys. Various sites hint at more advanced items like rods and gems of seeing. The selection of stuff is a little paltry in the traditional Ultima style.
- 5 points for the economy. It lacks a certain complexity, but money is certainly valuable. You almost never have enough keys, for one thing. Healing, torches, equipment, and leveling up consume gold fast, and it sounds like the shop on Dulfan would have served as an endless money sink for any extra you could accumulate.
- 2 points for a main quest with no side-quests or quest options.
- 4 points for graphics, sound, and interface. Almost all of that is for the interface. It adopts the Ultima standard of one key per action, which ought to have been mandatory as far as I'm concerned. Graphics are functional but sound sparse.
| I never quite got used to the perspective. That lava square is only one square in front of me. |
- 2 points for gameplay. It gets a bit for nonlinearity and a bit more for the moderate-to-challenging difficulty. But it's not very replayable and it's way, way, way, way too big and too long.
That gives is a final score of 32, which is hardly awful for the era. It's actually the highest score that I've given to the platform. The only things that stop me from finishing it are the number of hours it will take and the number of other games on my list.
The Georgetown, Ontario-based Oblique Triad was a mail-order developer and publisher, co-founded by Jeff Noyle and Dave Triggerson. The name referred to the decorative bars on the top of a Color Computer. Mr. Noyle used to host a page (available now only on the Internet Archive) with links to their games, which included a pair of graphical adventures called Caladuril: Flame of Light (1987) and Caladuril 2: Weatherstone's End (1988); a strategy game called Overlord (1990); an arcade game called Those Darn Marbles! (1990); and a sound recording and editing package called Studio Works.
![]() |
| Caladuril, the company's first game, is a decent-looking graphical adventure. |
With the Color Computer in serious decline by 1990, Oblique Triad shifted its focus to specializing in sound programming, and both Noyle and Triggerson have associated credits on Wizardry VI: Bane of the Cosmic Forge (1990) and Wizardry: Crusaders of the Dark Savant (1992). I haven't been able to trace Triggerson from there, but Noyle got a job at Microsoft in 1995 working on Direct3D, DirectX, and DirectDraw and remains (at least according to his LinkedIn profile) there today. He also has a voice credit for a Skyrim mod called Enderal: The Shards of Order (2016).
Mr. Noyle was kind enough to not only comment on one of my entries, but to take the time to create overworld maps to speed things along. I'm sorry that it wasn't quite enough, but every game that I abandon stands a chance of coming back when circumstances are different, and I'll consider trying this one again when I feel like I'm making better progress through the 1990s.
Subscribe to:
Posts (Atom)

























