COMPUTER.CPP
// Filename: Computer.cpp 
// 
// Description:  Implementation of CComputer 
// This file contains the code that handles the interaction between a human 
// player and a computer player. 
// 
// This file is provided as part of the Microsoft Transaction Server Samples 
// 
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT  
// WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,  
// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES  
// OF MERCHANTABILITY AND/OR FITNESS FOR A  PARTICULAR  
// PURPOSE. 
// 
// Copyright (C) 1997 Microsoft Corporation, All rights reserved 
 
#include "stdafx.h" 
#include "tServer.h" 
#include "Computer.h" 
 
#include <mtx.h> 
#include <mtxspm.h> 
#include <time.h> 
 
///////////////////////////////////////////////////////////////////////////// 
// 
 
STDMETHODIMP CComputer::InterfaceSupportsErrorInfo(REFIID riid) 
{ 
static const IID* arr[] =  
{ 
&IID_IComputer, 
}; 
 
for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++) 
{ 
if (InlineIsEqualGUID(*arr[i],riid)) 
return S_OK; 
} 
return S_FALSE; 
} 
 
 
STDMETHODIMP CComputer::AddNewGame (IN VARIANT_BOOL bEasy, OUT VARIANT* pvGameID, OUT VARIANT* pvOrder, 
OUT VARIANT* pvX, OUT VARIANT* pvY) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
 
ISharedProperty* spmPropCounter = NULL; 
ISharedProperty* spmPropState = NULL; 
 
pvGameID->vt = VT_I4; 
pvOrder->vt = VT_I4; 
pvX->vt = VT_I4; 
pvY->vt = VT_I4; 
 
long lGameID = 0; 
long lOrder = 0; 
long lNewX = 0; 
long lNewY = 0; 
 
 
try { 
 
// Get the object context 
THROW_ERR ( hr = GetObjectContext(&pObjectContext) ); 
if (pObjectContext == NULL) { 
THROW_ERR ( E_FAIL ); 
} 
 
// Create the SharedPropertyGroupManager 
THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr) ); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup) ); 
 
// Create the counter properties 
THROW_ERR ( spmGroup->CreateProperty (L"Counter", &bExists, &spmPropCounter) ); 
 
// Obtain the current value of the counter 
CComVariant vCounter; 
THROW_ERR ( spmPropCounter->get_Value (&vCounter) ); 
 
// Assign gameID 
if (vCounter.lVal == 0) 
vCounter.lVal = 1; 
 
lGameID = vCounter.lVal; 
 
// Update the counter 
vCounter.lVal ++; 
THROW_ERR ( spmPropCounter->put_Value (vCounter) ); 
 
// Set the state variable 
TCHAR szBuf [512]; 
BSTR bstrState; 
 
wsprintf (szBuf, _T("%dState"), lGameID); 
bstrState = TCHAR2BSTR (szBuf); 
THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) ); 
 
CComVariant vState; 
vState.vt = VT_I4; 
vState.lVal = 4; 
THROW_ERR ( spmPropState->put_Value (vState) ); 
 
// Initialize the array 
InitArray(); 
THROW_ERR ( SaveArray(lGameID) ); 
 
// Assign order randomly 
srand ((unsigned) time (NULL)); 
lOrder = rand() % 2 + 1; 
 
// Get computer's first move if player's order is 2 
if (lOrder == 2) { 
GetMove (lGameID, bEasy, lNewX, lNewY); 
plField [lNewX][lNewY] = 2; 
SaveArray(lGameID); 
} 
 
// Prepare return values 
pvX->lVal = lNewX; 
pvY->lVal = lNewY; 
pvOrder->lVal = lOrder; 
pvGameID->lVal = lGameID; 
 
// We're finished and happy 
pObjectContext->SetComplete(); 
 
} catch (HRESULT hr) { 
 
// Create an ErrorInfo object 
ICreateErrorInfo* pCreateErrInfo = NULL; 
IErrorInfo* pErrInfo = NULL; 
 
CreateErrorInfo (&pCreateErrInfo); 
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo); 
 
// Fill in error information 
TCHAR szErr [512]; 
wsprintf (szErr, _T("Error %d occurred in Computer::AddNewGame()"), hr); 
BSTR bstrDesc = TCHAR2BSTR (szErr); 
pCreateErrInfo->SetGUID (IID_IComputer); 
pCreateErrInfo->SetSource (L"Computer"); 
pCreateErrInfo->SetDescription (bstrDesc); 
::SysFreeString (bstrDesc); 
 
// Confirm error information 
SetErrorInfo (0, pErrInfo); 
 
// Clean up the error objects 
if (pCreateErrInfo) 
pCreateErrInfo->Release(); 
 
