Wednesday, September 23, 2020

Tech Book Face Off: Effective Python Vs. Data Science From Scratch

I must confess, I've used Python for quite some time without really learning most of the language. It's my go-to language for modeling embedded systems problems and doing data analysis, but I've picked up the language mostly through googling what I need and reading the abbreviated introductions of Python data science books. It was time to remedy that situation with the first book in this face-off: Effective Python: 59 Specific Ways to Write Better Python by Brett Slatkin. I didn't want a straight learn-a-programming-language book for this exercise because I already knew the basics and just wanted more depth. For the second book, I wanted to explore how machine learning libraries are actually implemented, so I picked up Data Science from Scratch: First Principles with Python by Joel Grus. These books don't seem directly related other than that they both use Python, but they are both books that look into how to use Python to write programs in an idiomatic way. Effective Python focuses more on the idiomatic part, and Data Science from Scratch focuses more on the writing programs part.

Effective Python front coverVS.Data Science from Scratch front cover

Effective Python

I thought I had learned a decent amount of Python already, but this book shows that Python is much more than list comprehensions and remembering self everywhere inside classes. My prior knowledge on the subjects in the first couple chapters was fifty-fifty at best, and it went down from there. Slatkin packed this book with useful information and advice on how to use Python to its fullest potential, and it is worthwhile for anyone with only basic knowledge of the language to read through it.

The book is split into eight chapters with the title's 59 Python tips grouped into logical topics. The first chapter covers the basic syntax and library functions that anyone who has used the language for more than a few weeks will know, but the advice on how to best use these building blocks is where the book is most helpful. Things like avoiding using start, end, and stride all at once in slices or using enumerate instead of range are good recommendations that will make your Python code much cleaner and more understandable.

Sometimes the advice gets a bit far-fetched, though. For example when recommending to spell out the process of setting default function arguments, Slatkin proposed this method:

def get_first_int(values, key, default=0):
    found = values.get(key, [''])
    if found[0]:
        found = int(found[0])
    else:
        found = default
    return found
Over this possibility using the or operator short-circuit behavior:
def get_first_int(values, key, default=0):
    found = values.get(key, [''])[0]
    return int(found or default)
He claimed that the first was more understandable, but I just found it more verbose. I actually prefer the second version. This example was the exception, though. I agreed and was impressed with nearly all of the rest of his advice.

The second chapter covered all things functions, including how to write generators and enforce keyword-only arguments. The next chapter, logically, moved into classes and inheritance, followed by metaclasses and attributes in the fourth chapter. What I liked about the items in these chapters was that Slatkin assumes the reader already knows the basic syntax so he spends his time describing how to use the more advanced features of Python most effectively. His advice is clear and direct so it's easy to follow and put to use.

Next up is chapter 5 on concurrency and parallelism. This chapter was great for understanding when to use threads, processes, and the other concurrency features of Python. It turns out that threads and processes have unique behavior (beyond processes just being heavier weight threads) because of the global interpreter lock (GIL):
The GIL has an important negative side effect. With programs written in languages like C++ or Java, having multiple threads of execution means your program could utilize multiple CPU cores at the same time. Although Python supports multiple threads of execution, the GIL causes only one of them to make forward progress at a time. This means that when you reach for threads to do parallel computation and speed up your Python programs, you will be sorely disappointed.
If you want to get true parallelism out of Python, you have to use processes or futures. Good to know. Even though this chapter was fairly short, it was full of useful advice like this, and it was possibly the most interesting part of the book.

The next chapter covered built-in modules, and specifically how to use some of the more complex parts of the standard library, like how to define decorators with functools.wraps, how to make some sense of datetime and time zones, and how to get precision right with decimal. Maybe these aren't the most interesting of topics, but they're necessary to get right.

Chapter 7 covers how to structure and document Python modules properly when you're collaborating with the rest of the community. These things probably aren't useful to everyone, but for those programmers working on open source libraries it's helpful to adhere to common conventions. The last chapter wraps up with advice for developing, debugging, and testing production level code. Since Python is a dynamic language with no static type checking, it's imperative to test any code you write. Slatkin relates a story about how one programmer he knew swore off ever using Python again because of a SyntaxError exception that was raised in a running production program, and he had this to say about it:
But I have to wonder, why wasn't the code tested before the program was deployed to production? Type safety isn't everything. You should always test your code, regardless of what language it's written in. However, I'll admit that the big difference between Python and many other languages is that the only way to have any confidence in a Python program is by writing tests. There is no veil of static type checking to make you feel safe.
I would have to agree. Every program needs to be tested because syntax errors should definitely be caught before releasing to production, and type errors are a small subset of all runtime errors that can occur in a program. If I was depending on the compiler to catch all of the bugs in my programs, I would have a heckuva lot more bugs causing problems in production. Not having a compiler to catch certain classes of errors shouldn't be a reason to give up the big productivity benefits of working in a dynamic language like Python.

