home *** CD-ROM | disk | FTP | other *** search
- // 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;
- }