if (pErrInfo) 
pErrInfo->Release(); 
 
// Indicate our unhappiness 
if (pObjectContext) 
pObjectContext->SetAbort(); 
 
hr = E_FAIL; 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
if (spmPropCounter) 
spmPropCounter->Release(); 
 
if (spmPropState) 
spmPropState->Release(); 
 
return hr; 
} 
 
 
STDMETHODIMP CComputer::NewMove (IN long lGameID, IN VARIANT_BOOL bEasy, IN long lX, IN long lY, 
 OUT VARIANT* pvMyX, OUT VARIANT* pvMyY, OUT VARIANT* pvWin) { 
// Win protocol 
// ============ 
// 0  -> moves did not end game 
// 1  -> player won 
// -1 -> computer won 
// 2  -> player's move tied the game 
// -2 -> computer's move tied the game 
 
HRESULT hr = S_OK; 
 
pvMyX->vt = VT_I4; 
pvMyY->vt = VT_I4; 
pvWin->vt = VT_I4; 
 
long lTurns = 0; 
long lWin = 0; 
long lNewX = 0; 
long lNewY = 0; 
 
IObjectContext* pObjectContext = NULL; 
 
 
try { 
 
// Get the object context 
THROW_ERR ( hr = GetObjectContext(&pObjectContext) ); 
if (pObjectContext == NULL) { 
THROW_ERR ( E_FAIL ); 
} 
 
THROW_ERR ( LoadArray(lGameID) );  
 
// Enter new move 
plField [lX][lY] = 1; 
 
// Check for player's win 
if (IsWin (1)) { 
 
// Player won 
lWin = 1; 
 
} else { 
 
// Check for fullness 
lTurns = HowManyTurns(); 
 
if (lTurns == 9) { 
 
// Tie 
lWin = - 2; 
 
} else { 
 
// Get computer's move 
GetMove (lGameID, bEasy, lNewX, lNewY); 
plField[lNewX][lNewY] = 2; 
 
// Check for computer win 
if (IsWin(2)) 
 
lWin = - 1; 
 
else 
 
// Check for fullness 
if (lTurns == 8) 
lWin = 2; 
} 
} 
 
// Save array 
THROW_ERR ( SaveArray(lGameID) ); 
 
// Set return values 
pvMyX->lVal = lNewX; 
pvMyY->lVal = lNewY; 
pvWin->lVal = lWin; 
 
// We're finished and happy 
pObjectContext->SetComplete(); 
 
} catch (HRESULT hr) { 
 
// Create an ErrorInfo object 
ICreateErrorInfo* pCreateErrInfo = NULL; 
IErrorInfo* pErrInfo = NULL; 
 
CreateErrorInfo (&pCreateErrInfo); 
pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo); 
 
// Fill in error information 
TCHAR szErr[512]; 
wsprintf (szErr, _T("Error %d occurred in Computer::NewMove()"), hr); 
BSTR bstrDesc = TCHAR2BSTR (szErr); 
pCreateErrInfo->SetGUID (IID_IComputer); 
pCreateErrInfo->SetSource (L"Computer"); 
pCreateErrInfo->SetDescription (bstrDesc); 
::SysFreeString (bstrDesc); 
 
// Confirm error information 
SetErrorInfo (0, pErrInfo); 
 
// Clean up the error objects 
if (pCreateErrInfo) 
pCreateErrInfo->Release(); 
 
if (pErrInfo) 
pErrInfo->Release(); 
 
// Indicate our unhappiness 
if (pObjectContext) 
pObjectContext->SetAbort(); 
 
hr = E_FAIL; 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
return hr; 
} 
 
 
void CComputer::GetMove (long lGameID, VARIANT_BOOL bEasy, long& lX, long& lY) { 
 
long lFlag = 0; 
long lNewX = 0; 
long lNewY = 0; 
 
// Perhaps there's only possible move 
if (HowManyTurns() == 8) { 
 
// Scan for open tile 
long i,j; 
for (i = 0; i < 3; i ++) { 
for (j = 0; j < 3; j ++) { 
if (plField[i][j] == 0) { 
lNewX = i; 
lNewY = j; 
lFlag = 1; 
} 
} 
} 
} 
 
// Check for win 
if (lFlag == 0) 
lFlag = LineScan (2, lNewX, lNewY); 
 
// Check for block 
if (lFlag == 0) 
lFlag = LineScan (1, lNewX, lNewY); 
 
// Else create a new move 
if (lFlag == 0) { 
 
// Prepare random seed 
srand ((unsigned) time (NULL)); 
 
// Decide intelligence level 
if (!bEasy) { 
 
// Place in center if it's open 
if (plField[1][1] == 0) { 
lNewX = 1; 
lNewY = 1; 
lFlag = 1; 
 
} else { 
 
// Place on corner, if opponent placed first move in center 
long lTurns = HowManyTurns(); 
if (lTurns == 1 && plField[1][1] > 0) { 
lNewX = (rand() % 2 == 0) ? 0:2; 
lNewY = (rand() % 2 == 0) ? 0:2; 
lFlag = 1; 
} else { 
 
// Place on opposite tile, if opponent's second 
// move was placed on diagonal 
if (lTurns == 2) { 
if (plField[0][0] == 1) { 
lNewX = 2; 
lNewY = 2; 
lFlag = 1; 
} 
 
if (plField[0][2] == 1) { 
lNewX = 2; 
lNewY = 0; 
lFlag = 1; 
} 
 
if (plField[2][0] == 1) { 
lNewX = 0; 
lNewY = 2; 
lFlag = 1; 
} 
 
if (plField[2][2] == 1) { 
lNewX = 0; 
lNewY = 0; 
lFlag = 1; 
} 
} 
} 
} 
 
// Else find most intelligent move 
if (lFlag == 0) { 
 
float value = 0; 
float maxValue = 0; 
 
for (long i = 0; i < 3; i ++) { 
for (long j = 0; j < 3; j ++) { 
 
// Evaluate tile 
value = Evaluate (i, j); 
 
// Randomize equal choices 
if (value == maxValue && rand() % 2 == 0) { 
 
lNewX = i; 
lNewY = j; 
 
} else { 
 
if (value > maxValue) { 
maxValue = value; 
lNewX = i; 
lNewY = j; 
} 
} 
} 
} 
} 
 
} else { 
 
// Choose a random slot 
while (lFlag == 0) { 
 
lNewX = rand() % 3; 
lNewY = rand() % 3; 
 
if (plField [lNewX][lNewY] == 0) 
lFlag = 1; 
} 
} 
} 
 
// Prepare return values 
lX = lNewX; 
lY = lNewY; 
} 
 
