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:
The final output when the simulations are complete:
c++ performance ai battleship
New contributor
add a comment |
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:
The final output when the simulations are complete:
c++ performance ai battleship
New contributor
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
add a comment |
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:
The final output when the simulations are complete:
c++ performance ai battleship
New contributor
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:
The final output when the simulations are complete:
c++ performance ai battleship
c++ performance ai battleship
New contributor
New contributor
edited 9 hours ago
Sᴀᴍ Onᴇᴌᴀ
8,09961751
8,09961751
New contributor
asked 13 hours ago
user186793
161
161
New contributor
New contributor
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
add a comment |
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
add a comment |
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/
New contributor
add a comment |
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/
New contributor
add a comment |
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/
New contributor
add a comment |
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/
New contributor
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/
New contributor
New contributor
answered 12 hours ago
user2966394
233
233
New contributor
New contributor
add a comment |
add a comment |
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.
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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