Sunday, November 29, 2015

Extended Range, at the Ready.

So, back in High School, I played a bunch of the "Star Wars Lightsaber Dueling Pack" by West End Games. It's a two player diceless combat system, where one player plays Luke Skywalker, the other player plays Darth Vader. Both players simultaneously select moves from a movement reference card keyed to their player (Luke selects "Jump/Dodge [8]" , Vader selects "Hurl Objects [24]"). The cards direct you to pages in combat books, which then dispatch based on the pair of actions, and the players end up looking at pictures of what their character would see, along with points (maybe), and instructions for their opponent. In this example, Luke manages to avoid the projectiles, and the combatants face off at extended range for more combat.

(from page 49 of the Darth Vader book. I claim "Fair Use", since it's a small portion of the original work.)

In college, I took a game theory course which touched on the traditional payoff matrix analysis, but spent a lot of time on combinatorial game theory. I guess if Berlekamp is teaching, you're in for a course on nimbers, tiny, spiny and any number (ha) of other esoteric concepts.

As a project in that class, we had to analyze a game. I selected Star Wars Lightsaber Dueling Pack. I'm going to call that Saber Dueling, because the full name sounds dumb. I wrote a C program to generate a payoff matrix for Saber Dueling, but I think I trimmed a few of the details. (For instance, you can dislodge an opponent's weapon, but I don't think my program represented that.) I ran into a few weird scenarios, probably due to oversimplifications. (In the example above,  Vader ends up at extended range as part of the basic logic, but Luke only ends up at extended range based on conditional logic that I probably didn't implement. If you don't get that right, Luke's close to Vader, who's far from Luke. Whoops.)

I had something like a 32x32 array of integers (probably 8-bit integers, because I was stingy back then) and I jammed that into the minimax algorithm that we had been taught on the one day of class we weren't doing the combinatorial Berlekamp stuff. And I came up with a mixed strategy for playing the game. And I forget, I probably got an OK grade on the project.

And yet, it wasn't really satisfying. I had drained out a bunch of the character of the game in the simplifications I had made. I mentioned losing your weapon. It's kind of important to know that you don't have a weapon in order to select good moves. There's a "Retrieve Weapon" action you can take, but it doesn't score you points, so if you're just looking to maximize the points you score on this turn, you'll overlook it, even if the representation is there.

Another simplification that I had made was to skip over the move restrictions and bonuses. Each move leaves you (and potentially your opponent) in a situation with potentially only a subset of moves available. If I knock you down, your next move has to be a jump. If I know that your next move has to be a jump, I don't have to defend against a downswing - I might restore some hit points, or something. Maybe choose an attack to hit you when you're down. Restrictions are important, but my college project skipped all that.

Years later, and this is perhaps not central to any particular narrative thread that I'm spinning, I had a dream where I was at some sort of gaming convention. I forget if this is the same dream that featured "Cremwits", which I might post about at some other time. This was a convention maybe something like GenCon, which I've never been to. Picture a bunch of folding tables with hard to find, rare, possibly out of print games. I was super excited to find (this is still inside the dream) a copy of "Han Solo with Bomb", a compatible book to go along with the Luke and Vader books. This particular bomb would probably be a thermal detonator, or some similar grenade-like explosive device.

The idea of a new character to play against these two characters was pretty exciting to me at the time. I'm still fairly excited about it.

On the left, some files, on the right some debug output.


I spent a chunk of this past weekend revisiting the analysis of Luke vs Vader. I started by scanning in everything. I don't think that's really important, but it feels handy to have digital copies.

I proceeded to create a Google Docs Spreadsheet (ahem, a "Google Spreadsheet" inside "Google Drive") with one page each for moves, results, and the dispatch table for each character. I exported them all as CSVs, and began writing some python scripts to wrangle the CSV information. I've got classes for restrictions, heavily laden with comments saying "TODO - prohibit use of the Force" or "TODO - enable Spin&Strike", as I still haven't got the full logic working for restrictions. I've got classes for characters. I've got a utility function that figures out what moves are available. I've got a test harness that takes two characters, with some (partially working) move restrictions, chooses legal moves, and figures out what resulting pages they end up on.

Which is, maybe, 75% of the way towards my somewhat disappointing Game Theory class project.

I could imagine this turning into a way for me to play a version of the game solo vs a randomized AI.

I've been thinking it might be a little more interesting to allow the AI to learn how to play the game by playing against itself, or against a population of AI players. I've been toying with a bayesian model that would adaptively figure out what the best moves are for each situation. I haven't made specific decisions about how it would be implemented, but the shapes I've considered would probably still have the one-move-lookahead problem I mentioned above - retrieving your weapon is a good idea, and you want to do it, even if it doesn't win you points right now.

The unfun bit that I was hitting yesterday was doing data entry, filling out a big dispatch table. I'm pretty sure that's done (modulo finding and correcting errors). The bit that's baking my bean right now is keeping track of what bits of information apply to which player - Player 1 looks up a move on the "Luke" movement reference, but turns to a page in the "Vader" book, and then, based on Player 2's action, Player 1 turns to a different page in the book, and tells Player 2 what Player 2's restrictions are for the next turn. Simultaneously, also vice versa.

And I haven't yet got to scoring. I've got the data for it. Each move has a score modifier. Did we properly apply those back in High School? I kind of suspect we ignored that. There's a bonus that carries over for one round that doubles the effect of The Force. There's a Force move that heals 3 hit points. There's another result that loses 6 hit points in one go. Doubling up the Force and then pulling off the heal would totally counter that one result. So, I'd like to see some AI that knows the value of a well timed healing move.


I mentioned that having one more character seems kind of exciting. Well, what about several dozen? Turns out, Saber Dueling is an adaptation of the Lost Worlds Combat Picture Books, which I was somewhat aware of in High School, but only started collecting more recently. I've got around a dozen books that I collected around a year ago. Just now, I went to a handful of different online retailers to see what copies they had lying around. I found three different places that would allow me to put titles into shopping carts, and two of them actually let me check out. It seems weird that an online store would have server problems this weekend of all weekends - or, rather, it's weird to me that this weekend, problems like not being able to complete a checkout would go unfixed.

If-when I get my Luke/Vader scripts playing Saber Dueling, then I'll consider expanding it to allow Man With Plate Mail and Broadsword vs Unicorn vs Magician With Dice Bag vs Skeleton with Scimitar. Perhaps Luke can hold his own against the skeleton. I'd be interested to see.

And, even if Luke vs Skeleton isn't a battle we can see, I think I understand the system now well enough to craft Han Solo vs somebody. One bit at a time.