I thoroughly enjoyed learning how to write better Python programs through the collection of pro tips in this book. Each tip was focused, relevant, and clear, and they all add up to a great advanced level book on Python. Even better, the next time I need to remember how to do concurrency or parallelism or how to write a proper function with keyword arguments, I'll know exactly where to look. If you want to learn how to write Python code the Pythonic way, I'd highly recommend reading through this book.

Data Science from Scratch

I didn't expect to enjoy this book quite as much as I did. I went into it expecting to learn about how to implement the fundamental tools of the trade for data science, and that was indeed what I got out of the book. But I also got a lighthearted, entertaining, and surprisingly easy-to-read tour of the basics of machine learning using Python. Joel Grus has a matter-of-fact writing style and a dry wit that I immediately took to and thoroughly enjoyed. These qualities made a potentially complex and confusing topic much easier to understand, and humorous to boot, like having an excellent tour guide in a museum that can explain medieval culture in detail while cracking jokes about how toilet paper wasn't invented until the 1850s.

Of course, like so many programming books, this book starts off with a primer on the Python language. I skipped this chapter and the next on drawing graphs, since I've had just about enough of language primers by now, especially for languages that I kind of already know. The real "from scratch" parts of the book start with chapter 4 on linear algebra, where Grus establishes the basic functions necessary for doing computations on vectors and matrices. The functions and classes shown throughout the book are well worth typing out in your own Python notebook or project folder and running through an interpreter, since they are constantly being used to build up tooling in later chapters from the more fundamental tools developed in earlier chapters. The progression of development from this chapter on linear algebra all the way to the end was excellent, and it flowed smoothly and logically over the course of the book.

The next few chapters were on statistics, probability, and their use with hypothesis testing and inference. Sometimes Grus glossed over important points here, like when explaining standard deviations he failed to mention that this metric only applies to (or at least applies best to) normal distributions. Distributions that deviate too much from the normal curve will not have meaningful standard deviations. I'm willing to cut him some slack, though, because he is covering things quickly and makes it clear that his goal is to show roughly what all of this stuff looks like in simple Python code, not to make everything rigorous and perfect. For instance, here's his gentle reminder on method in the probability chapter:
One could, were one so inclined, get really deep into the philosophy of what probability theory means. (This is best done over beers.) We won't be doing that.
He finishes up the introductory groundwork with a chapter on gradient descent, which is used extensively in the later machine learning algorithms. Then there are a couple chapters on gathering, cleaning, and munging data. He has some opinions about some API authors choice of data format:
Sometimes an API provider hates you and only provides responses in XML.
And he has some good expectation setting for the beginner data scientist:
After you've identified the questions you're trying to answer and have gotten your hands on some data, you might be tempted to dive in and immediately start building models and getting answers. But you should resist this urge. Your first step should be to explore your data.
Data is never exactly in the form that you need to do what you want to do with it, so while the gathering and the munging is tedious, it's a necessary skill that separates the great data scientist from the merely mediocre. Once we're done learning how to whip our data into shape, it's off to the races, which is great because we're now halfway through this book.

The chapters on machine learning models, starting with chapter 12, are excellent. While Grus does not go into intricate detail on how to make the fastest, most efficient MLMs (machine learning models, not multi-level marketing), that is not the point. His objective is to show as clearly as possible what each of these algorithms looks like and that it is possible to understand how they work when shown in their essence. The models include k-nearest neighbors, naive bayes, linear regression, multiple regression, logistic regression, decision trees, neural networks, and clustering. Each of these models is actually conceptually simple, and the models can be described in dozens of lines of code or less. These implementations may be doggedly slow for large data sets, but they're great for understanding the underlying ideas of each algorithm.

Threaded through each of these chapters are examples of how to use each of the statistical and machine learning tools that is being developed. These examples are presented within the context of the tasks given to a new data scientist who is an employee of a budding social media startup for…well…data scientists. I just have to say that it is truly amazing how many VPs a young startup can support, and I feel awfully sorry for this stalwart data scientist fulfilling all of their requests. This silliness definitely keeps the book moving along.

The next few chapters delve a bit deeper into some interesting problems in data science: natural language processing, network analysis (or graph algorithms), and recommender systems. These chapters were just as great as the others, and by now we've built up our data science tooling pretty well from the original basics of linear algebra and statistics. The one thing we haven't really talked about, yet, is databases. That's the topic of the 23rd chapter, where we implement some of the basic operations of SQL in Python in the most naive way possible. Once again it's surprising to see how little code is needed to implement things like SELECT or INNER JOIN as long as we don't give a flying hoot about performance.

