// POTM ENTRY: 6.5 KillerBytes
// Don Kirkby
// dkirkby@sierrasys.com
// This program sits on its home square and tries to annoy the 
// opponent by putting walls in its way. When the opponent gets
// within range, it fires down the side closest to the opponent
// until it hits the opponent or enough turns pass that the
// opponent could be sneaking up on the other side. If the
// opponent could be sneaking up on the other side the program
// begins alternating fire down both sides of the arena. If it 
// hits the opponent, it keeps firing until the game ends.

#include 
#include 
#include 
#include 
#include 

const cntRows = 9, cntCols = 26;

// State variables for reading and writing temp file.
class CState{
public:
	CState(const char *pszFilename);
	~CState();

	// global
	int GetRow() { return m_iRow; }
	void SetRow(int iRow) { m_iRow = iRow; }
	int GetCol() { return m_iCol; }
	void SetCol(int iCol) { m_iCol = iCol; }
	int GetDestRow() { return m_iDestRow; }
	int GetDestCol() { return m_iDestCol; }
	void SetDest(int iRow, int iCol) 
	  { m_iDestRow = iRow; m_iDestCol = iCol; }
	// direction towards destination
	int GetDir() { return m_iDestRow ? 1 : -1; } 

	void WriteGlobals();
	void ReadGlobals();

	// state specific.
	void WriteState(const char *pszStr);
	void WriteState(int i);

	void ReadState(char *pszBuf, int cntBufSize);
	void ReadState(int &i);

protected:
	int m_iRow, m_iCol, m_iDestRow, m_iDestCol;
	int m_bGlobalsWritten;
	char *m_pszFilename;
	ifstream *m_pInFile;
	ofstream *m_pOutFile;
};
static CState *pState = 0;

// Convert between "A 1" and 0, 0
ostream &WriteRowCol(ostream &os, int iRow, int iCol);
istream &ReadRowCol(istream &is, int &iRow, int &iCol);

// State processing functions:
// One of these is called when the program starts based on what
// state was stored in the temp file.
// Init: First move, no temp file
void InitState();

// Radar: RADAR was called last turn
void RadarState(int bFirstTurn = 0);

// Block: WALL was called last turn
void BlockState();

// Zap: ZAP was called last turn
void ZapState();


// This will call ZAP based on where the opponent was last seen
// and how long it has been since it was there.
void Zap(int iRadarRow, int iRadarCol, int nMovesSinceRadar);

int main(int argc, char **argv)
{
	pState = new CState(argv[1]);
	pState->ReadGlobals();

	char szState[10];
	szState[0] = '\0';
	pState->ReadState(szState, 10);

	if( !strcmp(szState, "") )
		InitState();
	else if( !strcmp(szState, "Radar") )
		RadarState();
	else if( !strcmp(szState, "Block") )
		BlockState();
	else if( !strcmp(szState, "Zap") )
		ZapState();
	else
		cout << "HELP";

	delete pState;
	return 0;
}

void InitState()
{
	int iRow, iCol;
	char szInit[5];
	// the setw probably doesn't do anything on input
	ReadRowCol(cin >> setw(5) >> szInit, iRow, iCol);

	pState->SetRow(iRow);
	pState->SetCol(iCol);
	if( iRow == 0 )
		pState->SetDest(8, 25);
	else
		pState->SetDest(0, 0);

	RadarState(1);
}

void RadarState(int bFirstTurn)
{
	int iTgtRow, iTgtCol, iDir = pState->GetDir();
	if( bFirstTurn ){
		iTgtRow = pState->GetDestRow();
		iTgtCol = pState->GetDestCol();
		if( iTgtRow ) 
		    // we go first, we know exactly where opponent is.
			iTgtCol++;
	}else{
		char szAt[3];
		ReadRowCol(cin >> setw(3) >> szAt, iTgtRow, iTgtCol);
	}
	
	// calculate minimum turns including this one before 
	// opponent is within range.
	int dRow = abs(iTgtRow - pState->GetRow()), 
		dCol = abs(iTgtCol - pState->GetCol());
	int nMinTurns = (dRow < dCol ? dRow : dCol) - 1;
	if( nMinTurns > 0 ){
		WriteRowCol(cout << "WALL ", iTgtRow, iTgtCol - 2*iDir);
		// opponent has had one move since radar call
		pState->WriteState("Block 1"); 
		pState->WriteState(nMinTurns);
		pState->WriteState(iTgtRow);
		pState->WriteState(iTgtCol);
	}else{
		// opponent has had one move since radar call
		Zap(iTgtRow, iTgtCol, 1); 
	}
}

void BlockState()
{
	int nMinTurns, iDir = pState->GetDir(), nMovesSinceRadar, 
		iTgtRow, iTgtCol;
	pState->ReadState(nMovesSinceRadar);
	pState->ReadState(nMinTurns);
	pState->ReadState(iTgtRow);
	pState->ReadState(iTgtCol);
	nMinTurns--;
	nMovesSinceRadar++;
	if( nMinTurns && nMovesSinceRadar == 2){
		// if opponent's still out of range, put a
		// second wall below it.
		WriteRowCol(cout << "WALL ", iTgtRow-iDir, 
			iTgtCol-iDir);
		// actually should be Block 2 but I'm not 
		// allowed to change logic now :-)
		pState->WriteState("Block 0");
		pState->WriteState(nMinTurns);
		pState->WriteState(iTgtRow);
		pState->WriteState(iTgtCol);
	}else if( nMinTurns ){
		cout << "RADAR";
		pState->WriteState("Radar");
	}else
		Zap(iTgtRow, iTgtCol, nMovesSinceRadar);
}