float CComputer::Evaluate (long lX, long lY) { 
 
long lNewX, lNewY, lNewX2, lNewY2; 
float fEval1, fEval2, fEval3; 
 
if (plField[lX][lY] != 0) { 
 
lX = - 100; 
lY = - 100; 
return - 100; 
} else { 
 
// Evaluate defensive options 
plField[lX][lY] = 1; 
fEval2 = (float) (LineScan (1, lNewX, lNewY) / 3); 
 
// Evaluate offensive options 
plField[lX][lY] = 2; 
fEval1 = (float) LineScan (2, lNewX, lNewY); 
 
// Evaluate opponent's follow-up 
if (fEval1 > 0) { 
plField[lNewX][lNewY] = 1; 
fEval3 = (float) (LineScan (1, lNewX2, lNewY2) * 0.9); 
plField[lNewX][lNewY] = 0; 
} else 
fEval3 = 0; 
 
// Restore tiles and return final evaluation 
plField[lX][lY] = 0; 
return fEval1 + fEval2 - fEval3; 
} 
} 
 
 
bool CComputer::IsWin (long lPlayer) { 
 
bool bWin = false; 
 
for (int i = 0; i < 3; i ++) { 
if (plField [i][0] == plField [i][1] && plField [i][1] == plField [i][2] && plField [i][0] == lPlayer) 
bWin = true; 
if (plField [0][i] == plField [1][i] && plField [1][i] == plField [2][i] && plField [0][i] == lPlayer) 
bWin = true; 
} 
 
if (plField [0][0] == plField [1][1] && plField [1][1] == plField [2][2] && plField [1][1] == lPlayer) 
bWin = true; 
if (plField [2][0] == plField [1][1] && plField [1][1] == plField [0][2] && plField [1][1] == lPlayer) 
bWin = true; 
 
return bWin; 
} 
 
 
long CComputer::HowManyTurns () { 
 
long lTurns = 0; 
 
for (int i = 0; i < 3; i ++) { 
for (int j = 0; j < 3; j ++) { 
if (plField[i][j] > 0) { 
lTurns ++; 
} 
} 
} 
 
return lTurns; 
} 
 
 
long CComputer::LineScan (long lPlayer, long& lX, long& lY) { 
 
long lCounter = 0; 
lX = - 100; 
lY = - 100; 
 
// Horizontal lines 
for (int i = 0; i < 3; i ++) { 
 
if (plField [1][i] == lPlayer && plField [0][i] == lPlayer && plField[2][i] == 0) { 
lX = 2; 
lY = i; 
lCounter ++; 
} 
 
if (plField [0][i] == lPlayer && plField [2][i] == lPlayer && plField[1][i] == 0) { 
lX = 1; 
lY = i; 
lCounter ++; 
} 
 
if (plField [1][i] == lPlayer && plField [2][i] == lPlayer && plField[0][i] == 0) { 
lX = 0; 
lY = i; 
lCounter ++; 
} 
} 
 
// Vertical lines 
for (i = 0; i < 3; i ++) { 
 
if (plField [i][1] == lPlayer && plField [i][0] == lPlayer && plField[i][2] == 0) { 
lX = i; 
lY = 2; 
lCounter ++; 
} 
 
if (plField [i][0] == lPlayer && plField [i][2] == lPlayer && plField[i][1] == 0) { 
lX = i; 
lY = 1; 
lCounter ++; 
} 
 
if (plField [i][1] == lPlayer && plField [i][2] == lPlayer && plField[i][0] == 0) { 
lX = i; 
lY = 0; 
lCounter ++; 
} 
} 
 
// Diagonals 
if (plField [1][1] == lPlayer && plField [0][0] == lPlayer && plField[2][2] == 0) { 
lX = 2; 
lY = 2; 
lCounter ++; 
} 
 
if (plField [1][1] == lPlayer && plField [2][0] == lPlayer && plField[0][2] == 0) { 
lX = 0; 
lY = 2; 
lCounter ++; 
} 
 
if (plField [1][1] == lPlayer && plField [0][2] == lPlayer && plField[2][0] == 0) { 
lX = 2; 
lY = 0; 
lCounter ++; 
} 
 
if (plField [1][1] == lPlayer && plField [2][2] == lPlayer && plField[0][0] == 0) { 
lX = 0; 
lY = 0; 
lCounter ++; 
} 
 
if (plField [0][0] == lPlayer && plField [2][2] == lPlayer && plField[1][1] == 0) { 
lX = 1; 
lY = 1; 
lCounter ++; 
} 
if (plField [0][2] == lPlayer && plField [2][0] == lPlayer && plField[1][1] == 0) { 
lX = 1; 
lY = 1; 
lCounter ++; 
} 
 
return lCounter; 
} 
 
 
HRESULT CComputer::LoadArray (long lGameID) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
ISharedProperty* spmPropField[3][3]; 
 
