/*
Free Battleship clone for Win32/Linux Console.
Copyright (C) 2005 Dennis Gertitschke
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "../inc/Computer.h"
#include "../inc/Ship.h"
#include "../inc/Screen.h"
#include "../inc/Common.h"
#include <time.h>
#include <stdlib.h>
int Computer::m_iInstanceCount=0;
Computer::Computer(){
m_iDistance=1;
m_iDirection=0;
m_cShotX=m_cFirstX=0;
m_iShotY=m_iFirstY=0;
m_iInstance=m_iInstanceCount;
m_iInstanceCount++;
m_iMode=GAMETYPE_PLAYER_VS_COMPUTER;
m_pScreen->setMode(GAMETYPE_PLAYER_VS_COMPUTER);
m_bHit=false;
m_iHitCount=0;
srand(static_cast<unsigned int>(time(NULL)));
}
Computer::~Computer(){
}
void Computer::registerCallback(void *pObj,F pFunc){
m_pObj=pObj;
m_pFunc=pFunc;
}
void Computer::setMode(int iMode){
m_pScreen->setMode(iMode);
m_iMode=iMode;
}
void Computer::validateCompShotCallback(void *pObj,char cX,int iY,bool& bRefHit,int& iRefHitCount){
Computer *self=static_cast<Computer*>(pObj);
self->validateCompShot(cX,iY,bRefHit,iRefHitCount);
}
void Computer::validateCompShot(char cX,int iY,bool& bRefHit,int& iRefHitCount){
int iShot=transformToBoardIndex(cX,iY);
char cTmpX=cX;
int iTmpY=iY;
if(m_arrBoard[iShot]!=NULL){
bRefHit=true;
iRefHitCount++;
m_arrBoard[iShot]->hit();
if(m_arrBoard[iShot]->getStatus()){
m_iShipCount--;
iRefHitCount=0;
if(!m_iShipCount)
m_bDefeat=true;
}
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayHit(cTmpX,iTmpY);
m_pScreen->printCompStatus(bRefHit,m_arrBoard[iShot]->getId(),m_arrBoard[iShot]->getStatus());
}
else{
bRefHit=false;
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayMiss(cTmpX,iTmpY);
m_pScreen->printCompStatus(bRefHit,NULL,false);
}
}
void Computer::reset(){
m_bDefeat=false;
m_iShipCount=4;
for(int i=0;i<100;i++){
m_arrBoard[i]=NULL;
m_barrValidStart[i]=true;
m_barrValidShot[i]=true;
}
m_iDistance=1;
m_iIndex=m_iDirection=0;
m_cShotX=m_cFirstX=0;
m_iShotY=m_iFirstY=0;
m_bHit=false;
m_iHitCount=0;
m_pFrigate->reset();
m_pDestroyer->reset();
m_pCruiser->reset();
m_pBattleship->reset();
}
void Computer::shoot(){
int i=0;
if(!m_iHitCount){
do{
randomCoordinate(m_cShotX,m_iShotY);
m_iIndex=transformToBoardIndex(m_cShotX,m_iShotY);
m_iDistance=1;
}while(!m_barrValidShot[m_iIndex]);
}
if(m_iHitCount==1){
if(m_bHit){
m_cFirstX=m_cShotX;
m_iFirstY=m_iShotY;
randomDirection();
}
while(i<4){
switch(m_iDirection){
case 1: if(validateCoordinate(static_cast<char>(m_cFirstX+m_iDistance),m_iFirstY)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cFirstX+m_iDistance),m_iFirstY);
if(m_barrValidShot[m_iIndex]){
m_cShotX=static_cast<char>(m_cFirstX+m_iDistance);
m_iShotY=m_iFirstY;
i=4;
break;
}
else
m_iDirection=2;
}
else
m_iDirection=2;
break;
case 2: if(validateCoordinate(static_cast<char>(m_cFirstX-m_iDistance),m_iFirstY)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cFirstX-m_iDistance),m_iFirstY);
if(m_barrValidShot[m_iIndex]){
m_cShotX=static_cast<char>(m_cFirstX-m_iDistance);
m_iShotY=m_iFirstY;
i=4;
break;
}
else
m_iDirection=3;
}
else
m_iDirection=3;
break;
case 3: if(validateCoordinate(m_cFirstX,m_iFirstY+m_iDistance)){
m_iIndex=transformToBoardIndex(m_cFirstX,m_iFirstY+m_iDistance);
if(m_barrValidShot[m_iIndex]){
m_cShotX=m_cFirstX;
m_iShotY=m_iFirstY+m_iDistance;
i=4;
break;
}
else
m_iDirection=4;
}
else
m_iDirection=4;
break;
case 4: if(validateCoordinate(m_cFirstX,m_iFirstY-m_iDistance)){
m_iIndex=transformToBoardIndex(m_cFirstX,m_iFirstY-m_iDistance);
if(m_barrValidShot[m_iIndex]){
m_cShotX=m_cFirstX;
m_iShotY=m_iFirstY-m_iDistance;
i=4;
break;
}
else
m_iDirection=1;
}
else
m_iDirection=1;
break;
}
i++;
}
}
if(m_iHitCount>1){
if(m_bHit)
m_iDistance++;
else
m_iDistance=1;
while(i<4){
switch(m_iDirection){
case 1: if(validateCoordinate(static_cast<char>(m_cFirstX+m_iDistance),m_iFirstY)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cFirstX+m_iDistance),m_iFirstY);
if(m_barrValidShot[m_iIndex]){
m_cShotX=static_cast<char>(m_cFirstX+m_iDistance);
m_iShotY=m_iFirstY;
i=4;
break;
}
else{
m_iDirection=2;
m_iDistance=1;
}
}
else{
m_iDirection=2;
m_iDistance=1;
}
break;
case 2: if(validateCoordinate(static_cast<char>(m_cFirstX-m_iDistance),m_iFirstY)){
m_iIndex=transformToBoardIndex(static_cast<char>(m_cFirstX-m_iDistance),m_iFirstY);
if(m_barrValidShot[m_iIndex]){
m_cShotX=static_cast<char>(m_cFirstX-m_iDistance);
m_iShotY=m_iFirstY;
i=4;
break;
}
else{
m_iDirection=1;
m_iDistance=1;
}
}
else{
m_iDirection=1;
m_iDistance=1;
break;
case 3: if(validateCoordinate(m_cFirstX,m_iFirstY+m_iDistance)){
m_iIndex=transformToBoardIndex(m_cFirstX,m_iFirstY+m_iDistance);
if(m_barrValidShot[m_iIndex]){
m_cShotX=m_cFirstX;
m_iShotY=m_iFirstY+m_iDistance;
i=4;
break;
}
else{
m_iDirection=4;
m_iDistance=1;
}
}
else{
m_iDirection=4;
m_iDistance=1;
}
break;
case 4: if(validateCoordinate(m_cFirstX,m_iFirstY-m_iDistance)){
m_iIndex=transformToBoardIndex(m_cFirstX,m_iFirstY-m_iDistance);
if(m_barrValidShot[m_iIndex]){
m_cShotX=m_cFirstX;
m_iShotY=m_iFirstY-m_iDistance;
i=4;
break;
}
else{
m_iDirection=3;
m_iDistance=1;
}
}
else{
m_iDirection=3;
m_iDistance=1;
}
break;
}
i++;
}
}
}
m_barrValidShot[m_iIndex]=false;
// call back the player (or the other computer when in GAMETYPE_COMPUTER_VS_COMPUTER)
m_pFunc(m_pObj,m_cShotX,m_iShotY,m_bHit,m_iHitCount);
}
void Computer::validateUserShotCallback(void *pObj,char cX,int iY){
Computer *self=static_cast<Computer*>(pObj);
self->validateUserShot(cX,iY);
}
void Computer::validateUserShot(char cX,int iY){
int iShot=transformToBoardIndex(cX,iY);
char cTmpX=cX;
int iTmpY=iY;
if(m_arrBoard[iShot]!=NULL){
m_arrBoard[iShot]->hit();
if(m_arrBoard[iShot]->getStatus()){
m_iShipCount--;
if(!m_iShipCount)
m_bDefeat=true;
}
m_pScreen->printCompStatus(true,m_arrBoard[iShot]->getId(),m_arrBoard[iShot]->getStatus());
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayHit(cTmpX,iTmpY);
}
else{
m_pScreen->printCompStatus(false,NULL,false);
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayMiss(cTmpX,iTmpY);
}
}
void Computer::randomCoordinate(char& cRefX,int& iRefY){
cRefX=static_cast<char>(rand()%10+65);
iRefY=rand()%10+1;
}
void Computer::randomDirection(){
m_iDirection=rand()%4+1;
}
void Computer::transformToCompScreen(char& cRefX,int& iRefY){
if(m_iInstance==0){
cRefX=static_cast<char>((cRefX-65)+2*(cRefX-40));
iRefY=iRefY+4;
}
if(m_iInstance==1){
cRefX=static_cast<char>((cRefX-65)+2*(cRefX-64));
iRefY=iRefY+4;
}
}
void Computer::placeShip(Ship* ship,char cStartX,int iStartY){
int i=0;
char cTmpX;
int iTmpY;
while(i<ship->getLength()){
switch(m_iDirection){
case 1:
if(m_iMode==GAMETYPE_COMPUTER_VS_COMPUTER){
cTmpX=cStartX;
iTmpY=iStartY;
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
markNeighboursInvalid(cStartX,iStartY);
m_iIndex=transformToBoardIndex(cStartX,iStartY);
m_arrBoard[m_iIndex]=ship;
m_barrValidStart[m_iIndex]=false;
cStartX++;
break;
case 2:
if(m_iMode==GAMETYPE_COMPUTER_VS_COMPUTER){
cTmpX=cStartX;
iTmpY=iStartY;
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
markNeighboursInvalid(cStartX,iStartY);
m_iIndex=transformToBoardIndex(cStartX,iStartY);
m_arrBoard[m_iIndex]=ship;
m_barrValidStart[m_iIndex]=false;
cStartX--;
break;
case 3:
if(m_iMode==GAMETYPE_COMPUTER_VS_COMPUTER){
cTmpX=cStartX;
iTmpY=iStartY;
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
markNeighboursInvalid(cStartX,iStartY);
m_iIndex=transformToBoardIndex(cStartX,iStartY);
m_arrBoard[m_iIndex]=ship;
m_barrValidStart[m_iIndex]=false;
iStartY++;
break;
case 4:
if(m_iMode==GAMETYPE_COMPUTER_VS_COMPUTER){
cTmpX=cStartX;
iTmpY=iStartY;
transformToCompScreen(cTmpX,iTmpY);
m_pScreen->displayShip(cTmpX,iTmpY);
}
markNeighboursInvalid(cStartX,iStartY);
m_iIndex=transformToBoardIndex(cStartX,iStartY);
m_arrBoard[m_iIndex]=ship;
m_barrValidStart[m_iIndex]=false;
iStartY--;
break;
}
i++;
}
}
void Computer::calculateValidStartEnd(Ship *pShip){
int iDistance=0;
int iStartY=0;
char cStartX=0;
bool bFound=false;
int i;
iDistance=pShip->getLength()-1;
do{
do{
randomCoordinate(cStartX,iStartY);
m_iIndex=transformToBoardIndex(cStartX,iStartY);
}while(!m_barrValidStart[m_iIndex]);
randomDirection();
i=0;
while(i<4){
switch(m_iDirection){
case 1: if(validateCoordinate(static_cast<char>(cStartX+iDistance),iStartY)){
if(checkStartEnd(cStartX,iStartY,pShip->getLength())){
bFound=true;
i=4;
break;
}
else
m_iDirection=2;
}
else
m_iDirection=2;
break;
case 2: if(validateCoordinate(static_cast<char>(cStartX-iDistance),iStartY)){
if(checkStartEnd(cStartX,iStartY,pShip->getLength())){
bFound=true;
i=4;
break;
}
else
m_iDirection=3;
}
else
m_iDirection=3;
break;
case 3: if(validateCoordinate(cStartX,iStartY+iDistance)){
if(checkStartEnd(cStartX,iStartY,pShip->getLength())){
bFound=true;
i=4;
break;
}
else m_iDirection=4;
}
else
m_iDirection=4;
break;
case 4: if(validateCoordinate(cStartX,iStartY-iDistance)){
if(checkStartEnd(cStartX,iStartY,pShip->getLength())){
bFound=true;
i=4;
break;
}
else
m_iDirection=1;
}
else
m_iDirection=1;
break;
}
i++;
}
}while(!bFound);
placeShip(pShip,cStartX,iStartY);
}
void Computer::deployFleet(){
calculateValidStartEnd(m_pFrigate);
calculateValidStartEnd(m_pDestroyer);
calculateValidStartEnd(m_pCruiser);
calculateValidStartEnd(m_pBattleship);
m_pScreen->printCompSucessfullyPlaced();
}
bool Computer::checkStartEnd(char cStartX,int iStartY,int iLength){
bool bResult=true;
int i=0;
while(i<iLength){
switch(m_iDirection){
case 1: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
cStartX++;
break;
case 2: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
cStartX--;
break;
case 3: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
iStartY++;
break;
case 4: m_iIndex=transformToBoardIndex(cStartX,iStartY);
if(!m_barrValidStart[m_iIndex])
return false;
iStartY--;
break;
}
i++;
}
return bResult;
}