Ruby battleship tournament

So I’ve been diligently working away on an entry for the upcoming battleship sparring tournament on Dec 1. It has been extremely fun. I think I’m almost done (just like every other project, eh?), and I thought I would share some insight into what I have learned, mainly about targeting strategies.

So the way I modeled my player (Joshua, son of Nun; see the book of Joshua for more details :-) was that he could choose a targeting strategy at run-time. That is to say, he would not necessarily have the same strategy for each battle. I have made 3 strategies: a completely random strategy (or as random as Kernel#rand can get), a diagonal strategy (targets successive diagonal spaces), and a knight strategy (like the chess piece, always tries to move two spaces in one direction and one space in another). Each strategy is responsible for choosing which targets to fire upon during the game. The strategies are NOT in charge of figuring out what to do when the successfully hit a ship. I’ll come back to that later.

So, that’s great (one might say), but which one is better? Great question! I wondered the same thing, so I went looking for the answer. I tried using the Limelight app that the game was to run against, but since it has a UI and was really meant to give pretty visual feedback instead of fast, data producing feedback, it was not the proper solution. I then built a little simulator that acts just like the Limelight app, but runs the code as fast as it can, as many times as I want, and spits out intelligible data that can be analyzed to see which strategy is the best.

Onto the data. I ran the script 4500 times, with strategies being chosen at random for each player, and here is what I got

player_one: 2286 wins (Random => 760, Diagonal => 760, Knight => 766)
player_two: 2214 wins (Random => 766, Diagonal => 771, Knight => 677)
57.806 moves per game

Looks like a dead heat for any strategy against any other. I did some very slight number crunching (even using the STDEV function in NeoOffice!) and my suspicions were confirmed. None of the strategies had any real advantage over any other.

So did I just do an awesome job programming those things, or what?! I decided to choose “what” and keep searching for an answer. Inside of my game simulator I hacked player_two’s strategy to not react when it hit a ship, and just keep spitting out the targets it had already chosen. Here is the result from running that scenario 100 times (random vs random):

player_one: 98 wins (Random => 98, Diagonal => 0, Knight => 0)
player_two: 2 wins (Random => 2, Diagonal => 0, Knight => 0)
65.25 moves per game

Aha! The difference is in how the strategy reacts to successful hits. player_one still reacted properly when he found a successful target (ie: hunting it down until it was destroyed), whereas player_two just blindly kept hitting targets. So it seems that success is not based on how well you place your targets, but how you react when one of your shots hits a ship. Fun stuff!

Posted by Steve on Saturday, November 15, 2008