?? engine.cpp
字號:
// engine.cpp
//
// Copyright (c) 2003 Symbian Ltd. All rights reserved.
#include "engine.h"
#include <e32math.h>
enum TPanic
{
EBadShipType, // TShip constructor given bad argument
EShipAtNoShip, // ShipAt() called, but square isn't a ship square
EShipAtNotMyFleet, // ShipAt() called, but it's not my fleet
EShipAtShipNotFound, // ShipAt() has searched, but not found right ship!
EBadSquare, // bad x,y reference
EHitUnknownSquare, // attempt to hit an unknown square
EHitRequestKnownSquare, // attempt to request hit on known square
EHitRequestAlreadyOutstanding, // hit request when one is already outstanding
EHitRequestMyFleet, // hit request on my fleet
EShipsNotAllPlaced, // ran out of attempts to place all ships
EMarkUnHitSquare, // set sea around hit, but square is not hit
};
void Panic(TInt aPanic)
{
_LIT(KPanicCategory,"BSHIPS Engine");
User::Panic(KPanicCategory, aPanic);
}
/*
class TShip
*/
TShip::TShip()
{
}
TShip::TShip(TShipType aType)
{
__ASSERT_DEBUG((aType>=EBattleship && aType<=EFrigate), Panic(EBadShipType));
iType=aType;
iLength=
aType==EBattleship ? 4 :
aType==ECruiser ? 3 :
aType==EDestroyer ? 2 :
1;
iRemaining=iLength;
}
// persistence
void TShip::ExternalizeL(RWriteStream& aStream) const
{
aStream.WriteUint8L(iType);
aStream.WriteInt8L(iLength);
aStream.WriteInt8L(iRemaining);
aStream.WriteInt8L(iStartX);
aStream.WriteInt8L(iStartY);
aStream.WriteInt8L(iDx);
aStream.WriteInt8L(iDy);
}
void TShip::InternalizeL(RReadStream& aStream)
{
iType=(TShipType)aStream.ReadUint8L();
iLength=aStream.ReadInt8L();
iRemaining=aStream.ReadInt8L();
iStartX=aStream.ReadInt8L();
iStartY=aStream.ReadInt8L();
iDx=aStream.ReadInt8L();
iDy=aStream.ReadInt8L();
}
/*
class TFleet
*/
// setup
TFleet::TFleet()
{
SetupBlank();
iMyFleetFlag=EFalse;
}
void TFleet::SetupBlank()
{
for (TInt x=0; x<8; x++)
for (TInt y=0; y<8; y++)
Square(x,y)=EUnknown;
iRemainingSquares=20;
iKnownShips=0;
iRemainingShips=10;
iSquaresHit=0;
}
void TFleet::SetupRandom()
{
// try to place each ship
User::After(1);
TTime now;
now.HomeTime();
iRandomSeed=now.Int64();
// now try placing, up to 10,000 times
TInt shipsPlaced=0;
for (TInt attempts=0; attempts<10000; attempts++)
{
SetupBlank(); // blank everything
shipsPlaced=0; // no ships placed yet
for (TInt ship=0; ship<10; ship++) // try placing 10
{
TShip::TShipType shipType=
ship<1 ? TShip::EBattleship :
ship<3 ? TShip::ECruiser :
ship<6 ? TShip::EDestroyer :
TShip::EFrigate;
if (!TryPlaceShip(ship,shipType)) break; // if couldn't place, do another attempt
shipsPlaced++; // one more ship placed
}
if (shipsPlaced==10)
break; // all ships placed - break
}
// check whether we placed all ships, or ran out of attempts
__ASSERT_ALWAYS(shipsPlaced==10, Panic(EShipsNotAllPlaced));
iKnownShips=10;
// set remaining squares to sea
for (TInt x=0; x<8; x++)
for (TInt y=0; y<8; y++)
if (Square(x,y)==EUnknown)
Square(x,y)=ESea;
}
void TFleet::SetMyFleet()
{
iMyFleetFlag=ETrue;
}
void TFleet::SetOppFleet()
{
iMyFleetFlag=EFalse;
}
// interrogators
TBool TFleet::IsMyFleet() const
{
return iMyFleetFlag;
}
TBool TFleet::IsOppFleet() const
{
return !iMyFleetFlag;
}
TBool TFleet::IsShip(TInt aX, TInt aY) const
{
TInt state=Square(aX, aY) & ~EHit;
return state>=EBattleship && state<=EFrigate;
}
TShip::TShipType TFleet::ShipType(TInt aX, TInt aY) const
{
TInt state=Square(aX, aY) & ~EHit;
return TShip::TShipType(state);
}
TBool TFleet::IsSea(TInt aX, TInt aY) const
{
TInt state=Square(aX, aY) & ~EHit;
return state==ESea;
}
TBool TFleet::IsKnown(TInt aX, TInt aY) const
{
TInt state=Square(aX, aY) & ~EHit;
return state!=EUnknown;
}
TBool TFleet::IsHit(TInt aX, TInt aY) const
{
TInt state=Square(aX, aY) & EHit;
return state==EHit;
}
TBool TFleet::SquaresHit() const
{
return iSquaresHit;
}
TShip& TFleet::ShipAt(TInt aX, TInt aY) const // useful if there's definitely a ship there
{
__ASSERT_DEBUG(IsMyFleet(), Panic(EShipAtNotMyFleet));
TShip* ship=PossibleShipAt(aX,aY);
__ASSERT_ALWAYS(ship,Panic(EShipAtShipNotFound));
return *ship;
}
TShip* TFleet::PossibleShipAt(TInt aX, TInt aY) const // useful if not sure if whole ship there
{
__ASSERT_DEBUG(IsShip(aX, aY), Panic(EShipAtNoShip));
for (TInt shipIndex=0; shipIndex<iKnownShips; shipIndex++) // all ships
{
const TShip& ship=iShips[shipIndex];
for (TInt squareIndex=0; squareIndex<ship.iLength; squareIndex++) // each square in the ship
{
if (ship.iStartX+squareIndex*ship.iDx==aX && ship.iStartY+squareIndex*ship.iDy==aY)
{ // this square is the one we were looking at, so return this ship
return const_cast<TShip*>(&ship);
}
}
}
return 0;
}
TInt TFleet::RemainingSquares() const // fleet squares which haven't been destroyed
{
return iRemainingSquares;
}
TInt TFleet::RemainingShips() const // ships which haven't been totally destroyed
{
return iRemainingShips;
}
// change
void TFleet::SetSea(TInt aX, TInt aY) // say a square is sea
{
Square(aX, aY) = ESea;
}
void TFleet::SetShipType(TInt aX, TInt aY, TShip::TShipType aShipType) // set ship type
{
Square(aX, aY)=(TSquareState) aShipType;
}
TFleet::THitResult TFleet::SetHit(TInt aX, TInt aY) // hit a square - must be known when it's hit
{
__ASSERT_DEBUG(IsKnown(aX, aY), Panic(EHitUnknownSquare));
THitResult aHitResult = EMiss;
if (IsHit(aX, aY)) return EVoid;
Square(aX, aY) = TSquareState(Square(aX,aY) | EHit);
iSquaresHit++; // total squares hit
if (IsShip(aX, aY)) // hit a ship: register consequences
{
aHitResult = EShip;
iRemainingSquares--; // one less remaining square
if (IsMyFleet()) // my fleet: find affected ship
{
TShip& ship=ShipAt(aX, aY);
ship.iRemaining--; // one less square in ship
if (ship.iRemaining==0)
{
iRemainingShips--; // one less ship in fleet
aHitResult = ESunk;
}
}
else
{
aHitResult = TestWholeShip(aX,aY); // see if we have a whole ship here
SetSeaAroundHit(aX,aY); // set sea around hit (and maybe whole ship)
}
}
return aHitResult;
}
TFleet::THitResult TFleet::TestWholeShip(TInt aX, TInt aY)
{
__ASSERT_DEBUG(IsHit(aX,aY), Panic(EMarkUnHitSquare));
// search in all directions for adjacent ships
TInt x, y; // point we'll scan with
TInt dx=0, dy=0; // direction we're scanning in
TInt squares=1; // number of linear adjacent ship squares
for (TInt i=0; i<4; i++)
{
// choose a direction vector
const TInt dxInit[4]={1,0,-1,0};
const TInt dyInit[4]={0,1,0,-1};
dx=dxInit[i], dy=dyInit[i];
// scan along that vector
x=aX+dx, y=aY+dy;
while (x>=0 && x<8 && y>=0 && y<8 && IsShip(x,y))
{
squares++;
x+=dx;
y+=dy;
}
if (squares>1) break; // interesting if found something
}
// quit if nothing found (and we're not on a frigate)
if (squares==1 && ShipType(aX,aY)!=TShip::EFrigate)
return EShip;
// now scan in opposite direction
dx=-dx, dy=-dy;
x=aX+dx, y=aY+dy;
while (x>=0 && x<8 && y>=0 && y<8 && IsShip(x,y))
{
squares++;
x+=dx;
y+=dy;
}
// test whether squares is correct amount
// our test here trusts that the opponent's board is set up properly!
TShip::TShipType shipType=ShipType(aX,aY);
if (!(
squares==1 && shipType==TShip::EFrigate ||
squares==2 && shipType==TShip::EDestroyer ||
squares==3 && shipType==TShip::ECruiser ||
squares==4 && shipType==TShip::EBattleship
))
return EShip;
// we must have sunk and destroyed an entire ship
TShip ship(shipType);
ship.iRemaining=0;
ship.iDx=-dx;
ship.iDy=-dy;
ship.iStartX=x-dx;
ship.iStartY=y-dy;
iShips[iKnownShips++]=ship;
iRemainingShips--;
return ESunk;
}
void TFleet::SetSeaAroundHit(TInt aX, TInt aY)
{
__ASSERT_DEBUG(IsHit(aX,aY), Panic(EMarkUnHitSquare));
TInt x, y; // for walking
TInt dx, dy; // direction vectors
// mark diagonal squares
for (dx=-1; dx<=1; dx+=2)
{
x=aX+dx;
if (x<0 || x>=8) continue;
for (dy=-1; dy<=1; dy+=2)
{
y=aY+dy;
if (y<0 || y>=8) continue;
if (!IsKnown(x,y))
SetSea(x,y);
}
}
// test whether to mark all around ship
const TShip* ship=PossibleShipAt(aX,aY);
if (!ship)
return;
// mark all around ship
TInt sx, sy; // ship elements
sx=ship->iStartX;
sy=ship->iStartY;
for (TInt s=0; s<ship->iLength; s++, sx+=ship->iDx, sy+=ship->iDy)
{
for (dx=-1; dx<=1; dx++)
{
x=sx+dx;
if (x<0 || x>=8) continue;
for (dy=-1; dy<=1; dy++)
{
y=sy+dy;
if (y<0 || y>=8) continue;
if (!IsKnown(x,y))
SetSea(x,y);
}
}
}
}
// setup
TBool TFleet::TryPlaceShip(TInt aShipIndex, TShip::TShipType aShipType)
{
TShip ship(aShipType); // initialize length, type and remaining squares
for (TInt attempt=0; attempt<20; attempt++)
{
// select starting coordinates
TInt coord1=TInt(Math::FRand(iRandomSeed)*(8-ship.iLength)); // 0..8-length
TInt coord2=TInt(Math::FRand(iRandomSeed)*8); // 0..7
// sanity check in case Math::FRand() returned 1
if (coord1+ship.iLength==8 || coord2==8)
continue;
// select orientation and set coords appropriately
if (Math::Rand(iRandomSeed) & 0x40000000)
{ // east-west
ship.iStartX=coord1;
ship.iDx=1;
ship.iStartY=coord2;
ship.iDy=0;
}
else
{ // north-south
ship.iStartX=coord2;
ship.iDx=0;
ship.iStartY=coord1;
ship.iDy=1;
}
// check all squares are free
TBool allFree=ETrue;
for (TInt square=0; square<ship.iLength; square++)
{
if (Square(ship.iStartX+square*ship.iDx,ship.iStartY+square*ship.iDy)!=EUnknown)
{
allFree=EFalse;
if (ship.iType==TShip::EBattleship)
User::Panic(_L("DEBUG"),0);
break;
}
}
// if all free then we have success
if (allFree)
{
PlaceShip(aShipIndex,ship); // place into position and surround by sea
return ETrue; // indicate success
}
}
// failed after 20 attempts - start again
return EFalse; // indicate failure
}
void TFleet::PlaceShip(TInt aShipIndex, TShip aShip)
{
for (TInt square=0; square<aShip.iLength; square++)
{
// calculate square and set it to ship
TInt x=aShip.iStartX+square*aShip.iDx;
TInt y=aShip.iStartY+square*aShip.iDy;
Square(x,y)=TSquareState(aShip.iType);
// set surrounding squares to sea
for (TInt dx=-1; dx<=1; dx++)
{
TInt xs=x+dx;
if (xs<0 || xs>=8)
continue; // don't write over edges
for (TInt dy=-1; dy<=1; dy++)
{
TInt ys=y+dy;
if (ys<0 || ys>=8)
continue; // don't write over edges
if (Square(xs,ys)!=EUnknown)
continue; // don't write over existing ship (or sea)
// set sea
Square(xs,ys)=ESea;
}
}
}
iShips[aShipIndex]=aShip; // binary copy the new ship into place
}
// square accessors
const TFleet::TSquareState& TFleet::Square(TInt aX, TInt aY) const
{
__ASSERT_DEBUG((aX>=0 && aX<8 && aY>=0 && aY<8), Panic(EBadSquare));
return iSquares[aX+8*aY];
}
TFleet::TSquareState& TFleet::Square(TInt aX, TInt aY)
{
__ASSERT_DEBUG((aX>=0 && aX<8 && aY>=0 && aY<8), Panic(EBadSquare));
return iSquares[aX+8*aY];
}
// persistence
void TFleet::ExternalizeL(RWriteStream& aStream) const
{
TInt i;
for (i=0; i<64; i++)
aStream.WriteUint8L(iSquares[i]);
aStream.WriteUint8L(iMyFleetFlag);
for (i=0; i<10; i++)
aStream << iShips[i];
aStream.WriteInt8L(iKnownShips);
aStream.WriteInt8L(iRemainingShips);
aStream.WriteInt8L(iRemainingSquares);
aStream.WriteInt8L(iSquaresHit);
}
void TFleet::InternalizeL(RReadStream& aStream)
{
TInt i;
for (i=0; i<64; i++)
iSquares[i]=(TSquareState) aStream.ReadUint8L();
iMyFleetFlag=aStream.ReadUint8L();
for (i=0; i<10; i++)
aStream >> iShips[i];
iKnownShips=aStream.ReadInt8L();
iRemainingShips=aStream.ReadInt8L();
iRemainingSquares=aStream.ReadInt8L();
iSquaresHit=aStream.ReadInt8L();
}
/*
class CGameEngine
*/
// setup
CGameEngine::CGameEngine()
{
Reset();
}
void CGameEngine::Reset()
{
iMyFleet.SetupRandom();
iMyFleet.SetMyFleet();
iOppFleet.SetupBlank();
iOppFleet.SetOppFleet();
//SetFirstPlayer();
}
// set up turns
void CGameEngine::SetFirstPlayer()
{
iFirstPlayer=ETrue;
}
void CGameEngine::SetSecondPlayer()
{
iFirstPlayer=EFalse;
}
// interrogate
TBool CGameEngine::IsWon() const
{
return iOppFleet.RemainingSquares()==0;
}
TBool CGameEngine::IsLost() const
{
return iMyFleet.RemainingSquares()==0;
}
TBool CGameEngine::IsStarted() const
{
return ETrue;
}
TBool CGameEngine::IsMyTurn() const
{
if (iFirstPlayer)
return iMyFleet.SquaresHit()==iOppFleet.SquaresHit(); // I initiate
else
return iMyFleet.SquaresHit()>iOppFleet.SquaresHit(); // I follow
}
// persistence
void CGameEngine::ExternalizeL(RWriteStream& aStream) const
{
aStream << iMyFleet;
aStream << iOppFleet;
aStream.WriteUint8L(iFirstPlayer);
}
void CGameEngine::InternalizeL(RReadStream& aStream)
{
Reset();
aStream >> iMyFleet;
aStream >> iOppFleet;
iFirstPlayer=aStream.ReadUint8L();
}
TStreamId CGameEngine::StoreL(CStreamStore& aStore) const
{
RStoreWriteStream stream;
TStreamId id = stream.CreateLC(aStore);
ExternalizeL(stream);
stream.CommitL();
CleanupStack::PopAndDestroy(); // stream
return id;
}
void CGameEngine::RestoreL(const CStreamStore& aStore, TStreamId aStreamId)
{
RStoreReadStream stream;
stream.OpenLC(aStore,aStreamId);
InternalizeL(stream);
CleanupStack::PopAndDestroy(); // stream
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -