C++ Battleship player AI











up vote
3
down vote

favorite












Found this project online and looking for some guidance. I'm in the process of developing a Battleship AI that will play against different computer difficulties.



The "barebones" part of the project is already completed. I don't need to develop the game board, create a makefile, write a tester, or anything like that. All I'm doing is creating a single AI that will play against the computer.



The playground is a 10x10 board.



There are :





  • 3 ships of length 5


  • 2 ships of length 4


  • 1 ship of length 3


There are four different levels of difficulty (that I'm supposed to beat) :




  • Beginner

  • Normal Player

  • Gambler

  • Learning Gambling


I am unable to see the code for any of these "players". From the names of the difficulties you can guess how they play the game.



So here's the code I developed last night. Everything runs fine and smooth, but I'm only able to defeat the first two difficulties (Beginner and Normal Player). My AI gets absolutely crushed by the later two (I simulated 500 games for each and I maybe won 50 games total).



It would be great is someone could help optimize my code for me. Improving the AI obviously has something to do with the piece placement strategy, but I'm not exactly sure where to go from here. Any help would be greatly appreciated!



ourPlayer.h



#ifndef DUMBPLAYERV2_H          // Double inclusion protection
#define DUMBPLAYERV2_H

using namespace std;

#include "PlayerV2.h"
#include "Message.h"
#include "defines.h"

// DumbPlayer inherits from/extends PlayerV2

class ourPlayer: public PlayerV2 {
public:
ourPlayer( int boardSize );
~ourPlayer();
void newRound();
Message placeShip(int length);
Message getMove();
void update(Message msg);

private:
void initializeBoard();
bool isLegal(Direction dir, int row, int col, int length);
void killFunction();
void markShip(Direction dir, int row, int col, int length);
int dts;
int scanRow;
int scanCol;
int huntRow;
int huntCol;
int numShipsPlaced;
char board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
char oppBoard[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
};

#endif


ourPlayer.cpp



#include <iostream>
#include <cstdio>
#include<cstdlib>

#include "ourPlayer.h"

ourPlayer::ourPlayer( int boardSize )
:PlayerV2(boardSize)
{
// Could do any initialization of inter-round data structures here.
}

/**
* @brief Destructor placeholder.
* If your code does anything that requires cleanup when the object is
* destroyed, do it here in the destructor.
*/
ourPlayer::~ourPlayer( ) {}

/*
* Private internal function that initializes a MAX_BOARD_SIZE 2D array of char to water.
*/
void ourPlayer::initializeBoard() {
for(int row=0; row<boardSize; row++) {
for(int col=0; col<boardSize; col++) {
this->board[row][col] = WATER;
this->oppBoard[row][col]=WATER;
}
}
}

Message ourPlayer::getMove() {
bool hunting=false;
dts= (rand()%4)+1;

if (scanCol==-3){
scanCol=0;
scanRow=0;
huntRow=0;
huntCol=0;
}

if (oppBoard[scanRow][scanCol]==HIT){
hunting=true;
huntRow=scanRow;
huntCol=scanCol;
}
// Deal with KILL somewhere

if (hunting){
killFunction();
}else {// if (!hunting)
// while (oppBoard[scanRow][scanCol]!=WATER){
scanCol+=3;
if( scanCol >= boardSize ) {
scanRow++;
scanCol= scanRow%3;
}
if( scanRow >= boardSize ) {
scanCol = 0;
scanRow = 0;
}
huntRow = scanRow;
huntCol=scanCol;
// }
}

Message result( SHOT, huntRow, huntCol, "Bang", None, 1 );
return result;
}

void ourPlayer::killFunction(){

// Hunt north
for (int row=huntRow-1;row>-1;row--){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt south
for (int row=huntRow+1;row<boardSize;row++){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
// Hunt east
for (int col=huntCol+1;col<boardSize;col++){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt west
for (int col=huntCol-1;col>-1;col--){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}

}

void ourPlayer::newRound() {
/* DumbPlayer is too simple to do any inter-round learning. Smarter players
* reinitialize any round-specific data structures here.
*/
this->scanRow = 0;
this->scanCol = -3;
this->numShipsPlaced = 0;

this->initializeBoard();
}

/**
* @brief Gets the AI's ship placement choice. This is then returned to the caller.
* @param length The length of the ship to be placed.
* @return Message The most important parts of the returned message are
* the direction, row, and column values.
*
* The parameters returned via the message are:
* 1. the operation: must be PLACE_SHIP
* 2. ship top row value
* 3. ship top col value
* 4. a string for the ship name
* 5. direction Horizontal/Vertical (see defines.h)
* 6. ship length (should match the length passed to placeShip)
*/
Message ourPlayer::placeShip(int length) {
char shipName[10];
snprintf(shipName, sizeof shipName, "Ship%d", numShipsPlaced);

if (numShipsPlaced==0){
int row=boardSize-1;
int col=boardSize-1-length;
markShip(Horizontal,row,col,length);
Message response(PLACE_SHIP,row,col,shipName,Horizontal,length);
numShipsPlaced++;
return response;
}else{
while(true){
Direction dir=Direction((rand()%2)+1);
int row;
int col;
if(dir==Horizontal) {
row=(rand()%(boardSize));
col=(rand()%(boardSize-length+1));
} else {
row=(rand()%(boardSize-length+1));
col=(rand()%(boardSize));
}
if(isLegal(dir,row,col,length)){
markShip(dir,row,col,length);
Message response( PLACE_SHIP, row, col, shipName, dir, length );
numShipsPlaced++;
return response;
}
}
}
}

void ourPlayer::markShip(Direction dir, int row, int col, int length){
if (dir==Horizontal){
for (int i=0;i<length;i++){
board[row][col+i]=SHIP;
}
}else{
for (int i=0;i<length;i++){
board[row+i][col]=SHIP;
}
}
}

bool ourPlayer::isLegal(Direction dir,int row,int col,int length){
if (dir==Horizontal){
if ((col+length)>boardSize){
return false;
}
for (int i=0;i<length;i++){
if (board[row][col+i]==SHIP/*||
board[row][col+i+1]==SHIP||
board[row][col+i-1]==SHIP||
board[row+1][col+i]==SHIP||
board[row-1][col+i]==SHIP*/){
return false;
}
}
return true;
}else{
if ((row+length)>boardSize){
return false;
}
for(int i=0;i<length;i++){
if (board[row+i][col]==SHIP/*||
board[row+i][col+1]==SHIP||
board[row+i][col-1]==SHIP||
board[row+1+i][col]==SHIP||
board[row-1+i][col]==SHIP*/){
return false;
}
}
return true;
}
}

/**
* @brief Updates the AI with the results of its shots and where the opponent is shooting.
* @param msg Message specifying what happened + row/col as appropriate.
*/
void ourPlayer::update(Message msg) {
switch(msg.getMessageType()) {
case HIT:
case KILL:
case MISS:
oppBoard[msg.getRow()][msg.getCol()] = msg.getMessageType();
break;
case WIN:
break;
case LOSE:
break;
case TIE:
break;
case OPPONENT_SHOT:
// TODO: get rid of the cout, but replace in your AI with code that does something
// useful with the information about where the opponent is shooting.
//cout << gotoRowCol(20, 30) << "DumbPl: opponent shot at "<< msg.getRow() << ", " << msg.getCol() << flush;
break;
}
}


EDITS




  • Ships are allowed to touch both vertically and horizontally.


  • Diagonal ships are not allowed


  • You receive confirmation after every move. The AI will known once a ship has sunk.


  • Since this is technically AI vs. AI, I have no say over where the specific ships are placed on the board.



I've already read tons of blogs online talking about similar projects. Problem is I'm a total novice at this stuff so conceptually it's hard for me to actually implement the "strategies" found online.



Here's a sample picture of the AI vs. AI action:



Here's a sample picture of the AI vs. AI action



The final output when the simulations are complete:



The final output when the simulations are complete










share|improve this question









New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
    – Sᴀᴍ Onᴇᴌᴀ
    11 hours ago















up vote
3
down vote

favorite












Found this project online and looking for some guidance. I'm in the process of developing a Battleship AI that will play against different computer difficulties.



The "barebones" part of the project is already completed. I don't need to develop the game board, create a makefile, write a tester, or anything like that. All I'm doing is creating a single AI that will play against the computer.



The playground is a 10x10 board.



There are :





  • 3 ships of length 5


  • 2 ships of length 4


  • 1 ship of length 3


There are four different levels of difficulty (that I'm supposed to beat) :




  • Beginner

  • Normal Player

  • Gambler

  • Learning Gambling


I am unable to see the code for any of these "players". From the names of the difficulties you can guess how they play the game.



So here's the code I developed last night. Everything runs fine and smooth, but I'm only able to defeat the first two difficulties (Beginner and Normal Player). My AI gets absolutely crushed by the later two (I simulated 500 games for each and I maybe won 50 games total).



It would be great is someone could help optimize my code for me. Improving the AI obviously has something to do with the piece placement strategy, but I'm not exactly sure where to go from here. Any help would be greatly appreciated!



ourPlayer.h



#ifndef DUMBPLAYERV2_H          // Double inclusion protection
#define DUMBPLAYERV2_H

using namespace std;

#include "PlayerV2.h"
#include "Message.h"
#include "defines.h"

// DumbPlayer inherits from/extends PlayerV2

class ourPlayer: public PlayerV2 {
public:
ourPlayer( int boardSize );
~ourPlayer();
void newRound();
Message placeShip(int length);
Message getMove();
void update(Message msg);

private:
void initializeBoard();
bool isLegal(Direction dir, int row, int col, int length);
void killFunction();
void markShip(Direction dir, int row, int col, int length);
int dts;
int scanRow;
int scanCol;
int huntRow;
int huntCol;
int numShipsPlaced;
char board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
char oppBoard[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
};

#endif


ourPlayer.cpp



#include <iostream>
#include <cstdio>
#include<cstdlib>

#include "ourPlayer.h"

ourPlayer::ourPlayer( int boardSize )
:PlayerV2(boardSize)
{
// Could do any initialization of inter-round data structures here.
}

/**
* @brief Destructor placeholder.
* If your code does anything that requires cleanup when the object is
* destroyed, do it here in the destructor.
*/
ourPlayer::~ourPlayer( ) {}

/*
* Private internal function that initializes a MAX_BOARD_SIZE 2D array of char to water.
*/
void ourPlayer::initializeBoard() {
for(int row=0; row<boardSize; row++) {
for(int col=0; col<boardSize; col++) {
this->board[row][col] = WATER;
this->oppBoard[row][col]=WATER;
}
}
}

Message ourPlayer::getMove() {
bool hunting=false;
dts= (rand()%4)+1;

if (scanCol==-3){
scanCol=0;
scanRow=0;
huntRow=0;
huntCol=0;
}

if (oppBoard[scanRow][scanCol]==HIT){
hunting=true;
huntRow=scanRow;
huntCol=scanCol;
}
// Deal with KILL somewhere

if (hunting){
killFunction();
}else {// if (!hunting)
// while (oppBoard[scanRow][scanCol]!=WATER){
scanCol+=3;
if( scanCol >= boardSize ) {
scanRow++;
scanCol= scanRow%3;
}
if( scanRow >= boardSize ) {
scanCol = 0;
scanRow = 0;
}
huntRow = scanRow;
huntCol=scanCol;
// }
}

Message result( SHOT, huntRow, huntCol, "Bang", None, 1 );
return result;
}

void ourPlayer::killFunction(){

// Hunt north
for (int row=huntRow-1;row>-1;row--){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt south
for (int row=huntRow+1;row<boardSize;row++){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
// Hunt east
for (int col=huntCol+1;col<boardSize;col++){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt west
for (int col=huntCol-1;col>-1;col--){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}

}

void ourPlayer::newRound() {
/* DumbPlayer is too simple to do any inter-round learning. Smarter players
* reinitialize any round-specific data structures here.
*/
this->scanRow = 0;
this->scanCol = -3;
this->numShipsPlaced = 0;

this->initializeBoard();
}

/**
* @brief Gets the AI's ship placement choice. This is then returned to the caller.
* @param length The length of the ship to be placed.
* @return Message The most important parts of the returned message are
* the direction, row, and column values.
*
* The parameters returned via the message are:
* 1. the operation: must be PLACE_SHIP
* 2. ship top row value
* 3. ship top col value
* 4. a string for the ship name
* 5. direction Horizontal/Vertical (see defines.h)
* 6. ship length (should match the length passed to placeShip)
*/
Message ourPlayer::placeShip(int length) {
char shipName[10];
snprintf(shipName, sizeof shipName, "Ship%d", numShipsPlaced);

if (numShipsPlaced==0){
int row=boardSize-1;
int col=boardSize-1-length;
markShip(Horizontal,row,col,length);
Message response(PLACE_SHIP,row,col,shipName,Horizontal,length);
numShipsPlaced++;
return response;
}else{
while(true){
Direction dir=Direction((rand()%2)+1);
int row;
int col;
if(dir==Horizontal) {
row=(rand()%(boardSize));
col=(rand()%(boardSize-length+1));
} else {
row=(rand()%(boardSize-length+1));
col=(rand()%(boardSize));
}
if(isLegal(dir,row,col,length)){
markShip(dir,row,col,length);
Message response( PLACE_SHIP, row, col, shipName, dir, length );
numShipsPlaced++;
return response;
}
}
}
}

void ourPlayer::markShip(Direction dir, int row, int col, int length){
if (dir==Horizontal){
for (int i=0;i<length;i++){
board[row][col+i]=SHIP;
}
}else{
for (int i=0;i<length;i++){
board[row+i][col]=SHIP;
}
}
}

bool ourPlayer::isLegal(Direction dir,int row,int col,int length){
if (dir==Horizontal){
if ((col+length)>boardSize){
return false;
}
for (int i=0;i<length;i++){
if (board[row][col+i]==SHIP/*||
board[row][col+i+1]==SHIP||
board[row][col+i-1]==SHIP||
board[row+1][col+i]==SHIP||
board[row-1][col+i]==SHIP*/){
return false;
}
}
return true;
}else{
if ((row+length)>boardSize){
return false;
}
for(int i=0;i<length;i++){
if (board[row+i][col]==SHIP/*||
board[row+i][col+1]==SHIP||
board[row+i][col-1]==SHIP||
board[row+1+i][col]==SHIP||
board[row-1+i][col]==SHIP*/){
return false;
}
}
return true;
}
}

/**
* @brief Updates the AI with the results of its shots and where the opponent is shooting.
* @param msg Message specifying what happened + row/col as appropriate.
*/
void ourPlayer::update(Message msg) {
switch(msg.getMessageType()) {
case HIT:
case KILL:
case MISS:
oppBoard[msg.getRow()][msg.getCol()] = msg.getMessageType();
break;
case WIN:
break;
case LOSE:
break;
case TIE:
break;
case OPPONENT_SHOT:
// TODO: get rid of the cout, but replace in your AI with code that does something
// useful with the information about where the opponent is shooting.
//cout << gotoRowCol(20, 30) << "DumbPl: opponent shot at "<< msg.getRow() << ", " << msg.getCol() << flush;
break;
}
}


EDITS




  • Ships are allowed to touch both vertically and horizontally.


  • Diagonal ships are not allowed


  • You receive confirmation after every move. The AI will known once a ship has sunk.


  • Since this is technically AI vs. AI, I have no say over where the specific ships are placed on the board.



I've already read tons of blogs online talking about similar projects. Problem is I'm a total novice at this stuff so conceptually it's hard for me to actually implement the "strategies" found online.



Here's a sample picture of the AI vs. AI action:



Here's a sample picture of the AI vs. AI action



The final output when the simulations are complete:



The final output when the simulations are complete










share|improve this question









New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.




















  • did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
    – Sᴀᴍ Onᴇᴌᴀ
    11 hours ago













up vote
3
down vote

favorite









up vote
3
down vote

favorite











Found this project online and looking for some guidance. I'm in the process of developing a Battleship AI that will play against different computer difficulties.



The "barebones" part of the project is already completed. I don't need to develop the game board, create a makefile, write a tester, or anything like that. All I'm doing is creating a single AI that will play against the computer.



The playground is a 10x10 board.



There are :





  • 3 ships of length 5


  • 2 ships of length 4


  • 1 ship of length 3


There are four different levels of difficulty (that I'm supposed to beat) :




  • Beginner

  • Normal Player

  • Gambler

  • Learning Gambling


I am unable to see the code for any of these "players". From the names of the difficulties you can guess how they play the game.



So here's the code I developed last night. Everything runs fine and smooth, but I'm only able to defeat the first two difficulties (Beginner and Normal Player). My AI gets absolutely crushed by the later two (I simulated 500 games for each and I maybe won 50 games total).



It would be great is someone could help optimize my code for me. Improving the AI obviously has something to do with the piece placement strategy, but I'm not exactly sure where to go from here. Any help would be greatly appreciated!



ourPlayer.h



#ifndef DUMBPLAYERV2_H          // Double inclusion protection
#define DUMBPLAYERV2_H

using namespace std;

#include "PlayerV2.h"
#include "Message.h"
#include "defines.h"

// DumbPlayer inherits from/extends PlayerV2

class ourPlayer: public PlayerV2 {
public:
ourPlayer( int boardSize );
~ourPlayer();
void newRound();
Message placeShip(int length);
Message getMove();
void update(Message msg);

private:
void initializeBoard();
bool isLegal(Direction dir, int row, int col, int length);
void killFunction();
void markShip(Direction dir, int row, int col, int length);
int dts;
int scanRow;
int scanCol;
int huntRow;
int huntCol;
int numShipsPlaced;
char board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
char oppBoard[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
};

#endif


ourPlayer.cpp



#include <iostream>
#include <cstdio>
#include<cstdlib>

#include "ourPlayer.h"

ourPlayer::ourPlayer( int boardSize )
:PlayerV2(boardSize)
{
// Could do any initialization of inter-round data structures here.
}

/**
* @brief Destructor placeholder.
* If your code does anything that requires cleanup when the object is
* destroyed, do it here in the destructor.
*/
ourPlayer::~ourPlayer( ) {}

/*
* Private internal function that initializes a MAX_BOARD_SIZE 2D array of char to water.
*/
void ourPlayer::initializeBoard() {
for(int row=0; row<boardSize; row++) {
for(int col=0; col<boardSize; col++) {
this->board[row][col] = WATER;
this->oppBoard[row][col]=WATER;
}
}
}

Message ourPlayer::getMove() {
bool hunting=false;
dts= (rand()%4)+1;

if (scanCol==-3){
scanCol=0;
scanRow=0;
huntRow=0;
huntCol=0;
}

if (oppBoard[scanRow][scanCol]==HIT){
hunting=true;
huntRow=scanRow;
huntCol=scanCol;
}
// Deal with KILL somewhere

if (hunting){
killFunction();
}else {// if (!hunting)
// while (oppBoard[scanRow][scanCol]!=WATER){
scanCol+=3;
if( scanCol >= boardSize ) {
scanRow++;
scanCol= scanRow%3;
}
if( scanRow >= boardSize ) {
scanCol = 0;
scanRow = 0;
}
huntRow = scanRow;
huntCol=scanCol;
// }
}

Message result( SHOT, huntRow, huntCol, "Bang", None, 1 );
return result;
}

void ourPlayer::killFunction(){

// Hunt north
for (int row=huntRow-1;row>-1;row--){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt south
for (int row=huntRow+1;row<boardSize;row++){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
// Hunt east
for (int col=huntCol+1;col<boardSize;col++){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt west
for (int col=huntCol-1;col>-1;col--){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}

}

void ourPlayer::newRound() {
/* DumbPlayer is too simple to do any inter-round learning. Smarter players
* reinitialize any round-specific data structures here.
*/
this->scanRow = 0;
this->scanCol = -3;
this->numShipsPlaced = 0;

this->initializeBoard();
}

/**
* @brief Gets the AI's ship placement choice. This is then returned to the caller.
* @param length The length of the ship to be placed.
* @return Message The most important parts of the returned message are
* the direction, row, and column values.
*
* The parameters returned via the message are:
* 1. the operation: must be PLACE_SHIP
* 2. ship top row value
* 3. ship top col value
* 4. a string for the ship name
* 5. direction Horizontal/Vertical (see defines.h)
* 6. ship length (should match the length passed to placeShip)
*/
Message ourPlayer::placeShip(int length) {
char shipName[10];
snprintf(shipName, sizeof shipName, "Ship%d", numShipsPlaced);

if (numShipsPlaced==0){
int row=boardSize-1;
int col=boardSize-1-length;
markShip(Horizontal,row,col,length);
Message response(PLACE_SHIP,row,col,shipName,Horizontal,length);
numShipsPlaced++;
return response;
}else{
while(true){
Direction dir=Direction((rand()%2)+1);
int row;
int col;
if(dir==Horizontal) {
row=(rand()%(boardSize));
col=(rand()%(boardSize-length+1));
} else {
row=(rand()%(boardSize-length+1));
col=(rand()%(boardSize));
}
if(isLegal(dir,row,col,length)){
markShip(dir,row,col,length);
Message response( PLACE_SHIP, row, col, shipName, dir, length );
numShipsPlaced++;
return response;
}
}
}
}

void ourPlayer::markShip(Direction dir, int row, int col, int length){
if (dir==Horizontal){
for (int i=0;i<length;i++){
board[row][col+i]=SHIP;
}
}else{
for (int i=0;i<length;i++){
board[row+i][col]=SHIP;
}
}
}

bool ourPlayer::isLegal(Direction dir,int row,int col,int length){
if (dir==Horizontal){
if ((col+length)>boardSize){
return false;
}
for (int i=0;i<length;i++){
if (board[row][col+i]==SHIP/*||
board[row][col+i+1]==SHIP||
board[row][col+i-1]==SHIP||
board[row+1][col+i]==SHIP||
board[row-1][col+i]==SHIP*/){
return false;
}
}
return true;
}else{
if ((row+length)>boardSize){
return false;
}
for(int i=0;i<length;i++){
if (board[row+i][col]==SHIP/*||
board[row+i][col+1]==SHIP||
board[row+i][col-1]==SHIP||
board[row+1+i][col]==SHIP||
board[row-1+i][col]==SHIP*/){
return false;
}
}
return true;
}
}

/**
* @brief Updates the AI with the results of its shots and where the opponent is shooting.
* @param msg Message specifying what happened + row/col as appropriate.
*/
void ourPlayer::update(Message msg) {
switch(msg.getMessageType()) {
case HIT:
case KILL:
case MISS:
oppBoard[msg.getRow()][msg.getCol()] = msg.getMessageType();
break;
case WIN:
break;
case LOSE:
break;
case TIE:
break;
case OPPONENT_SHOT:
// TODO: get rid of the cout, but replace in your AI with code that does something
// useful with the information about where the opponent is shooting.
//cout << gotoRowCol(20, 30) << "DumbPl: opponent shot at "<< msg.getRow() << ", " << msg.getCol() << flush;
break;
}
}


EDITS




  • Ships are allowed to touch both vertically and horizontally.


  • Diagonal ships are not allowed


  • You receive confirmation after every move. The AI will known once a ship has sunk.


  • Since this is technically AI vs. AI, I have no say over where the specific ships are placed on the board.



I've already read tons of blogs online talking about similar projects. Problem is I'm a total novice at this stuff so conceptually it's hard for me to actually implement the "strategies" found online.



Here's a sample picture of the AI vs. AI action:



Here's a sample picture of the AI vs. AI action



The final output when the simulations are complete:



The final output when the simulations are complete










share|improve this question









New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











Found this project online and looking for some guidance. I'm in the process of developing a Battleship AI that will play against different computer difficulties.



The "barebones" part of the project is already completed. I don't need to develop the game board, create a makefile, write a tester, or anything like that. All I'm doing is creating a single AI that will play against the computer.



The playground is a 10x10 board.



There are :





  • 3 ships of length 5


  • 2 ships of length 4


  • 1 ship of length 3


There are four different levels of difficulty (that I'm supposed to beat) :




  • Beginner

  • Normal Player

  • Gambler

  • Learning Gambling


I am unable to see the code for any of these "players". From the names of the difficulties you can guess how they play the game.



So here's the code I developed last night. Everything runs fine and smooth, but I'm only able to defeat the first two difficulties (Beginner and Normal Player). My AI gets absolutely crushed by the later two (I simulated 500 games for each and I maybe won 50 games total).



It would be great is someone could help optimize my code for me. Improving the AI obviously has something to do with the piece placement strategy, but I'm not exactly sure where to go from here. Any help would be greatly appreciated!



ourPlayer.h



#ifndef DUMBPLAYERV2_H          // Double inclusion protection
#define DUMBPLAYERV2_H

using namespace std;

#include "PlayerV2.h"
#include "Message.h"
#include "defines.h"

// DumbPlayer inherits from/extends PlayerV2

class ourPlayer: public PlayerV2 {
public:
ourPlayer( int boardSize );
~ourPlayer();
void newRound();
Message placeShip(int length);
Message getMove();
void update(Message msg);

private:
void initializeBoard();
bool isLegal(Direction dir, int row, int col, int length);
void killFunction();
void markShip(Direction dir, int row, int col, int length);
int dts;
int scanRow;
int scanCol;
int huntRow;
int huntCol;
int numShipsPlaced;
char board[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
char oppBoard[MAX_BOARD_SIZE][MAX_BOARD_SIZE];
};

#endif


ourPlayer.cpp



#include <iostream>
#include <cstdio>
#include<cstdlib>

#include "ourPlayer.h"

ourPlayer::ourPlayer( int boardSize )
:PlayerV2(boardSize)
{
// Could do any initialization of inter-round data structures here.
}

/**
* @brief Destructor placeholder.
* If your code does anything that requires cleanup when the object is
* destroyed, do it here in the destructor.
*/
ourPlayer::~ourPlayer( ) {}

/*
* Private internal function that initializes a MAX_BOARD_SIZE 2D array of char to water.
*/
void ourPlayer::initializeBoard() {
for(int row=0; row<boardSize; row++) {
for(int col=0; col<boardSize; col++) {
this->board[row][col] = WATER;
this->oppBoard[row][col]=WATER;
}
}
}

Message ourPlayer::getMove() {
bool hunting=false;
dts= (rand()%4)+1;

if (scanCol==-3){
scanCol=0;
scanRow=0;
huntRow=0;
huntCol=0;
}

if (oppBoard[scanRow][scanCol]==HIT){
hunting=true;
huntRow=scanRow;
huntCol=scanCol;
}
// Deal with KILL somewhere

if (hunting){
killFunction();
}else {// if (!hunting)
// while (oppBoard[scanRow][scanCol]!=WATER){
scanCol+=3;
if( scanCol >= boardSize ) {
scanRow++;
scanCol= scanRow%3;
}
if( scanRow >= boardSize ) {
scanCol = 0;
scanRow = 0;
}
huntRow = scanRow;
huntCol=scanCol;
// }
}

Message result( SHOT, huntRow, huntCol, "Bang", None, 1 );
return result;
}

void ourPlayer::killFunction(){

// Hunt north
for (int row=huntRow-1;row>-1;row--){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt south
for (int row=huntRow+1;row<boardSize;row++){
char ch=oppBoard[row][huntCol];
if (ch==WATER){
huntRow=row;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
// Hunt east
for (int col=huntCol+1;col<boardSize;col++){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}
//Hunt west
for (int col=huntCol-1;col>-1;col--){
char ch=oppBoard[huntRow][col];
if (ch==WATER){
huntCol=col;
return;
}
else if(ch==MISS||ch==KILL){
break;
}
else{
//HIT=keep going
}
}

}

void ourPlayer::newRound() {
/* DumbPlayer is too simple to do any inter-round learning. Smarter players
* reinitialize any round-specific data structures here.
*/
this->scanRow = 0;
this->scanCol = -3;
this->numShipsPlaced = 0;

this->initializeBoard();
}

/**
* @brief Gets the AI's ship placement choice. This is then returned to the caller.
* @param length The length of the ship to be placed.
* @return Message The most important parts of the returned message are
* the direction, row, and column values.
*
* The parameters returned via the message are:
* 1. the operation: must be PLACE_SHIP
* 2. ship top row value
* 3. ship top col value
* 4. a string for the ship name
* 5. direction Horizontal/Vertical (see defines.h)
* 6. ship length (should match the length passed to placeShip)
*/
Message ourPlayer::placeShip(int length) {
char shipName[10];
snprintf(shipName, sizeof shipName, "Ship%d", numShipsPlaced);

if (numShipsPlaced==0){
int row=boardSize-1;
int col=boardSize-1-length;
markShip(Horizontal,row,col,length);
Message response(PLACE_SHIP,row,col,shipName,Horizontal,length);
numShipsPlaced++;
return response;
}else{
while(true){
Direction dir=Direction((rand()%2)+1);
int row;
int col;
if(dir==Horizontal) {
row=(rand()%(boardSize));
col=(rand()%(boardSize-length+1));
} else {
row=(rand()%(boardSize-length+1));
col=(rand()%(boardSize));
}
if(isLegal(dir,row,col,length)){
markShip(dir,row,col,length);
Message response( PLACE_SHIP, row, col, shipName, dir, length );
numShipsPlaced++;
return response;
}
}
}
}

void ourPlayer::markShip(Direction dir, int row, int col, int length){
if (dir==Horizontal){
for (int i=0;i<length;i++){
board[row][col+i]=SHIP;
}
}else{
for (int i=0;i<length;i++){
board[row+i][col]=SHIP;
}
}
}

bool ourPlayer::isLegal(Direction dir,int row,int col,int length){
if (dir==Horizontal){
if ((col+length)>boardSize){
return false;
}
for (int i=0;i<length;i++){
if (board[row][col+i]==SHIP/*||
board[row][col+i+1]==SHIP||
board[row][col+i-1]==SHIP||
board[row+1][col+i]==SHIP||
board[row-1][col+i]==SHIP*/){
return false;
}
}
return true;
}else{
if ((row+length)>boardSize){
return false;
}
for(int i=0;i<length;i++){
if (board[row+i][col]==SHIP/*||
board[row+i][col+1]==SHIP||
board[row+i][col-1]==SHIP||
board[row+1+i][col]==SHIP||
board[row-1+i][col]==SHIP*/){
return false;
}
}
return true;
}
}

/**
* @brief Updates the AI with the results of its shots and where the opponent is shooting.
* @param msg Message specifying what happened + row/col as appropriate.
*/
void ourPlayer::update(Message msg) {
switch(msg.getMessageType()) {
case HIT:
case KILL:
case MISS:
oppBoard[msg.getRow()][msg.getCol()] = msg.getMessageType();
break;
case WIN:
break;
case LOSE:
break;
case TIE:
break;
case OPPONENT_SHOT:
// TODO: get rid of the cout, but replace in your AI with code that does something
// useful with the information about where the opponent is shooting.
//cout << gotoRowCol(20, 30) << "DumbPl: opponent shot at "<< msg.getRow() << ", " << msg.getCol() << flush;
break;
}
}


EDITS




  • Ships are allowed to touch both vertically and horizontally.


  • Diagonal ships are not allowed


  • You receive confirmation after every move. The AI will known once a ship has sunk.


  • Since this is technically AI vs. AI, I have no say over where the specific ships are placed on the board.



I've already read tons of blogs online talking about similar projects. Problem is I'm a total novice at this stuff so conceptually it's hard for me to actually implement the "strategies" found online.



Here's a sample picture of the AI vs. AI action:



Here's a sample picture of the AI vs. AI action



The final output when the simulations are complete:



The final output when the simulations are complete







c++ performance ai battleship






share|improve this question









New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited 9 hours ago









Sᴀᴍ Onᴇᴌᴀ

8,09961751




8,09961751






New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked 13 hours ago









user186793

161




161




New contributor




user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






user186793 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.












  • did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
    – Sᴀᴍ Onᴇᴌᴀ
    11 hours ago


















  • did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
    – Sᴀᴍ Onᴇᴌᴀ
    11 hours ago
















did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
– Sᴀᴍ Onᴇᴌᴀ
11 hours ago




did you recently register with the account Caleb J, with the suggested edit? if so, we can request the accounts be merged.
– Sᴀᴍ Onᴇᴌᴀ
11 hours ago










1 Answer
1






active

oldest

votes

















up vote
2
down vote













Can you add some rules on ship placement? Can they touch horizontally or vertically? Can they touch diagonally? Do you get a confirmation when you have sunk a ship?



Why do you reset 'hunting' each turn? I would put this as class member.
Do you use the variable 'dts'?



I see you already implemented the parity strategy, so according to the blog, probability density function are your next best bet.
This involves calculating all possible locations for the remaining enemy ships, and calculating the probability of a present ship for each tile.
http://www.datagenetics.com/blog/december32011/






share|improve this answer








New contributor




user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.


















    Your Answer





    StackExchange.ifUsing("editor", function () {
    return StackExchange.using("mathjaxEditing", function () {
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
    });
    });
    }, "mathjax-editing");

    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "196"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    convertImagesToLinks: false,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: null,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });






    user186793 is a new contributor. Be nice, and check out our Code of Conduct.










    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209373%2fc-battleship-player-ai%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    2
    down vote













    Can you add some rules on ship placement? Can they touch horizontally or vertically? Can they touch diagonally? Do you get a confirmation when you have sunk a ship?



    Why do you reset 'hunting' each turn? I would put this as class member.
    Do you use the variable 'dts'?



    I see you already implemented the parity strategy, so according to the blog, probability density function are your next best bet.
    This involves calculating all possible locations for the remaining enemy ships, and calculating the probability of a present ship for each tile.
    http://www.datagenetics.com/blog/december32011/






    share|improve this answer








    New contributor




    user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






















      up vote
      2
      down vote













      Can you add some rules on ship placement? Can they touch horizontally or vertically? Can they touch diagonally? Do you get a confirmation when you have sunk a ship?



      Why do you reset 'hunting' each turn? I would put this as class member.
      Do you use the variable 'dts'?



      I see you already implemented the parity strategy, so according to the blog, probability density function are your next best bet.
      This involves calculating all possible locations for the remaining enemy ships, and calculating the probability of a present ship for each tile.
      http://www.datagenetics.com/blog/december32011/






      share|improve this answer








      New contributor




      user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.




















        up vote
        2
        down vote










        up vote
        2
        down vote









        Can you add some rules on ship placement? Can they touch horizontally or vertically? Can they touch diagonally? Do you get a confirmation when you have sunk a ship?



        Why do you reset 'hunting' each turn? I would put this as class member.
        Do you use the variable 'dts'?



        I see you already implemented the parity strategy, so according to the blog, probability density function are your next best bet.
        This involves calculating all possible locations for the remaining enemy ships, and calculating the probability of a present ship for each tile.
        http://www.datagenetics.com/blog/december32011/






        share|improve this answer








        New contributor




        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        Can you add some rules on ship placement? Can they touch horizontally or vertically? Can they touch diagonally? Do you get a confirmation when you have sunk a ship?



        Why do you reset 'hunting' each turn? I would put this as class member.
        Do you use the variable 'dts'?



        I see you already implemented the parity strategy, so according to the blog, probability density function are your next best bet.
        This involves calculating all possible locations for the remaining enemy ships, and calculating the probability of a present ship for each tile.
        http://www.datagenetics.com/blog/december32011/







        share|improve this answer








        New contributor




        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        share|improve this answer



        share|improve this answer






        New contributor




        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.









        answered 12 hours ago









        user2966394

        233




        233




        New contributor




        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.





        New contributor





        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






        user2966394 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
        Check out our Code of Conduct.






















            user186793 is a new contributor. Be nice, and check out our Code of Conduct.










            draft saved

            draft discarded


















            user186793 is a new contributor. Be nice, and check out our Code of Conduct.













            user186793 is a new contributor. Be nice, and check out our Code of Conduct.












            user186793 is a new contributor. Be nice, and check out our Code of Conduct.
















            Thanks for contributing an answer to Code Review Stack Exchange!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            Use MathJax to format equations. MathJax reference.


            To learn more, see our tips on writing great answers.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209373%2fc-battleship-player-ai%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Quarter-circle Tiles

            build a pushdown automaton that recognizes the reverse language of a given pushdown automaton?

            Mont Emei