home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / software / pelne / optionp / mts4.cab / TServer_Computer.cpp < prev    next >
C/C++ Source or Header  |  1997-11-14  |  17KB  |  733 lines

  1. // Filename: Computer.cpp
  2. //
  3. // Description:  Implementation of CComputer
  4. // This file contains the code that handles the interaction between a human
  5. // player and a computer player.
  6. //
  7. // This file is provided as part of the Microsoft Transaction Server Samples
  8. //
  9. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT 
  10. // WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 
  11. // INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES 
  12. // OF MERCHANTABILITY AND/OR FITNESS FOR A  PARTICULAR 
  13. // PURPOSE.
  14. //
  15. // Copyright (C) 1997 Microsoft Corporation, All rights reserved
  16.  
  17. #include "stdafx.h"
  18. #include "tServer.h"
  19. #include "Computer.h"
  20.  
  21. #include <mtx.h>
  22. #include <mtxspm.h>
  23. #include <time.h>
  24.  
  25. /////////////////////////////////////////////////////////////////////////////
  26. //
  27.  
  28. STDMETHODIMP CComputer::InterfaceSupportsErrorInfo(REFIID riid)
  29. {
  30.     static const IID* arr[] = 
  31.     {
  32.         &IID_IComputer,
  33.     };
  34.     
  35.     for (int i=0;i<sizeof(arr)/sizeof(arr[0]);i++)
  36.     {
  37.         if (InlineIsEqualGUID(*arr[i],riid))
  38.             return S_OK;
  39.     }
  40.     return S_FALSE;
  41. }
  42.  
  43.  
  44. STDMETHODIMP CComputer::AddNewGame (IN VARIANT_BOOL bEasy, OUT VARIANT* pvGameID, OUT VARIANT* pvOrder,
  45.                                     OUT VARIANT* pvX, OUT VARIANT* pvY) {
  46.     
  47.     HRESULT hr = S_OK;
  48.     
  49.     IObjectContext* pObjectContext = NULL;
  50.     
  51.     ISharedPropertyGroupManager* spmMgr = NULL;
  52.     ISharedPropertyGroup* spmGroup = NULL;
  53.     
  54.     ISharedProperty* spmPropCounter = NULL;
  55.     ISharedProperty* spmPropState = NULL;
  56.     
  57.     pvGameID->vt = VT_I4;
  58.     pvOrder->vt = VT_I4;
  59.     pvX->vt = VT_I4;
  60.     pvY->vt = VT_I4;
  61.     
  62.     long lGameID = 0;
  63.     long lOrder = 0;
  64.     long lNewX = 0;
  65.     long lNewY = 0;
  66.     
  67.     
  68.     try {
  69.         
  70.         // Get the object context
  71.         THROW_ERR ( hr = GetObjectContext(&pObjectContext) );
  72.         if (pObjectContext == NULL) {
  73.             THROW_ERR ( E_FAIL );
  74.         }
  75.         
  76.         // Create the SharedPropertyGroupManager
  77.         THROW_ERR ( pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr) );
  78.         
  79.         // Create the SharedPropertyGroup
  80.         LONG lIsolationMode = LockMethod;
  81.         LONG lReleaseMode = Process;
  82.         VARIANT_BOOL bExists = VARIANT_FALSE;
  83.         THROW_ERR ( spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup) );
  84.         
  85.         // Create the counter properties
  86.         THROW_ERR ( spmGroup->CreateProperty (L"Counter", &bExists, &spmPropCounter) );
  87.         
  88.         // Obtain the current value of the counter
  89.         CComVariant vCounter;        
  90.         THROW_ERR ( spmPropCounter->get_Value (&vCounter) );
  91.         
  92.         // Assign gameID
  93.         if (vCounter.lVal == 0)
  94.             vCounter.lVal = 1;
  95.         
  96.         lGameID = vCounter.lVal;
  97.         
  98.         // Update the counter
  99.         vCounter.lVal ++;
  100.         THROW_ERR ( spmPropCounter->put_Value (vCounter) );
  101.         
  102.         // Set the state variable
  103.         TCHAR szBuf [512];
  104.         BSTR bstrState;
  105.         
  106.         wsprintf (szBuf, _T("%dState"), lGameID);
  107.         bstrState = TCHAR2BSTR (szBuf);
  108.         THROW_ERR ( spmGroup->CreateProperty (bstrState, &bExists, &spmPropState) );
  109.         
  110.         CComVariant vState;
  111.         vState.vt = VT_I4;
  112.         vState.lVal = 4;
  113.         THROW_ERR ( spmPropState->put_Value (vState) );
  114.         
  115.         // Initialize the array
  116.         InitArray();
  117.         THROW_ERR ( SaveArray(lGameID) );
  118.         
  119.         // Assign order randomly
  120.         srand ((unsigned) time (NULL));
  121.         lOrder = rand() % 2 + 1;
  122.         
  123.         // Get computer's first move if player's order is 2
  124.         if (lOrder == 2) {
  125.             GetMove (lGameID, bEasy, lNewX, lNewY);
  126.             plField [lNewX][lNewY] = 2;
  127.             SaveArray(lGameID);
  128.         }
  129.         
  130.         // Prepare return values
  131.         pvX->lVal = lNewX;
  132.         pvY->lVal = lNewY;
  133.         pvOrder->lVal = lOrder;
  134.         pvGameID->lVal = lGameID;
  135.         
  136.         // We're finished and happy
  137.         pObjectContext->SetComplete();
  138.         
  139.     } catch (HRESULT hr) {
  140.         
  141.         // Create an ErrorInfo object
  142.         ICreateErrorInfo* pCreateErrInfo = NULL;
  143.         IErrorInfo* pErrInfo = NULL;
  144.         
  145.         CreateErrorInfo (&pCreateErrInfo);
  146.         pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo);
  147.         
  148.         // Fill in error information
  149.         TCHAR szErr [512];
  150.         wsprintf (szErr, _T("Error %d occurred in Computer::AddNewGame()"), hr);
  151.         BSTR bstrDesc = TCHAR2BSTR (szErr);
  152.         pCreateErrInfo->SetGUID (IID_IComputer);
  153.         pCreateErrInfo->SetSource (L"Computer");
  154.         pCreateErrInfo->SetDescription (bstrDesc);
  155.         ::SysFreeString (bstrDesc);
  156.         
  157.         // Confirm error information
  158.         SetErrorInfo (0, pErrInfo);
  159.         
  160.         // Clean up the error objects
  161.         if (pCreateErrInfo)
  162.             pCreateErrInfo->Release();
  163.         
  164.         if (pErrInfo)
  165.             pErrInfo->Release();
  166.         
  167.         // Indicate our unhappiness
  168.         if (pObjectContext)
  169.             pObjectContext->SetAbort();
  170.         
  171.         hr = E_FAIL;
  172.     }
  173.     
  174.     if (pObjectContext)
  175.         pObjectContext->Release();
  176.     
  177.     if (spmMgr)
  178.         spmMgr->Release();
  179.     
  180.     if (spmGroup)
  181.         spmGroup->Release();
  182.     
  183.     if (spmPropCounter)
  184.         spmPropCounter->Release();
  185.     
  186.     if (spmPropState)
  187.         spmPropState->Release();
  188.     
  189.     return hr;
  190. }
  191.  
  192.  
  193. STDMETHODIMP CComputer::NewMove (IN long lGameID, IN VARIANT_BOOL bEasy, IN long lX, IN long lY,
  194.                                  OUT VARIANT* pvMyX, OUT VARIANT* pvMyY, OUT VARIANT* pvWin) {
  195.     // Win protocol
  196.     // ============
  197.     // 0  -> moves did not end game
  198.     // 1  -> player won
  199.     // -1 -> computer won
  200.     // 2  -> player's move tied the game
  201.     // -2 -> computer's move tied the game
  202.     
  203.     HRESULT hr = S_OK;
  204.     
  205.     pvMyX->vt = VT_I4;
  206.     pvMyY->vt = VT_I4;
  207.     pvWin->vt = VT_I4;
  208.     
  209.     long lTurns = 0;
  210.     long lWin = 0;
  211.     long lNewX = 0;
  212.     long lNewY = 0;
  213.     
  214.     IObjectContext* pObjectContext = NULL;
  215.     
  216.     
  217.     try {
  218.         
  219.         // Get the object context
  220.         THROW_ERR ( hr = GetObjectContext(&pObjectContext) );
  221.         if (pObjectContext == NULL) {
  222.             THROW_ERR ( E_FAIL );
  223.         }
  224.         
  225.         THROW_ERR ( LoadArray(lGameID) ); 
  226.         
  227.         // Enter new move
  228.         plField [lX][lY] = 1;
  229.         
  230.         // Check for player's win
  231.         if (IsWin (1)) {
  232.             
  233.             // Player won
  234.             lWin = 1;
  235.             
  236.         } else {
  237.             
  238.             // Check for fullness
  239.             lTurns = HowManyTurns();
  240.             
  241.             if (lTurns == 9) {
  242.                 
  243.                 // Tie
  244.                 lWin = - 2;
  245.                 
  246.             } else {
  247.                 
  248.                 // Get computer's move
  249.                 GetMove (lGameID, bEasy, lNewX, lNewY);
  250.                 plField[lNewX][lNewY] = 2;
  251.                 
  252.                 // Check for computer win
  253.                 if (IsWin(2))
  254.                     
  255.                     lWin = - 1;
  256.                 
  257.                 else
  258.                     
  259.                     // Check for fullness            
  260.                     if (lTurns == 8)
  261.                         lWin = 2;
  262.             }
  263.         }
  264.         
  265.         // Save array
  266.         THROW_ERR ( SaveArray(lGameID) );
  267.         
  268.         // Set return values
  269.         pvMyX->lVal = lNewX;
  270.         pvMyY->lVal = lNewY;
  271.         pvWin->lVal = lWin;
  272.         
  273.         // We're finished and happy        
  274.         pObjectContext->SetComplete();
  275.         
  276.     } catch (HRESULT hr) {
  277.         
  278.         // Create an ErrorInfo object
  279.         ICreateErrorInfo* pCreateErrInfo = NULL;
  280.         IErrorInfo* pErrInfo = NULL;
  281.         
  282.         CreateErrorInfo (&pCreateErrInfo);
  283.         pCreateErrInfo->QueryInterface (IID_IErrorInfo, (LPVOID FAR*) &pErrInfo);
  284.         
  285.         // Fill in error information
  286.         TCHAR szErr[512];
  287.         wsprintf (szErr, _T("Error %d occurred in Computer::NewMove()"), hr);
  288.         BSTR bstrDesc = TCHAR2BSTR (szErr);
  289.         pCreateErrInfo->SetGUID (IID_IComputer);
  290.         pCreateErrInfo->SetSource (L"Computer");
  291.         pCreateErrInfo->SetDescription (bstrDesc);
  292.         ::SysFreeString (bstrDesc);
  293.         
  294.         // Confirm error information
  295.         SetErrorInfo (0, pErrInfo);
  296.         
  297.         // Clean up the error objects
  298.         if (pCreateErrInfo)
  299.             pCreateErrInfo->Release();
  300.         
  301.         if (pErrInfo)
  302.             pErrInfo->Release();
  303.         
  304.         // Indicate our unhappiness
  305.         if (pObjectContext)
  306.             pObjectContext->SetAbort();
  307.         
  308.         hr = E_FAIL;
  309.     }
  310.     
  311.     if (pObjectContext)
  312.         pObjectContext->Release();
  313.     
  314.     return hr;
  315. }
  316.  
  317.  
  318. void CComputer::GetMove (long lGameID, VARIANT_BOOL bEasy, long& lX, long& lY) {
  319.     
  320.     long lFlag = 0;
  321.     long lNewX = 0;
  322.     long lNewY = 0;
  323.     
  324.     // Perhaps there's only possible move
  325.     if (HowManyTurns() == 8) {
  326.         
  327.         // Scan for open tile
  328.         long i,j;
  329.         for (i = 0; i < 3; i ++) {
  330.             for (j = 0; j < 3; j ++) {
  331.                 if (plField[i][j] == 0) {
  332.                     lNewX = i;
  333.                     lNewY = j;
  334.                     lFlag = 1;
  335.                 }
  336.             }
  337.         }
  338.     }
  339.     
  340.     // Check for win
  341.     if (lFlag == 0)
  342.         lFlag = LineScan (2, lNewX, lNewY);
  343.     
  344.     // Check for block
  345.     if (lFlag == 0)
  346.         lFlag = LineScan (1, lNewX, lNewY);
  347.     
  348.     // Else create a new move
  349.     if (lFlag == 0) {
  350.         
  351.         // Prepare random seed
  352.         srand ((unsigned) time (NULL));
  353.         
  354.         // Decide intelligence level
  355.         if (!bEasy) {
  356.             
  357.             // Place in center if it's open
  358.             if (plField[1][1] == 0) {
  359.                 lNewX = 1;
  360.                 lNewY = 1;
  361.                 lFlag = 1;
  362.                 
  363.             } else {
  364.                 
  365.                 // Place on corner, if opponent placed first move in center
  366.                 long lTurns = HowManyTurns();
  367.                 if (lTurns == 1 && plField[1][1] > 0) {
  368.                     lNewX = (rand() % 2 == 0) ? 0:2;
  369.                     lNewY = (rand() % 2 == 0) ? 0:2;
  370.                     lFlag = 1;
  371.                 } else {
  372.                     
  373.                     // Place on opposite tile, if opponent's second
  374.                     // move was placed on diagonal
  375.                     if (lTurns == 2) {
  376.                         if (plField[0][0] == 1) {
  377.                             lNewX = 2;
  378.                             lNewY = 2;
  379.                             lFlag = 1;
  380.                         }
  381.                         
  382.                         if (plField[0][2] == 1) {
  383.                             lNewX = 2;
  384.                             lNewY = 0;
  385.                             lFlag = 1;
  386.                         }
  387.                         
  388.                         if (plField[2][0] == 1) {
  389.                             lNewX = 0;
  390.                             lNewY = 2;
  391.                             lFlag = 1;
  392.                         }
  393.                         
  394.                         if (plField[2][2] == 1) {
  395.                             lNewX = 0;
  396.                             lNewY = 0;
  397.                             lFlag = 1;
  398.                         }
  399.                     }
  400.                 }
  401.             }
  402.             
  403.             // Else find most intelligent move
  404.             if (lFlag == 0) {
  405.                 
  406.                 float value = 0;
  407.                 float maxValue = 0;
  408.                 
  409.                 for (long i = 0; i < 3; i ++) {
  410.                     for (long j = 0; j < 3; j ++) {
  411.                         
  412.                         // Evaluate tile
  413.                         value = Evaluate (i, j);
  414.                         
  415.                         // Randomize equal choices
  416.                         if (value == maxValue && rand() % 2 == 0) {
  417.                             
  418.                             lNewX = i;
  419.                             lNewY = j;
  420.                             
  421.                         } else {
  422.                             
  423.                             if (value > maxValue) {
  424.                                 maxValue = value;
  425.                                 lNewX = i;
  426.                                 lNewY = j;
  427.                             }
  428.                         }
  429.                     }
  430.                 }
  431.             }
  432.             
  433.         } else {
  434.             
  435.             // Choose a random slot
  436.             while (lFlag == 0) {
  437.                 
  438.                 lNewX = rand() % 3;
  439.                 lNewY = rand() % 3;
  440.                 
  441.                 if (plField [lNewX][lNewY] == 0)
  442.                     lFlag = 1;
  443.             }
  444.         }
  445.     }
  446.     
  447.     // Prepare return values
  448.     lX = lNewX;
  449.     lY = lNewY;
  450. }
  451.  
  452. float CComputer::Evaluate (long lX, long lY) {
  453.     
  454.     long lNewX, lNewY, lNewX2, lNewY2;
  455.     float fEval1, fEval2, fEval3;
  456.     
  457.     if (plField[lX][lY] != 0) {
  458.         
  459.         lX = - 100;
  460.         lY = - 100;
  461.         return - 100;
  462.     } else {
  463.         
  464.         // Evaluate defensive options
  465.         plField[lX][lY] = 1;
  466.         fEval2 = (float) (LineScan (1, lNewX, lNewY) / 3);
  467.         
  468.         // Evaluate offensive options
  469.         plField[lX][lY] = 2;
  470.         fEval1 = (float) LineScan (2, lNewX, lNewY);
  471.         
  472.         // Evaluate opponent's follow-up
  473.         if (fEval1 > 0) {
  474.             plField[lNewX][lNewY] = 1;
  475.             fEval3 = (float) (LineScan (1, lNewX2, lNewY2) * 0.9);
  476.             plField[lNewX][lNewY] = 0;
  477.         } else
  478.             fEval3 = 0;
  479.         
  480.         // Restore tiles and return final evaluation
  481.         plField[lX][lY] = 0;
  482.         return fEval1 + fEval2 - fEval3;
  483.     }
  484. }
  485.  
  486.  
  487. bool CComputer::IsWin (long lPlayer) {
  488.     
  489.     bool bWin = false;
  490.     
  491.     for (int i = 0; i < 3; i ++) {
  492.         if (plField [i][0] == plField [i][1] && plField [i][1] == plField [i][2] && plField [i][0] == lPlayer)
  493.             bWin = true;
  494.         if (plField [0][i] == plField [1][i] && plField [1][i] == plField [2][i] && plField [0][i] == lPlayer)
  495.             bWin = true;
  496.     }
  497.     
  498.     if (plField [0][0] == plField [1][1] && plField [1][1] == plField [2][2] && plField [1][1] == lPlayer)
  499.         bWin = true;
  500.     if (plField [2][0] == plField [1][1] && plField [1][1] == plField [0][2] && plField [1][1] == lPlayer)
  501.         bWin = true;
  502.     
  503.     return bWin;
  504. }
  505.  
  506.  
  507. long CComputer::HowManyTurns () {
  508.     
  509.     long lTurns = 0;
  510.     
  511.     for (int i = 0; i < 3; i ++) {
  512.         for (int j = 0; j < 3; j ++) {
  513.             if (plField[i][j] > 0) {
  514.                 lTurns ++;
  515.             }
  516.         }
  517.     }
  518.     
  519.     return lTurns;
  520. }
  521.  
  522.  
  523. long CComputer::LineScan (long lPlayer, long& lX, long& lY) {
  524.     
  525.     long lCounter = 0;
  526.     lX = - 100;
  527.     lY = - 100;
  528.     
  529.     // Horizontal lines
  530.     for (int i = 0; i < 3; i ++) {
  531.         
  532.         if (plField [1][i] == lPlayer && plField [0][i] == lPlayer && plField[2][i] == 0) {
  533.             lX = 2;
  534.             lY = i;
  535.             lCounter ++;
  536.         }
  537.         
  538.         if (plField [0][i] == lPlayer && plField [2][i] == lPlayer && plField[1][i] == 0) {
  539.             lX = 1;
  540.             lY = i;
  541.             lCounter ++;
  542.         }
  543.         
  544.         if (plField [1][i] == lPlayer && plField [2][i] == lPlayer && plField[0][i] == 0) {
  545.             lX = 0;
  546.             lY = i;
  547.             lCounter ++;
  548.         }
  549.     }
  550.     
  551.     // Vertical lines
  552.     for (i = 0; i < 3; i ++) {
  553.         
  554.         if (plField [i][1] == lPlayer && plField [i][0] == lPlayer && plField[i][2] == 0) {
  555.             lX = i;
  556.             lY = 2;
  557.             lCounter ++;
  558.         }
  559.         
  560.         if (plField [i][0] == lPlayer && plField [i][2] == lPlayer && plField[i][1] == 0) {
  561.             lX = i;
  562.             lY = 1;
  563.             lCounter ++;
  564.         }
  565.         
  566.         if (plField [i][1] == lPlayer && plField [i][2] == lPlayer && plField[i][0] == 0) {
  567.             lX = i;
  568.             lY = 0;
  569.             lCounter ++;
  570.         }
  571.     }
  572.     
  573.     // Diagonals
  574.     if (plField [1][1] == lPlayer && plField [0][0] == lPlayer && plField[2][2] == 0) {
  575.         lX = 2;
  576.         lY = 2;
  577.         lCounter ++;
  578.     }
  579.     
  580.     if (plField [1][1] == lPlayer && plField [2][0] == lPlayer && plField[0][2] == 0) {
  581.         lX = 0;
  582.         lY = 2;
  583.         lCounter ++;
  584.     }
  585.     
  586.     if (plField [1][1] == lPlayer && plField [0][2] == lPlayer && plField[2][0] == 0) {
  587.         lX = 2;
  588.         lY = 0;
  589.         lCounter ++;
  590.     }
  591.     
  592.     if (plField [1][1] == lPlayer && plField [2][2] == lPlayer && plField[0][0] == 0) {
  593.         lX = 0;
  594.         lY = 0;
  595.         lCounter ++;
  596.     }
  597.     
  598.     if (plField [0][0] == lPlayer && plField [2][2] == lPlayer && plField[1][1] == 0) {
  599.         lX = 1;
  600.         lY = 1;
  601.         lCounter ++;
  602.     }
  603.     if (plField [0][2] == lPlayer && plField [2][0] == lPlayer && plField[1][1] == 0) {
  604.         lX = 1;
  605.         lY = 1;
  606.         lCounter ++;
  607.     }
  608.     
  609.     return lCounter;
  610. }
  611.  
  612.  
  613. HRESULT CComputer::LoadArray (long lGameID) {
  614.     
  615.     HRESULT hr = S_OK;
  616.     
  617.     IObjectContext* pObjectContext = NULL;
  618.     
  619.     ISharedPropertyGroupManager* spmMgr = NULL;
  620.     ISharedPropertyGroup* spmGroup = NULL;
  621.     ISharedProperty* spmPropField[3][3];
  622.     
  623.     // Get context
  624.     hr = GetObjectContext(&pObjectContext);
  625.     
  626.     // Create the SharedPropertyGroupManager
  627.     hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr);
  628.     
  629.     // Create the SharedPropertyGroup
  630.     LONG lIsolationMode = LockMethod;
  631.     LONG lReleaseMode = Process;
  632.     VARIANT_BOOL bExists = VARIANT_FALSE;
  633.     hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup);
  634.     
  635.     // Load the field SharedProperties
  636.     TCHAR szBuf [512];
  637.     BSTR bstrField;
  638.     CComVariant vField;
  639.     
  640.     for (long i = 0; i < 3; i ++ ) {
  641.         for (long j = 0; j < 3; j ++) {
  642.             
  643.             wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j);
  644.             bstrField = TCHAR2BSTR (szBuf);
  645.             hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]);
  646.             ::SysFreeString (bstrField);
  647.             
  648.             hr = spmPropField[i][j]->get_Value (&vField);
  649.             plField[i][j] = vField.lVal;
  650.         }
  651.     }
  652.     
  653.     if (pObjectContext)
  654.         pObjectContext->Release();
  655.     
  656.     if (spmMgr)
  657.         spmMgr->Release();
  658.     
  659.     if (spmGroup)
  660.         spmGroup->Release();
  661.     
  662.     for (i = 0; i < 3; i ++) {
  663.         for (long j = 0; j < 3; j ++) {
  664.             if (spmPropField[i][j]) {
  665.                 spmPropField[i][j]->Release();
  666.             }
  667.         }
  668.     }
  669.     
  670.     return hr;
  671. }
  672.  
  673.  
  674. HRESULT CComputer::SaveArray (long lGameID) {
  675.     
  676.     HRESULT hr = S_OK;
  677.     
  678.     IObjectContext* pObjectContext = NULL;
  679.     
  680.     ISharedPropertyGroupManager* spmMgr = NULL;
  681.     ISharedPropertyGroup* spmGroup = NULL;
  682.     ISharedProperty* spmPropField[3][3];
  683.     
  684.     // Get context
  685.     hr = GetObjectContext(&pObjectContext);
  686.     
  687.     // Create the SharedPropertyGroupManager
  688.     hr = pObjectContext->CreateInstance (CLSID_SharedPropertyGroupManager, IID_ISharedPropertyGroupManager, (void**)&spmMgr);
  689.     
  690.     // Create the SharedPropertyGroup
  691.     LONG lIsolationMode = LockMethod;
  692.     LONG lReleaseMode = Process;
  693.     VARIANT_BOOL bExists = VARIANT_FALSE;
  694.     hr = spmMgr->CreatePropertyGroup (L"TicTacToe", &lIsolationMode, &lReleaseMode, &bExists, &spmGroup);
  695.     
  696.     // Save the field SharedProperties
  697.     TCHAR szBuf [512];
  698.     BSTR bstrField;
  699.     CComVariant vField;
  700.     vField.vt = VT_I4;
  701.     
  702.     for (long i = 0; i < 3; i ++ ) {
  703.         for (long j = 0; j < 3; j ++) {
  704.             
  705.             wsprintf (szBuf, _T("%dField%d%d"), lGameID, i, j);
  706.             bstrField = TCHAR2BSTR (szBuf);
  707.             hr = spmGroup->CreateProperty (bstrField, &bExists, &spmPropField[i][j]);
  708.             ::SysFreeString (bstrField);
  709.             
  710.             vField.lVal = plField[i][j];
  711.             hr = spmPropField[i][j]->put_Value (vField);
  712.         }
  713.     }
  714.     
  715.     if (pObjectContext)
  716.         pObjectContext->Release();
  717.     
  718.     if (spmMgr)
  719.         spmMgr->Release();
  720.     
  721.     if (spmGroup)
  722.         spmGroup->Release();
  723.     
  724.     for (i = 0; i < 3; i ++) {
  725.         for (long j = 0; j < 3; j ++) {
  726.             if (spmPropField[i][j]) {
  727.                 spmPropField[i][j]->Release();
  728.             }
  729.         }
  730.     }
  731.     
  732.     return hr;
  733. }