void Zap(int iRadarRow, int iRadarCol, int nMovesSinceRadar)
{
	int iRow = pState->GetRow(), iCol = pState->GetCol();
	int nRows = abs(iRadarRow - iRow), 
		nCols = abs(iRadarCol - iCol);
	int iTgtRow, iTgtCol, cntShots;

	// aim along row or column and calculate number of shots
	// before opponent could sneak up on the other side.
	if( nRows <= nCols ){
		iTgtRow = iRow;
		iTgtCol = iCol + pState->GetDir();
		cntShots = nCols - nMovesSinceRadar;
	}else{
		iTgtRow = iRow + pState->GetDir();
		iTgtCol = iCol;
		cntShots = nRows - nMovesSinceRadar;
	}
	WriteRowCol(cout << "ZAP ", iTgtRow, iTgtCol);
	pState->WriteState("Zap");
	pState->WriteState(iTgtRow);
	pState->WriteState(iTgtCol);
	pState->WriteState(cntShots);
}

void ZapState()
{
	char szResult[10];
	cin >> setw(10) >> szResult;
	int iTgtRow, iTgtCol, cntShots;
	pState->ReadState(iTgtRow);
	pState->ReadState(iTgtCol);
	pState->ReadState(cntShots);
	cntShots--;

	if( !strcmp(szResult, "HIT") || cntShots > 0 ){
		; // do nothing shoot again in the same direction
	}else if( iTgtRow == pState->GetRow() ){
		// previous shot was horizontal, shoot vertically
		iTgtRow = pState->GetRow() + pState->GetDir();
		iTgtCol = pState->GetCol();
		cntShots = 1;
	}else{
		// previous shot was vertical, shoot horizontally
		iTgtRow = pState->GetRow();
		iTgtCol = pState->GetCol() + pState->GetDir();
		cntShots = 1;
	}
	WriteRowCol(cout << "ZAP ", iTgtRow, iTgtCol);
	pState->WriteState("Zap");
	pState->WriteState(iTgtRow);
	pState->WriteState(iTgtCol);
	pState->WriteState(cntShots);
}

// CState implementation
CState::CState(const char *pszFilename)
{
	m_pszFilename = new char[strlen(pszFilename)+1];
	strcpy(m_pszFilename, pszFilename);
	m_bGlobalsWritten = 0;
	m_iCol = 0;
	m_iRow = 0;
	m_pInFile = 0;
	m_pOutFile = 0;
}

CState::~CState()
{
	if( !m_bGlobalsWritten )
		WriteGlobals();
	if( m_pInFile ){
		m_pInFile->close();
		delete m_pInFile;
	}
	if( m_pOutFile ){
		m_pOutFile->close();
		delete m_pOutFile;
	}
}

// global
void CState::ReadGlobals()
{
	m_pInFile = new ifstream(m_pszFilename, 
		ios::in|ios::nocreate);
	if(m_pInFile && m_pInFile->good() ){
		*m_pInFile >> m_iRow >> m_iCol >> m_iDestRow 
			>> m_iDestCol;
	}
}

void CState::WriteGlobals()
{
	if(m_pInFile ){
		m_pInFile->close();
		delete m_pInFile;
		m_pInFile = 0;
	}

	m_pOutFile = new ofstream(m_pszFilename);
	if( m_pOutFile && m_pOutFile->good() ){
		*m_pOutFile << m_iRow << ' ' << m_iCol << ' ' 
			<< m_iDestRow << ' ' << m_iDestCol << ' ';
	}
	m_bGlobalsWritten = 1;
}

// state specific.
void CState::WriteState(const char *pszStr)
{
	if( !m_bGlobalsWritten )
		WriteGlobals();

	if( m_pOutFile && m_pOutFile->good() )
		*m_pOutFile << pszStr << ' ';
}

void CState::WriteState(int i)
{
	if( !m_bGlobalsWritten )
		WriteGlobals();

	if( m_pOutFile && m_pOutFile->good() )
		*m_pOutFile << i << ' ';
}

void CState::ReadState(char *pszBuf, int cntBufSize)
{
	if( m_pInFile && m_pInFile->good() )
		*m_pInFile >> setw(cntBufSize-1) >> pszBuf;
	else
		*pszBuf = '\0';
}

void CState::ReadState(int &i)
{
	if( m_pInFile && m_pInFile->good() )
		*m_pInFile >> i;
	else
		i = 0;
}

ostream &WriteRowCol(ostream &os, int iRow, int iCol)
{
	if( os.good() )
		os << (char)(iRow + 'A') << ' ' << iCol+1 << ' ';
	return os;
}

istream &ReadRowCol(istream &is, int &iRow, int &iCol)
{
	if( is.good() ){
		char cRow;
		is >> cRow >> iCol;
		iRow = cRow - 'A';
		iCol--;
	}else{
		iRow = 0;
		iCol = 0;
	}
	return is;
}