Grus wraps things up with an explanation of the great and all-powerfull MapReduce, and shows the basics of how it would be implemented with mapper and reducer functions and the plumbing to string it together. He does not get into how to distribute this implementation to a compute cluster, but that's the topic of other more complicated books. This one's done from scratch so like everything else, it's just the basics. That was all fine with me because the basics are really important, and knowing the basics well can lead you to a much deeper understanding of the more complex concepts much faster than if you were to try to dive into the deep end without knowing the basic strokes. This book provides that foundation, and it does it with flair. I highly recommend giving it a read.


Both Effective Python and Data Science from Scratch were excellent books, and together they could give a programmer a solid foundation in Python and data science as long as they already have some experience in the language. With that being said, Data Science from Scratch will not provide the knowledge on how to use the powerful data analysis and machine learning libraries like numpy, pandas, scikit-learn, and tensorflow. For that, you'll have to look elsewhere, but the advanced, idiomatic Python and fundamental data science principles are well covered between these two books.

Tuesday, September 22, 2020

Welcome To My Process (Part 5)


New here?  This is where you begin.

Before I forget, the blog has a new heading.  Jon Mollison did the last one based on The Outer Presence, and he created this one for Cha'alt.  Let's give him a round of applause.  Yes, slap those slimy tentacles together in appreciation.  Huzzah!

Ok, everything is taking shape and we're close to the "finished" stage.  Once the writing's done, I'll start handing things off to professionals who can take the rather rough material and make it look awesome.

Specifically, I'll need to find one or more artists.  The cover image is key, but this could do with a couple additional pieces of interior art, as well.

As I mentioned before, the map should be re-imagined by someone who draws maps every day and is head and shoulders beyond my passable skills.  So, find a suitable cartographer.

What else?  Oh yeah, layout.  I don't do my own layout.  That, too, gets farmed out to someone who'll do a great job.  Obviously, if you're creating an adventure for home use, you don't have to do any of this.  And if you're just starting out or really want it to be a DIY project, feel free to go it alone.  That's up to you.

As I reach out to those individuals, I will refine what we already have, embellishing the good, pruning the bad, and turning what's ugly into beauty.  Basically, it only needs a bit of TLC from this point until we're ready for the tedious but necessary writing-it-all-out phase.

Let's work on that random table for those touching the green slime dripping down the curving crimson-brown walls.

  1. It stings a bit, but nothing serious.
  2. You get a nasty chemical burn (take 1d3 points of damage).
  3. Whatever touched the slime and the surrounding flesh pulses with an eldritch green glow (save or die in the next 10 minutes).
  4. You're mutated into a screeching, hot-to-the-touch, green and hairy primordial man-wolf [props if you get that reference].
  5. You get a psionic ability.
  6. The flesh that touched the slime is forever stained with Old One ichor - the next time you're knocked unconscious, a ghastly visitation of horrors beyond time and space take you on a little tour of your own past, present, and future.  Each stop contains gratuitous and unwanted groping by one or more tentacles. 
I might change things later, but that's a good start.  Want my generic advice on creating a random table with various effects?  Here we go...

  • One result where nothing much happens.
  • One result where something bad (minor) happens.
  • One result where something bad (major) happens.
  • One result where something weird happens that could be either beneficial or detrimental.
  • One result that's clearly a benefit.
  • One result that's just plain weird.

Ok, what now... the dimensional toxic waste?

I'm thinking a pile of sludge on the floor that's a melange of strange, constantly changing colors.  Touching it won't do anything, but lengthy handling of the waste will transport one to another dimension... maybe through time, as well.

Now, onto Queen Tresillda's personal magic item!

What about an anklet of youth?  Wearing it makes one look and feel like they're 25 years old.  I may decide to give her another one, something with offensive power... or possibly her sorcerer Xa'algex.

Last but not least, there should be some kind of twist or revelation at the end.  Something to make the adventure pop, taking it to the next level... 

What if the PCs eventually (either before the Queen is killed or just after) learn that Queen Tresillda was right all along?  The priesthood disavowed and banished her because of their idiotic hypocrisy and cowardice, and the Queen's revenge was not only justified but thoroughly righteous.  Perhaps the PCs would even be willing to help her, or finish her task if they killed her off before realizing the truth?

Now, that we have a strong foundation, we write it out, revising little details as we go, making improvements.  That's how adventures get finished.  Before too long, you just have to get in there and get to it.  Remember to feel the flow, ride the snake, and make it awesome!

Good luck,

VS


Sunday, September 13, 2020

Download Naruto Shippuden Ultimate Ninja Storm 4 Road To Boruto For PS4

Download Naruto Shippuden Ultimate Ninja Storm 4 Road to Boruto For PS4

FPKG | CUSA06210 | Update v1.02 | 

  • Release Date: Out Now
  • Genre: Action / Fighting
  • Publisher: BANDAI NAMCO Entertainment Inc.
  • Developer: CyberConnect2 Co., Ltd.









With more than 13 million NARUTO SHIPPUDEN™: Ultimate Ninja® STORM games sold worldwide, this series has established itself among the pinnacle of Anime & Manga adaptations to videogames! NARUTO SHIPPUDEN: Ultimate Ninja STORM 4 Road to Boruto concludes the Ultimate Ninja Storm series and collects all of the DLC content packs for Storm 4 and previously exclusive pre-order bonuses! Not only will players get the Ultimate Ninja Storm 4 game and content packs, they will also get an all new adventure Road to Boruto which contains many new hours of gameplay focusing on the son of Naruto who is part of a whole new generation of ninjas.
• All Ultimate Ninja Storm 4 Content in One Edition – Includes the Ultimate Ninja Storm 4 game, 3 DLC packs from the Season Pass (Gaara's Tale Extra Scenario Pack, Shikamaru's Tale Extra Scenario Pack, and the Sound Four Extra Playable Character's Pack), the all new Road to Boruto DLC, and all the previously exclusive pre-order bonus content
• New Generation Systems – With development made specifically to leverage the power of PlayStation®4. Road to Boruto will take players through an incredible journey of beautifully Anime-rendered fights!
• Huge Character Roster and New Hidden Leaf Village – Additional playable characters including Boruto, Sarada, Mitsuki, and Sasuke (Wandering Shinobi) and a new setting of a New Hidden Leaf Village
• New Collection and Challenge Elements that extends gameplay


Experimenting With Outlines

I posted recently about how I often do one-week projects to learn and experiment. I don't have a strict one-week time limit. Sometimes I'll extend a project or come back to it after I've learned something new.

Ten weeks ago I had a one-week project in which I wanted to draw outlines on an isometric view of a dungeon game. I didn't want to outline each block, which could be implemented easily by editing the sprites. Instead, I wanted to outline the larger units. Here's a comparison:

Outlines around every sprite vs around walls

The second thing I wanted to do was to implement all of this in shaders. My first attempt was to draw a "surface id" to a texture, and then draw black outlines whenever the surface changes.

Draw a black line whenever the surface id changes

There were lots more details to implement, including outlines around billboard sprites, field of view, and lighting of wall blocks beyond the outline.

Dungeon map with outlines

I was pretty happy with that, even though it had some glitches, and I decided that project was finished.

A few weeks later I re-opened this project to explore a different approach. Instead of drawing the lines in a post-process step, I wanted to draw the lines as the sprites were being rendered. I posted some images on Twitter and got a suggestion from @Rezoner, who had made a version where some lines were white and some were black, depending on camera direction. I took that idea and ran with it, making white lines where the player could see the walls.

Dungeon map with lit and unlit outlines

I was pretty happy with this version too. I then merged the code together into one unified demo, with a toggle. Now I think I'm finished. But who knows? Maybe I'll re-open it later.

Take a look at the demo!

Things for me to keep in mind:

  • The one-week self-imposed deadline is just a rough guide. I don't have to follow it strictly.
  • Sharing unfinished work can lead to more ideas for improvement. I should share more things early.
  • Sometimes all I need is a proof of concept. I don't need to make everything work perfectly. If I actually use this in a real project, I can work out those details then.

Friday, September 04, 2020

Buck Rogers: Matrix Cubed: Collapsing Choices

It doesn't make any difference.
         
I pressed ahead and won Matrix Cubed last weekend, because at some point it became clear that if I stopped, I probably wouldn't start again. The game managed to go from seeming like an improvement from its predecessor to "meh" to actively pissing me off. After overcoming the bias I had built against it in the last session, it lost almost all that good will in this one.
     
When I last wrote, the Venusian scientist Leander had agreed to oversee the Matrix Device project. Needing a high-gravity environment, he and the science team departed for a laboratory in the orbit of Jupiter. Meanwhile, my next step was to find a PURGE scientist named Dr. Jerod Malcoln, who had invented an explosive radioactive gas that we need. He was probably at PURGE headquarters on Santa Catalina. SCOTT.DOS, our ship's artificial intelligence, had also identified an electronics scientist on Luna named Dr. Coldor. Buck Rogers gave us a passcode to enter a building in Losangelorg and find an agent named Red Carrin so that he could tell us how to get to Santa Catalina.
      
Buck reminds us of our current tasks.
        
We returned to Losangelorg and poked around the downtown until we found the right building. Carrin was supposed to be in Suite 5403, so we took the elevator to the 54th floor and found his suite. (The game allows you to take the elevator to any floor between 1 and 85, but most of them are just generic hallways with no encounters.) On Carrin's door, we found a note saying that he'd be in the Sparkhouse Cafe on the 85th floor, but this was crossed out and replaced with "Smoking Gun Slots on the 13th floor."
    
The 13th floor had several rooms we could enter and gamble. We tried our luck at "One-card Monte," an unsophisticated game that consists of everyone drawing one card, making a round of bets, and awarding the pot to the person with the highest card. Somehow I won almost $5,000 with my queen of hearts.
       
"War" was too complicated for you fellows, huh?
        
Anyway, the crossed-out part of the note on Carrin's door was a ruse meant to lure unsuspecting victims into an ambush. A panhandler warned us of this after we gave him a few credits. We tripped the ambush anyway because no one ever earned experience for walking away from a fight.
    
After mopping the floor with the robbers, we went to the 85th floor and found Carrin in the cafe. After a brief conversation, he gave us a passcard that would allow us to take his boat from the docks. As we took the elevator back down, I couldn't help but think that in the 25th century, there ought to be some easier way for six people to get to an island twenty-six miles offshore.
      
I'd work for anyone, even the NEO, who could float me to my island dream.
         
We also, of course, pondered what calamity must have occurred to move Santa Catalina closer to the mainland, move it about forty miles north-northwest, enlarge it, and change its shape. 
    
Wilma Deering, Buck Rogers' girlfriend, approached the party as we landed, offering no explanation as to how she got to the island. It's a minor issue, but the phrase "offering no explanation" is going to become a big part of the Matrix Cubed narrative from now on, so we might as well ramp up. She said she'd been scouting the headquarters and had identified two ways inn: smooth talk the receptionist in the lobby and hack the security doors in the maintenance tunnel.
      
One also wonders why a secret island facility belonging to a terrorist organization needs a receptionist.
            
We chose to try the receptionist. Elias, who has the highest skills in persuasion, passed his skill check, and we got in. For a while, we wandered the first floor, listening in on PURGE conversations and meetings. In a conference room, some members were talking about broadcasting a subliminal message using a satellite signal. In another room, a printing press churned out PURGE brochures.
     
Eventually, we came across a PURGE Commander Sooth talking to a subordinate, and they recognized us as infiltrators. An alarm went off, and the game was on. We were attacked by a variety of PURGE forces in most rooms and intersections.
     
The beginning of another PURGE battle.
            
We continued to explore the base, eventually transitioning to a second floor. We rescued Dr. Romney, inventor of the Matrix Device, from a jail cell. We wiped out a squad in a control room and found evidence on a computer that a PURGE team was attacking a Desert Runner radio station near the edge of the desert. In Commander Sooth's office, we found a letter from Sid Refuge, the cyborg, indicating that he'd recovered from his defeat on Venus. A laboratory held evidence that PURGE had been experimenting with subliminal writing. Most important, a logbook described an "anti-personality" virus called Bug Nine created by PURGE engineers.
      
Every time I have to read something in the paper logbook, it feels like a little piece of my soul dies.
           
Finally, we wandered into a room and found Commander Sooth sitting in a chair with a bunch of electrodes stuck to his head. Technicians were transferring his consciousness into a computer, to make him an artificial personality like SCOT.DOS. They finished just as we arrived. After a few more battles, we came across Dr. Malcoln taking orders from Sooth's voice, coming out of a computer. Yet another battle ensued, in which we killed Dr. Malcoln.
     
This would be a good place to mention that battles from this point forward were fought almost entirely with explosives. I'd found enough rocket launchers, plasma-throwers, and grenade launchers during the last session that I didn't have to worry about scrimping. Since enemies all have explosives, too, it would have been irresponsible not to use them.
      
A rocket launcher blasts some technicians.
       
The problem is that you can only use these explosive devices once every two rounds. To operationalize this rule, the game makes the process of equipping the weapon take a full round, and it then un-equips it every time you use it. After combat, you have to remember to re-equip all the items or else you'll waste the first round of your next combat equipping them.
     
Combats soon fell into a predictable rhythm of all my characters blasting the hell out of the enemies in the first round, then mopping up what was left with regular weapons in the second round. In the few major battles in which a substantial number of enemies were left after the first round, I would usually have some characters throw explosive grenades. (You can only use the launcher, which has a much greater range, every two rounds, but you can throw as often as you want.) I still used chaff grenades occasionally, when facing a large number of enemies (particularly robots) with their own explosives, but for the most part the power of the explosive weapons made the game feel a lot less tactical, much like having eight or ten "Fireball" spells memorized ruins things in the Dungeons and Dragons Gold Box titles.
     
Anyway, we fought our way into a computer room, where Theta Sigma used his "Programming" skill to access the Bug Nine virus and send it after the newly-created SOOTH.DOP, killing the artificial intelligence in its first hour. (Given all the body parts in the rooms behind us, I don't know why this felt particularly mean, but it did.) With Sooth dead, we retrieved Dr. Malcon's notes about Efanite, the explosive gas needed for the Matrix Device.
   
Sooth is unfortunately wrong about both things.
        
This episode is the first of another trend that dogged me through the rest of the game: progress gated by skill checks. I don't know for sure that there's no way to finish this mission without someone with sufficient "Programming" skill, but I do know that I couldn't find one. I had to reload several times before Theta Sigma passed the necessary checks. Later in the game, I had even worse problems. Not only do many episodes require checks, many of the thresholds are quite high. Given all the skills that exist in the game, you'd have to have some prior knowledge that some of them were used at all before you would bother investing points into them.
    
The game makes the whole process difficult in a few other ways, too. First, you can only learn one new skill per level-up. So if you started the game with only, say, five skills, the most you'll ever get in Matrix is maybe ten, and you certainly wouldn't have time to build the last few to any reasonable level. Second, you can't choose skills for new characters; the game does that for you. So if you find yourself lacking in "Programming," you can't whip up a new temporary character with a massive focus in "Programming" to save the day. We'll see later moments were this issue got a lot worse.
      
A lot worse.
         
As we left the facility and took the boat back to the mainland, I decided to comprehensively explore the Losangelorg metro area to see if the PURGE raid in the Desert Runners' radio station was actually something we could intercept. I also wanted to see if there were any other encounters on the outdoor map. It turns out that there weren't many, but a slew of random encounters with various  "gennies" probably added one to our final levels by the end of the game.
     
This is feeling a lot like a Fallout game.
       
We did eventually find the radio station, run by a Desert Runner named "Bad Dog." PURGE forces had thrown him out and were using his equipment to broadcast their anti-gennie propaganda. Once again, I have to emphasize that I don't know why we're against this. We just killed a few hundred gennies in the desert right outside the radio station door. Who is pro-giant scorpion? But we still defeated the PURGE forces and yanked their agent off the air.
       
This is feeling even more like a Fallout game.
              
We returned to Salvation, where Buck Rogers thanked us and said he'd send the Efanite files to Leander. That left our only mission to find Dr. Coldor on Luna. The Moon, if I haven't already covered it, is actually not a part of the New Earth Organization, but rather an independent colony with strict isolationist tendencies. To get there, I had to launch my ship, fly one square away from Earth, fly one square back to Earth, and choose to land on either Tycho or Copernicus. (Incidentally, half the time that you're on approach to a planet like this, the solar system suddenly rotates out from under you, putting your destination an extra few squares in front of you or even behind you.) It turns out that Tycho is just a menu town--we stopped in just long enough to get thrown out of a bar--and Copernicus is the actual destination.
      
I can only land at one place on Earth, which is four times as large, but the moon is parceled out.
            
As we arrived at Copernicus, we received a notice that our ship was impounded and that we should report to a Lt. Jenner. As we made our way to the police station, we saw Dr. Coldor getting into a jetcar with the "Tsai Weaponry" logo. We tried to approach her, but she said that "NEO has her services" and told us to get lost before we could explain that we were NEO and that whoever had her services definitely was not.
 
At the police station, Lt. Jenner explained that Luna is filthy with corruption, and that the police chief, Senator Koi, and the CEO of Tsai Weaponry are involved in a major conspiracy. He promised that if we could collect enough evidence against them, he'd help us collect Dr. Coldor and get our ship back. He gave us fake police badges to assist.
      
That was easy.
         
The rest of the map was a bit tedious, as we ran around from building to building--the offices and houses of the three major players--collecting intelligence. The process was annoyingly linear. For instance, the first time we visited Chief McKay's house, we found nothing. But later, we found a computer entry that mentioned a logbook. When we returned and looked again, we found the logbook.
    
So we had to visit the various places in the right order, then call the suspects to tell them we had dirt on them so Jenner could raid the subsequent meeting. I got sick of the whole thing in the middle of it and tried to just force my way into Tsai Weaponry, but automated laser cannons kept doing scripted damage to the party as we tried to explore the complex. These cannons weren't there when we did things "honestly."
     
Way to facilitate role-playing, guys.
        
So we followed the game's chosen path and got the police chief, senator, and CEO arrested for conspiracy, and Lt. Jenner gave us a pass to get into Tsai. We explored their headquarters (another 16 x 16 map) long enough to find Dr. Coldor consorting with Sid Refuge from PURGE. What is it with this organization? No one had heard of them two weeks ago, and now they have a headquarters on an island and have tendrils into every faction in the solar system.
    
Coldor learned at this point that she had been working for a bunch of lunatics. Apparently, Tsai had been creating some kind of chemical (confusingly called a "mutagen") capable of killing genetically-engineered creatures. Refuge had a little speech about it:
       
NEO's propaganda has served PURGE well. I recruited this scientist on your reputation. The pap of genetic mongrels living in harmony with the pure race fools so many people. They are blind to the inevitability of conflict. Either the pure strain will survive unsullied, or humanity will revert to packs of mindless animals!
       
I'm more confused than ever about PURGE's philosophy. Does Refuge think that humans are mating with gennies? Even if that were biologically possible, I haven't seen any evidence of it. Anyway, Refuge took off with Coldor, forcing us to follow them through the base. At various points, we were menaced by "plant gennies" that were just the same graphic as Bits o' Moander from Pools of Darkness.
       
They arguably make more sense in this context.
            
It became clear that PURGE or Tsai was about to launch a rocket containing the mutagen, somehow causing it to spread throughout the galaxy. We burst into the launch pad just as it was preparing to launch. While the party fought a big group with Refuge and his commandos . . .
     
This, like all situations, seems like a good situation for a rocket launcher.
      
. . . Dr. Coldor sneaked aboard the rocket and overrode the automatic controls. As we watched Refuge's cyborg corpse somehow escape yet again, we receive word from SCOT.DOS that Coldor had joined NEO and was taking the equipment and mutagen to the Matrix Device project at Jupiter. The police arrived to sweep everything up, and Luna officials thanked us while simultaneously inviting us never to visit the moon again.
    
As we blasted off from Luna, an explosion rocked the Maelstrom Rider, and radiation levels began to increase. The computer reported a 98% chance that the ship would soon explode. We couldn't seem to do anything to fix it, and the game clearly wanted us to escape in a pod, so we reluctantly took that option.
    
We get the hint.
         
As the pod sailed away, the party was surprised to see that the ship didn't explode, and instead flew "gracefully out of sight." Meanwhile, our pod was picked up by a ship called Rogue, captained by someone named Killer Kane. I was obviously supposed to recognize him from the game's setting, so I took some time to read up on him, which left me more confused than ever, because in no version does he seem to be depicted as the roguish "frenemy" of Rogers as he does here.
         
Well, I'm surely not going to call you "Killer."
         
Kane proposed an exchange: He would set us free if we agreed to infiltrate and help destroy a RAM cruiser called Deimos, which was transporting a "high-level Mercurian official" to Mars. This reminded me that well into the game, "De Sade" hasn't made a return appearance, suggesting all the intrigue on Mercury at the beginning of the game had nothing to do with the plot. This would turn out to be true; I never heard from De Sade again. Even the whole issue with Mercurian forces invading Venus turned out to be a mystery.
     
The game offered me the option of accepting Kane's offer, and I was feeling ornery by this point, so I said no. Matrix is the most linear of the Gold Box series, much more so than Countdown, which let you visit the planets and asteroids in almost any order. Here, we've had the choice of which of two missions to do first and second, but that's about it. This extends to not even providing alternate ways to solve puzzles that require skill checks.
    
In response, Kane shoved us back into our escape pod and said, "Good luck drifting." Well, we hadn't been drifting long before we were picked up by . . . the RAM cruiser Deimos. On which Killer Kane was somehow also taken prisoner in the hour since we saw him last. So that turned out to be another Morton's Fork.
      
A whole hour! Noooooooooo.
        
In the Deimos's brig, we were contacted by a RAM agent named "Oiler" who let us out of the cell and gave us explosives to plant in the weapons control room. Deimos was five levels, all of them relatively small, but we had to backtrack a lot, finding a keycard in on one floor that would let us through a door on another floor, and so forth. If there were battles, I didn't bother to take any screen shots; my recollection is that RAM crew seemed easily fooled by our lack of outfits or any identifying insignia.
     
We placed the explosive charges in the right place, but I don't think they were ever set off. As we left the room, we found ourselves surrounded by RAM bots who offered us a choice of surrendering or not. Either answer (I reloaded to be sure) led to them blasting us with sonic stunners and us waking up in a prison on Mars, so that was another choice they need not have offered.
     
The sun being too bright is a good sign we're not actually on Mars.
        
We awoke in a prison cell. For the second time in the game--but not the last--we were stripped of all equipment. The cell was made to resemble the Martian surface and extended infinitely in all directions.

Buck Rogers soon appeared. He said that when our ship returned to Salvation empty, he blasted off in search of us, found our pod, but was knocked out by gas and similarly awoke in this cell. This story makes no sense, as Rogers is a commander and shouldn't be soloing on rescue missions, but nevertheless, there we were. Rogers had his .45 automatic because it's so old that our RAM captors hadn't even recognized it as a weapon. Moments after Rogers joined us, so did Killer Kane, whose appearance makes even less sense.
     
And we had no idea, when we rejected you, that we'd end up in the same party. Though in retrospect, maybe we should have.
       
After finding nothing in our lateral explorations, Rogers suggested that we explore vertically. We dug down and hit a metal floor. Rogers then suggested we try to reach the "ceiling" via a human pyramid, with him and Kane as the base and the most athletic person at the top. Unfortunately, my "most athletic" person lacked the "Climb" skill to make it to the top, plus the "Acrobatics" skill to make it from the top of the pyramid through a hatch on the ceiling.
     
Spoiler: he failed.
      
In multiple re-tries, he kept falling and taking damage, and because the game offers no way to heal characters except at the end of combats, I eventually had to stop trying and use my second-most-agile character instead. And so on from him to the third. After about 15 minutes of this, the game took pity on me and let me succeed. I know it did because the character who finally got through the hatch had no "Climb" or "Acrobatics" skill at all. Incidentally, Rogers and Kane both have decent skills at both, but they insisted they had to form the base of the pyramid.
     
It turned out that my problems were just beginning. On the top side of the hatch was a gennie guard dog with about 76 hit points, and all I had to fight him were my puny fists. Even Austin, who had the highest number of hit points and the best chance of succeeding at hand-to-hand combat, was unable to kill the creature. Defeat means the character gets kicked back down the hole, the pyramid collapses, and we have to start over again.
    
In case it's not clear, this is when the "actively pissing me off" stage began.
      
The obvious solution, which somehow didn't occur to me, was to take Rogers' handgun. But I later consulted several walkthroughs and videos, and none of them offered it as a solution, so I suspect Rogers won't give up the weapon. If I'm wrong and someone has done this, please tell me. Meanwhile, all the walkthroughs and videos offered the same solution that ultimately worked for me: lower the difficulty level so that the enemy hit points get lowered. I hated, hated, hated doing this, but the damned dog was simply mathematically unbeatable without it. I thought maybe a character who specialized in unarmed combat could do it, but later I checked and found that's not even an option.
      
With the difficultly lowered, Austin was able to defeat the creature and then drop a cable to haul everyone up. Our problems didn't end there, though. The two-level prison that ensued offered combats with more dogs, robots, and other prisoners, all of which we had to fight bare-handed except for Rogers' gun, which we no longer had after Rogers suggested that we "split up." Kane fled shortly after we got out of the cell.
    
Since you have the only weapon, that seems like a really bad idea.
        
This was the only area of the game that I mapped myself, and only long enough to test various paths and determine that they led to dead-ends, or unwinnable battles. We ultimately found our way to a storage room and our equipment. Shortly after, we freed a "Stormrider" named Natbakka. He had been an ambassador to the Amaltheans, but they sold him to RAM. This story sent me back to the logbook to remind myself about the backstory. Stormriders are supposedly genetically-modified humans (though they don't look much like it) who live in high-pressure cities above the moons of Jupiter. Amaltheans are humans living on one of Jupiter's moons; they created the Stormriders in the first place but are now hostile to them.
       
I shudder to think what my brain is overwriting in order to learn this lore.
      
We found a computer console that allowed us to reach SCOT.DOS, who told us that he had found evidence of a spy at the NEO. Then some virus started attacking him, and we had to try to stop it through some confusing menu process that I didn't understand, and for which Theta Sigma kept failing his "Programming" checks anyway. At the end of the process, SCOT.DOS was dead, which I'm not sure wasn't scripted. If so, they should have just made it seem like it was scripted instead of making it seem like a character failed a skill check.
        
To be honest, I only ever vaguely understood who he was.
       
Buck Rogers rejoined us; we fought one final battle against RAM forces; and a NEO ship dropped in to rescue us. Soon after, we were finally back at Salvation. I felt like I had been through so much that my entire team should be able to level up, but only one person could.

That gets us far enough that I should be able to cover the rest of the game in one entry. By this time, the false choices and linearity were getting annoying, the skill checks were getting infuriating, and the combat was getting boring. You can see why I just pushed through to the end.
     
Time so far: 21 hours