// Get context 
hr = GetObjectContext(&pObjectContext); 
 
// Create the SharedPropertyGroupManager 
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup); 
 
// Load the field SharedProperties 
TCHAR szBuf [512]; 
BSTR bstrField; 
CComVariant vField; 
 
for (long i = 0; i < 3; i ++ ) { 
for (long j = 0; j < 3; j ++) { 
 
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j); 
bstrField = TCHAR2BSTR (szBuf); 
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]); 
::SysFreeString (bstrField); 
 
hr = spmPropField[i][j]->get_Value (&vField); 
plField[i][j] = vField.lVal; 
} 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
for (i = 0; i < 3; i ++) { 
for (long j = 0; j < 3; j ++) { 
if (spmPropField[i][j]) { 
spmPropField[i][j]->Release(); 
} 
} 
} 
 
return hr; 
} 
 
 
HRESULT CComputer::SaveArray (long lGameID) { 
 
HRESULT hr = S_OK; 
 
IObjectContext* pObjectContext = NULL; 
 
ISharedPropertyGroupManager* spmMgr = NULL; 
ISharedPropertyGroup* spmGroup = NULL; 
ISharedProperty* spmPropField[3][3]; 
 
// Get context 
hr = GetObjectContext(&pObjectContext); 
 
// Create the SharedPropertyGroupManager 
hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr); 
 
// Create the SharedPropertyGroup 
LONG lIsolationMode = LockMethod; 
LONG lReleaseMode = Process; 
VARIANT_BOOL bExists = VARIANT_FALSE; 
hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup); 
 
// Save the field SharedProperties 
TCHAR szBuf [512]; 
BSTR bstrField; 
CComVariant vField; 
vField.vt = VT_I4; 
 
for (long i = 0; i < 3; i ++ ) { 
for (long j = 0; j < 3; j ++) { 
 
wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j); 
bstrField = TCHAR2BSTR (szBuf); 
hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]); 
::SysFreeString (bstrField); 
 
vField.lVal = plField[i][j]; 
hr = spmPropField[i][j]->put_Value (vField); 
} 
} 
 
if (pObjectContext) 
pObjectContext->Release(); 
 
if (spmMgr) 
spmMgr->Release(); 
 
if (spmGroup) 
spmGroup->Release(); 
 
for (i = 0; i < 3; i ++) { 
for (long j = 0; j < 3; j ++) { 
if (spmPropField[i][j]) { 
spmPropField[i][j]->Release(); 
} 
} 
} 
 
return hr; 
}