In a further exercise of my original Battleship test framework, I have enhanced and refactored the SmartRandom
class from an earlier question and have updated GitHub project to incorporate most of the suggestions I received for the previous questions. This is the second non-trivial bombing strategy class for the May 2015 Community Challenge.
Like the previous SmartRandom
class, the SmarterRandom
class guesses randomly until it gets a hit. It then checks for possible ship positions based on that hit, and caches those guesses. Unlike the previous version, though, it does not dump the cached guesses even if a ship is sunk and only guesses points that have at least one space between them. That is, on the first row, the hits would look like this: x.x.x.x.x.
.
SmarterRandom.h
#ifndef SMARTERRANDOM_H
#define SMARTERRANDOM_H
#include "Ocean.h"
#include "Bomber.h"
class SmarterRandom : public Bomber
{
public:
const char *id() const { return "SmarterRandom"; }
std::ostream& printTo(std::ostream &out) {
return out << "\nTracking:\n" << tracking << std::endl;
}
private:
unsigned guess();
void result(unsigned location, char bombresult);
void reset() {next.clear(); tracking.reset(); }
unsigned randLocation();
bool isBadLocation(unsigned loc) const {
unsigned col = loc % Ocean::dim;
unsigned row = loc / Ocean::dim;
if (col & 1) {
return (row & 1);
}
return !(row & 1);
}
private:
// vector holding the next guesses
std::vector<unsigned> next;
Ocean tracking;
};
#endif // SMARTERRANDOM_H
SmarterRandom.cpp
#include <iostream>
#include "SmarterRandom.h"
/*
* The strategy here is to bomb mostly randomly, but
* to only randomly select every other point
* and to follow up promptly when a ship is found.
*/
unsigned SmarterRandom::guess() {
unsigned location = randLocation();
// try using our pre-stored guesses first
if (!next.empty()) {
for (location = next.back();
tracking[location] && !next.empty();
location = next.back())
{
next.pop_back();
}
}
if (tracking[location]) {
for (location = randLocation();
tracking[location];
location = randLocation())
{ }
}
return location;
}
void SmarterRandom::result(unsigned location, char bombresult)
{
if (bombresult != Ocean::empty) {
if (bombresult == Ocean::hit) { // generic hit
unsigned shortship = Ocean::shipcount-1;
if (tracking.place(location, shortship, true, true))
next.push_back(location+1);
if (tracking.place(location, shortship, false, true))
next.push_back(location+Ocean::dim);
if (tracking.place(location-1, shortship, true, true))
next.push_back(location-1);
if (tracking.place(location-Ocean::dim, shortship, false, true))
next.push_back(location-Ocean::dim);
} else { // specific ship was just sunk
//next.clear();
}
}
tracking.record(location, bombresult);
}
unsigned SmarterRandom::randLocation()
{
unsigned loc = randSq(gen);
// only allow the location if it's an odd row or odd colum
// but not both
for ( ; isBadLocation(loc); loc=randSq(gen));
return loc;
}
Results
I used two different placement strategies (as explained in the Battleship test framework question) and three different bombardment strategies. The first was a relatively stupid random bombing strategy, the second was posted in the previous question and this one, which performs much better. (The numbers are the number of turns it took to sink all ships.)
There are 2 placers, and 3 bombers
Running 10 iterations
UpperLeft/RandomBomber UpperLeft/SmartRandom UpperLeft/SmarterRandom RandomPlacer/RandomBomber RandomPlacer/SmartRandom RandomPlacer/SmarterRandom
100 87 27 80 53 62
99 60 26 93 54 70
100 47 21 100 67 34
99 39 21 91 61 55
98 65 24 91 68 56
98 97 18 99 64 52
100 56 19 98 75 60
98 96 30 97 45 53
97 70 25 82 58 54
87 99 21 92 42 44
I'm currently working on some code in R to analyze these results, but it's pretty clear that the latest algorithm is the best of the three so far.
Questions
I'm interested in comments on the code, of course, but also in suggestions for improvements to the algorithm. Specifically, should the random search pattern remember when the smallest ship has been sunk and widen the guess grid spacing as a result? If so, how might that best be done?