<http://lib.cnfolio.com/OthelloGame>
Introduction to Algorithms and Programming

Coursework Project – Othello Game



During the academic year 2005/6, students were assigned the task of developing an Othello game.

The game was written in C and used the GTK 1.2 graphics library. The program operated on a peer to peer basis with each player able to initiate or accept invitations to play head to head with another player over a TCP/IP network. As such, each program acted as both client and server during a game. All networking communications was done using the UDP protocol to minimize interference with firewalls.

Students were provided with the files othello.h and board.c at the start of the project to have graphics and networking functionalities. The project required implementation of the game logic. The othello.c file was provided to students at the end of the project as a sample solution.

othello.h

  1. /*
  2. * othello.h : Project Assignment header file for Othello game.
  3. *
  4. * University of Portsmouth, UK
  5. * Introduction to Algorithms and Programming
  6. * Lecturer: C Nguyen
  7. *
  8. * Updated 12 Mar 2006
  9. *
  10. */
  11.  
  12. #include <arpa/inet.h>
  13. #include <fcntl.h>
  14. #include <glib.h>
  15. #include <gtk/gtk.h>
  16. #include <netdb.h>
  17. #include <netinet/in.h>
  18. #include <sys/types.h>
  19. #include <sys/socket.h>
  20. #include <unistd.h>
  21.  
  22. /*
  23. * Descriptive string containing name and version number
  24. */
  25. #define APPLICATION_NAME_STR        "OTHELLO v1.0C"
  26.  
  27. /*
  28. * Descriptive string to indicate the game board
  29. */
  30. #define APPLICATION_GAME_BOARD      "BOARD"
  31.  
  32. /*
  33. * Descriptive string to indicate the message display
  34. */
  35. #define APPLICATION_MSGBOX_STR      "MESSAGES"
  36.  
  37. /*
  38. * Descriptive string for button to start a game
  39. */
  40. #define APPLICATION_BUTTON_PLAY_STR "PLAY"
  41.  
  42. /*
  43. * Descriptive string for button to display information about current game
  44. */
  45. #define APPLICATION_BUTTON_INFO_STR "INFO"
  46.  
  47. /*
  48. * Descriptive string for button to end a game and exit application
  49. */
  50. #define APPLICATION_BUTTON_EXIT_STR "EXIT"
  51.  
  52. /*
  53. * Descriptive string for title of dialog window
  54. */
  55. #define APPLICATION_DIALOG_STR      "CONTINUE?"
  56.  
  57. /*
  58. * Descriptive string for dialog button for user response Yes
  59. */
  60. #define APPLICATION_DIALOG_YES_STR  "YES"
  61.  
  62. /*
  63. * Descriptive string for dialog button for user response No
  64. */
  65. #define APPLICATION_DIALOG_NO_STR   "NO"
  66.  
  67. /*
  68. * Descriptive string for dialog asking whether player wants to exit
  69. */
  70. #define APPLICATION_DIALOG_QUESTION "Are you sure you want to exit now?"
  71.  
  72. /*
  73. * Descriptive string for title of dialog window when asking for opponent host info
  74. */
  75. #define APPLICATION_DIALOG_HOST_STR "OPPONENT HOST"
  76.  
  77. /*
  78. * Descriptive string for dialog button to connect to opponent host
  79. */
  80. #define APPLICATION_DIALOG_HOST_YES "PLAY"
  81.  
  82. /*
  83. * Descriptive string for dialog button for cancel play request
  84. */
  85. #define APPLICATION_DIALOG_HOST_NO  "CANCEL"
  86.  
  87. /*
  88. * Max length permitted in text entry field
  89. */
  90. #define TEXT_ENTRY_MAX_LENGTH       128
  91.  
  92. /*
  93. * Default width of application window
  94. */
  95. #define APPLICATION_WINDOW_WIDTH    350
  96.  
  97. /*
  98. * Default height of application window
  99. */
  100. #define APPLICATION_WINDOW_HEIGHT   560
  101.  
  102. /*
  103. * Default width of dialog window
  104. */
  105. #define APPLICATION_DIALOG_WIDTH    250
  106.  
  107. /*
  108. * Default height of dialog window
  109. */
  110. #define APPLICATION_DIALOG_HEIGHT   80
  111.  
  112. /*
  113. * Default value if network socket is invalid or not connected
  114. */
  115. #define NETWORK_SOCKET_CLOSED       -1
  116.  
  117. /*
  118. * Color of main window background
  119. */
  120. #define COLOR_WINDOW_BG             "lightsteelblue3"
  121.  
  122. /*
  123. * Color of normal grid backgrounds
  124. */
  125. #define COLOR_GRID_BG               "palegreen3"
  126.  
  127. /*
  128. * Color of grid backgrounds when mouse is hovering above
  129. */
  130. #define COLOR_GRID_HOVER_BG         "gold"
  131.  
  132. /*
  133. * Color of grid backgrounds when mouse button is pressed
  134. */
  135. #define COLOR_GRID_ACTIVE_BG        "palegreen4"
  136.  
  137. /*
  138. * Color of message display background
  139. */
  140. #define COLOR_MSGBOX_BG             "azure3"
  141.  
  142. /*
  143. * Name of XPM image for use with Play button
  144. * Requires image file to be in same directory as application
  145. */
  146. #define XPM_BUTTON_PLAY             "g_play.xpm"
  147.  
  148. /*
  149. * Name of XPM image for use with Info button
  150. * Requires image file to be in same directory as application
  151. */
  152. #define XPM_BUTTON_INFO             "g_info.xpm"
  153.  
  154. /*
  155. * Name of XPM image for use with Exit button
  156. * Requires image file to be in same directory as application
  157. */
  158. #define XPM_BUTTON_EXIT             "g_exit.xpm"
  159.  
  160. /*
  161. * Name of XPM image to indicate Yes response on dialog window
  162. * Requires image file to be in same directory as application
  163. */
  164. #define XPM_BUTTON_DIALOG_YES       "g_yes.xpm"
  165.  
  166. /*
  167. * Name of XPM image to indicate No response on dialog window
  168. * Requires image file to be in same directory as application
  169. */
  170. #define XPM_BUTTON_DIALOG_NO        "g_no.xpm"
  171.  
  172. /*
  173. * Name of XPM image to indicate a game square without any discs
  174. * Requires image file to be in same directory as application
  175. */
  176. #define XPM_SQUARE_EMPTY            "g_no_disc.xpm"
  177.  
  178. /*
  179. * Name of XPM image to indicate a game square with a black disc
  180. * Requires image file to be in same directory as application
  181. */
  182. #define XPM_SQUARE_BLACK            "g_black_disc.xpm"
  183.  
  184. /*
  185. * Name of XPM image to indicate a game square with a white disc
  186. * Requires image file to be in same directory as application
  187. */
  188. #define XPM_SQUARE_WHITE            "g_white_disc.xpm"
  189.  
  190. /*
  191. * Size of game board
  192. */
  193. #define BOARD_SIZE                   8
  194.  
  195. /*
  196. * Game event type when a player clicks on the PLAY button
  197. */
  198. #define REQUEST_PLAY_EVENT          111
  199.  
  200. /*
  201. * Game event type when a player clicks on the INFO button
  202. */
  203. #define REQUEST_INFO_EVENT          112
  204.  
  205. /*
  206. * Game event type when a player clicks on a square of the game BOARD
  207. */
  208. #define REQUEST_BOARD_EVENT         113
  209.  
  210. /*
  211. * Game event type when opponent sends message to initialise a new game
  212. */
  213. #define MESSAGE_INIT_EVENT          221
  214.  
  215. /*
  216. * Game event type when opponent sends message to start playing a new game after initialisation is done
  217. */
  218. #define MESSAGE_PLAY_EVENT          222
  219.  
  220. /*
  221. * Game event type when opponent sends message to place a new disc on the game board
  222. */
  223. #define MESSAGE_BOARD_EVENT         223
  224.  
  225. /*
  226. * Game event type when opponent sends message to confirm that player's new disc has been placed on board
  227. */
  228. #define MESSAGE_RESPONSE_EVENT      224
  229.  
  230. /*
  231. * Game event type when opponent sends message to indicate end of game
  232. */
  233. #define MESSAGE_END_EVENT           225
  234.  
  235. /*
  236. * Status value when a game square is empty
  237. */
  238. #define EMPTY_SQUARE                331
  239.  
  240. /*
  241. * Status value when a game square contains a black disc
  242. */
  243. #define BLACK_SQUARE                332
  244.  
  245. /*
  246. * Status value when a game square contains a white disc
  247. */
  248. #define WHITE_SQUARE                333
  249.  
  250. /*
  251. * gameEventsDispatcher : Main function to handle game events
  252. *
  253. * Parameters           : Unique ID number to identify events and help with debugging
  254. *                        Type of game event
  255. *                        The row identifier of the active board square
  256. *                        The column identifier of the active board square
  257. *                        The IP address and port number of the opponent
  258. *
  259. */
  260. void gameEventsDispatcher( long, int, int, char, char* );
  261.  
  262. /*
  263. * updateBoard : Show status of game square at specific location on the board
  264. *
  265. * Parameters  : Row label
  266. *               Column label
  267. *               Game square status
  268. *
  269. */
  270. void updateBoard( int, char, int );
  271.  
  272. /*
  273. * resetBoard : Reset all game squares on the board to be all empty
  274. *
  275. */
  276. void resetBoard();
  277.  
  278. /*
  279. * displayMessage : Appends message to game message console
  280. *
  281. * Parameters     : Text message to display
  282. *
  283. */
  284. void displayMessage( char* );
  285.  
  286. /*
  287. * notifyInit : Send message to opponent initiating a game
  288. *
  289. * Parameters : String of opponent host IP address
  290. *
  291. */
  292. int notifyInit( char* );
  293.  
  294. /*
  295. * notifyPlay : Send message to opponent that you are ready to start playing
  296. *
  297. */
  298. int notifyPlay();
  299.  
  300. /*
  301. * putNewDisc : Place a new disc on a specific location on the game board
  302. *
  303. * Parameters : Row label
  304. *              Column label
  305. */
  306. void putNewDisc( int, char );
  307.  
  308. /*
  309. * respond    : Send confirmation response to opponent's placement of a new disc
  310. *
  311. * Parameters : Row label
  312. *              Column label
  313. *
  314. */
  315. void respond( int, char );
  316.  
  317. /*
  318. * getPortNumber : Returns port number of current game program
  319. *
  320. * Returns port number if server is connected, else returns NETWORK_SOCKET_CLOSED value
  321. *
  322. */
  323. int getPortNumber( void );
  324.  
  325. /*
  326. * getIPAddress : Returns IP address of current computer
  327. *
  328. * Returns string representing the computer IP address
  329. *
  330. */
  331. char* getIPAddress( void );
  332.  
  333. /*
  334. * endGame    : Send message to opponent indicating end of game
  335. *
  336. * Parameters : TRUE if application should also exit after end of game message has been sent
  337. *
  338. */
  339. void endGame( int );



board.c

  1. /*
  2. * board.c : Common Project Assignment functions for Othello game.
  3. *
  4. * University of Portsmouth, UK
  5. * Introduction to Algorithms and Programming
  6. * Lecturer: C Nguyen
  7. *
  8. * Updated 12 Mar 2006
  9. *
  10. */
  11.  
  12. #include "othello.h"
  13.  
  14. /*
  15. * Polling period for network activities; in milliseconds
  16. */
  17. #define NETWORK_POLL_PERIOD         1200
  18.  
  19. /*
  20. * Maximum for range of port numbers for game
  21. */
  22. #define NETWORK_PORT_MAX            5000
  23.  
  24. /*
  25. * Minimum and default value for range of port numbers for game
  26. */
  27. #define NETWORK_PORT_MIN            2000
  28.  
  29. /*
  30. * Default size of buffer when reading incoming network data
  31. */
  32. #define NETWORK_BUFFER_SIZE         512
  33.  
  34. /*
  35. * Delimiter to separate host IP address from port number
  36. */
  37. #define NETWORK_ADDR_DELIMITERS     ":"
  38.  
  39. /*
  40. * Internal structure for game events
  41. *
  42. * eventID     = Unique ID number to identify events and help with debugging
  43. * eventType   = Indicates type of event
  44. * eventRow    = The row identifier of the active board square
  45. * eventCol    = The column identifier of the active board square
  46. * eventData   = The IP address and port number of the opponent
  47. *
  48. */
  49. struct _gameEvent
  50. {
  51.   long  eventID;
  52.   int   eventType;
  53.   int   eventRow;
  54.   char  eventCol;
  55.   int   eventStatus;
  56.   char *eventData;
  57. };
  58. typedef struct _gameEvent GameEvent;
  59.  
  60. /*
  61. * Main application window
  62. */
  63. static GtkWidget *g_pMainWindow = NULL;
  64.  
  65. /*
  66. * Button to start a new game
  67. */
  68. static GtkWidget *g_pButtonPlay = NULL;
  69.  
  70. /*
  71. * Button to display game information to player
  72. */
  73. static GtkWidget *g_pButtonInfo = NULL;
  74.  
  75. /*
  76. * Button to surrender current game if one is in progress
  77. * and exit application if requested
  78. */
  79. static GtkWidget *g_pButtonExit = NULL;
  80.  
  81. /*
  82. * Array of buttons for game board
  83. */
  84. static GtkWidget *g_pGameBoard[ BOARD_SIZE ][ BOARD_SIZE ];
  85.  
  86. /*
  87. * Text display for application messages
  88. */
  89. static GtkWidget *g_pMessageBox = NULL;
  90.  
  91. /*
  92. * Modal dialog for simple user questions
  93. */
  94. static GtkWidget *g_pDialogWindow = NULL;
  95.  
  96. /*
  97. * Text entry field for user to type response
  98. */
  99. static GtkWidget *g_pDialogAnswer = NULL;
  100.  
  101. /*
  102. * Button on dialog for Yes response
  103. */
  104. static GtkWidget *g_pDialogYes = NULL;
  105.  
  106. /*
  107. * Button on dialog for Play response
  108. */
  109. static GtkWidget *g_pDialogPlay = NULL;
  110.  
  111. /*
  112. * Button on dialog for No/Cancel response
  113. */
  114. static GtkWidget *g_pDialogNo = NULL;
  115.  
  116. /*
  117. * Maintains counter for identifying game event ID numbers
  118. */
  119. static long       g_iNextGameEventID = 0;
  120.  
  121. /*
  122. * Port number used by host to listen for incoming game data
  123. */
  124. static int        g_iHostPort = NETWORK_PORT_MIN;
  125.  
  126. /*
  127. * Datagram socket used by host to listen for incoming game data
  128. */
  129. static int        g_iHostSocket = NETWORK_SOCKET_CLOSED;
  130.  
  131. /*
  132. * Current opponent host IP address
  133. */
  134. static char      *g_pTargetIP = NULL;
  135.  
  136. /*
  137. * Current opponent host port number
  138. */
  139. static int        g_iTargetPort = NETWORK_PORT_MIN;
  140.  
  141. /*
  142. * Forward function declarations
  143. *
  144. */
  145. static void describeEvent( GameEvent *, int, int );
  146. static gboolean initHost();
  147. static gboolean cbReadMsg( gpointer );
  148. static gboolean sendMsg( gpointer, gint, gpointer );
  149. static void cbButtonClick( GtkWidget*, gpointer );
  150. static gint cbMainWindowClose( GtkWidget*, GdkEventAny*, gpointer );
  151. static void exitMainApp( void );
  152. static void replaceContainerWidgets( GtkWidget*, GtkWidget* );
  153. static void setWidgetColors( GtkWidget*, gchar*, gchar*, gchar*, gchar* );
  154. static GameEvent *createGameEvent( void );
  155. static char *createNetworkMsg( GameEvent* );
  156. static GtkWidget *createImageLabel( GtkWidget*, GtkWidget*, gchar*, gchar* );
  157. static void parseHostStr( char*, char**, int* );
  158. static void showDialogWindow( gchar*, GtkWidget*, GtkWidget*, GtkWidget* );
  159. static void createAppWindow( void );
  160.  
  161. /*
  162. * main : Program
  163. *
  164. * argc   Number of command line parameters received
  165. * argv   Array of command line parameters received
  166. *
  167. */
  168. int main( int argc, char* argv[] )
  169. {
  170.   /*
  171.    * Connects to X server
  172.    * Parses some default arguments understood by all GTK+ programs
  173.    * Registers a cleanup function using atexit()
  174.    */
  175.   gtk_init( &argc, &argv );
  176.  
  177.   /*
  178.    * Create graphical display
  179.    */
  180.   createAppWindow();
  181.  
  182.   /*
  183.    * Install callback function to integrate network events
  184.    */
  185.   if ( initHost() == FALSE ) { return 1; }
  186.   g_timeout_add( NETWORK_POLL_PERIOD, &cbReadMsg, NULL );
  187.  
  188.   /*
  189.    * Start GTK application loop
  190.    */
  191.   gtk_widget_show_all( g_pMainWindow );
  192.   resetBoard();
  193.   gtk_main();
  194.  
  195.   /*
  196.    * Convention is that zero indicates no errors during program run
  197.    */
  198.   return 0;
  199. }
  200. /* end of main */
  201.  
  202.  
  203.  
  204. /*
  205. * updateBoard : Show status of game square at specific location on the board
  206. *
  207. * row           Row label
  208. * col           Column label
  209. * status        Game square status
  210. *
  211. */
  212. void updateBoard( int row, char col, int status )
  213. {
  214.   int       iCounterA, iCounterB;
  215.   int       iRow = -1;
  216.   char      cColumn = 'A';
  217.   GtkWidget *pLabel = NULL;
  218.  
  219.   for ( iRow = 1, iCounterA = 0; iCounterA < BOARD_SIZE; iCounterA++, iRow++ )
  220.   {
  221.     for ( cColumn = 'A', iCounterB = 0; iCounterB < BOARD_SIZE; iCounterB++, cColumn++ )
  222.     {
  223.       if ( row == iRow && col == cColumn )
  224.       {
  225.         switch ( status )
  226.         {
  227.         case WHITE_SQUARE :
  228.           pLabel = createImageLabel( g_pMainWindow,
  229.                                      g_pGameBoard[ iCounterA ][ iCounterB ],
  230.                                      NULL, XPM_SQUARE_WHITE );
  231.           break;
  232.         case BLACK_SQUARE :
  233.           pLabel = createImageLabel( g_pMainWindow,
  234.                                      g_pGameBoard[ iCounterA ][ iCounterB ],
  235.                                      NULL, XPM_SQUARE_BLACK );
  236.           break;
  237.         case EMPTY_SQUARE :
  238.           pLabel = createImageLabel( g_pMainWindow,
  239.                                      g_pGameBoard[ iCounterA ][ iCounterB ],
  240.                                      NULL, XPM_SQUARE_EMPTY );
  241.           break;
  242.         default :
  243.           return;
  244.         }
  245.  
  246.         replaceContainerWidgets( g_pGameBoard[ iCounterA ][ iCounterB ], pLabel );
  247.         return;
  248.       }
  249.     }
  250.   }
  251. }
  252. /* end of updateBoard */
  253.  
  254.  
  255.  
  256. /*
  257. * resetBoard : Reset all game squares on the board to be all empty
  258. *
  259. */
  260. void resetBoard()
  261. {
  262.   int       iCounterA, iCounterB;
  263.   GtkWidget *pLabel = NULL;
  264.  
  265.   for ( iCounterA = 0; iCounterA < BOARD_SIZE; iCounterA++ )
  266.   {
  267.     for ( iCounterB = 0; iCounterB < BOARD_SIZE; iCounterB++ )
  268.     {
  269.       pLabel = createImageLabel( g_pMainWindow,
  270.                                  g_pGameBoard[ iCounterA ][ iCounterB ],
  271.                                  NULL, XPM_SQUARE_EMPTY );
  272.       replaceContainerWidgets( g_pGameBoard[ iCounterA ][ iCounterB ], pLabel );
  273.     }
  274.   }
  275. }
  276. /* end of resetBoard */
  277.  
  278.  
  279.  
  280. /*
  281. * displayMessage : Appends message to game message console
  282. *
  283. * message          Text message to display
  284. *
  285. */
  286. void displayMessage( char* message )
  287. {
  288.   if ( message != NULL && g_pMessageBox != NULL )
  289.   {
  290.     gtk_text_insert( GTK_TEXT( g_pMessageBox ), NULL, NULL, NULL, message, -1 );
  291.     gtk_text_insert( GTK_TEXT( g_pMessageBox ), NULL, NULL, NULL, "\n", -1 );
  292.   }
  293. }
  294. /* end of displayMessage */
  295.  
  296.  
  297.  
  298. /*
  299. * notifyInit : Send message to opponent initiating a game
  300. *
  301. * host         String of opponent host IP address and port number
  302. *
  303. */
  304. int notifyInit( char *host )
  305. {
  306.   GameEvent *pEvent = NULL;
  307.   gchar     *pMsg = NULL;
  308.  
  309.   g_pTargetIP = NULL;
  310.   g_iTargetPort = NETWORK_PORT_MIN;
  311.  
  312.   if ( host == NULL ) { return FALSE; }
  313.   parseHostStr( host, &g_pTargetIP, &g_iTargetPort );
  314.  
  315.   if ( ( g_pTargetIP != NULL ) && ( strlen( g_pTargetIP ) > 1 ) && ( g_iTargetPort != NETWORK_SOCKET_CLOSED ) )
  316.   {
  317.     pEvent = createGameEvent();
  318.     pEvent->eventType = MESSAGE_INIT_EVENT;
  319.     pMsg = createNetworkMsg( pEvent );
  320.     if ( sendMsg( g_pTargetIP, g_iTargetPort, pMsg ) > 0 )
  321.     {
  322.       g_free( pMsg );
  323.       g_free( pEvent );
  324.       return TRUE;
  325.     }
  326.     g_free( pMsg );
  327.     g_free( pEvent );
  328.   }
  329.  
  330.   return FALSE;
  331. }
  332. /* end of notifyInit */
  333.  
  334.  
  335.  
  336. /*
  337. * notifyPlay : Send message to opponent that you are ready to start playing
  338. *
  339. */
  340. int notifyPlay()
  341. {
  342.   GameEvent *pEvent = NULL;
  343.   gchar     *pMsg = NULL;
  344.  
  345.   if ( ( g_pTargetIP != NULL ) && ( strlen( g_pTargetIP ) > 1 ) && ( g_iTargetPort != NETWORK_SOCKET_CLOSED ) )
  346.   {
  347.     pEvent = createGameEvent();
  348.     pEvent->eventType = MESSAGE_PLAY_EVENT;
  349.     pMsg = createNetworkMsg( pEvent );
  350.     if ( sendMsg( g_pTargetIP, g_iTargetPort, pMsg ) > 0 )
  351.     {
  352.       g_free( pMsg );
  353.       g_free( pEvent );
  354.       return TRUE;
  355.     }
  356.     g_free( pMsg );
  357.     g_free( pEvent );
  358.   }
  359.  
  360.   return FALSE;
  361. }
  362. /* end of notifyPlay */
  363.  
  364.  
  365.  
  366. /*
  367. * putNewDisc : Place a new disc on a specific location on the game board
  368. *
  369. * row          Row label
  370. * col          Column label
  371. *
  372. */
  373. void putNewDisc( int row, char col )
  374. {
  375.   GameEvent *pEvent = NULL;
  376.   gchar     *pMsg = NULL;
  377.  
  378.   if ( ( g_pTargetIP != NULL ) && ( strlen( g_pTargetIP ) > 1 ) && ( g_iTargetPort != NETWORK_SOCKET_CLOSED ) )
  379.   {
  380.     pEvent = createGameEvent();
  381.     pEvent->eventType = MESSAGE_BOARD_EVENT;
  382.     pEvent->eventRow = row;
  383.     pEvent->eventCol = col;
  384.     pMsg = createNetworkMsg( pEvent );
  385.     sendMsg( g_pTargetIP, g_iTargetPort, pMsg );
  386.     g_free( pMsg );
  387.     g_free( pEvent );
  388.   }
  389. }
  390. /* end of putNewDisc */
  391.  
  392.  
  393.  
  394. /*
  395. * respond : Send confirmation response to opponent's placement of a new disc
  396. *
  397. * row       Row label
  398. * col       Column label
  399. *
  400. */
  401. void respond( int row, char col )
  402. {
  403.   GameEvent *pEvent = NULL;
  404.   gchar     *pMsg = NULL;
  405.  
  406.   if ( ( g_pTargetIP != NULL ) && ( strlen( g_pTargetIP ) > 1 ) && ( g_iTargetPort != NETWORK_SOCKET_CLOSED ) )
  407.   {
  408.     pEvent = createGameEvent();
  409.     pEvent->eventType = MESSAGE_RESPONSE_EVENT;
  410.     pEvent->eventRow = row;
  411.     pEvent->eventCol = col;
  412.     pMsg = createNetworkMsg( pEvent );
  413.     sendMsg( g_pTargetIP, g_iTargetPort, pMsg );
  414.     g_free( pMsg );
  415.     g_free( pEvent );
  416.   }
  417. }
  418. /* end of respond */
  419.  
  420.  
  421.  
  422. /*
  423. * getPortNumber : Returns port number of current game program
  424. *
  425. * Returns port number if server is connected, else returns NETWORK_SOCKET_CLOSED value
  426. *
  427. */
  428. int getPortNumber( void ) { return g_iHostPort; }
  429. /* end of getPortNumber */
  430.  
  431.  
  432.  
  433. /*
  434. * getIPAddress : Returns IP address of current computer
  435. *
  436. * Returns string representing the computer IP address
  437. *
  438. */
  439. char* getIPAddress( void )
  440. {
  441.   struct hostent *pHost = NULL;
  442.   char   hostname[ 256 ];
  443.  
  444.   if ( gethostname( hostname, sizeof( char ) * 256 ) != -1 ) { pHost = gethostbyname( hostname ); }
  445.   if ( pHost != NULL ) { return g_strdup_printf( "%s", inet_ntoa( * ( ( struct in_addr * ) pHost->h_addr ) ) ); }
  446.   return NULL;
  447. }
  448. /* end of getIPAddress */
  449.  
  450.  
  451.  
  452. /*
  453. * endGame    : Send message to opponent indicating end of game
  454. *
  455. * exitFlag     Indicates if application should also exit after end of game message has been sent
  456. *
  457. */
  458. void endGame( int exitFlag )
  459. {
  460.   GameEvent *pEvent = NULL;
  461.   gchar     *pMsg = NULL;
  462.  
  463.   if ( ( g_pTargetIP != NULL ) && ( strlen( g_pTargetIP ) > 1 ) && ( g_iTargetPort != NETWORK_SOCKET_CLOSED ) )
  464.   {
  465.     pEvent = createGameEvent();
  466.     pEvent->eventType = MESSAGE_END_EVENT;
  467.     pMsg = createNetworkMsg( pEvent );
  468.     sendMsg( g_pTargetIP, g_iTargetPort, pMsg );
  469.     g_free( pMsg );
  470.     g_free( pEvent );
  471.   }
  472.  
  473.   if ( exitFlag == TRUE ) { exitMainApp(); }
  474. }
  475. /* end of endGame */
  476.  
  477.  
  478.  
  479. /*
  480. * describeEvent : Display text description of event data
  481. *
  482. * event           Game event structure
  483. * window          TRUE if description should appear in window message console
  484. * output          TRUE if description should be printed to standard output
  485. *
  486. */
  487. static void describeEvent( GameEvent *event, int window, int output )
  488. {
  489.   gchar *pMessage = NULL;
  490.  
  491.   if ( event == NULL ) { return; }
  492.  
  493.   switch ( event->eventType )
  494.     {
  495.     case REQUEST_PLAY_EVENT :
  496.       pMessage = g_strdup_printf( "Received event: [%ld] REQUEST_PLAY_EVENT with %s",
  497.                                   event->eventID, event->eventData );
  498.       break;
  499.     case REQUEST_INFO_EVENT :
  500.       pMessage = g_strdup_printf( "Received event: [%ld] REQUEST_INFO_EVENT", event->eventID );
  501.       break;
  502.     case REQUEST_BOARD_EVENT :
  503.       pMessage = g_strdup_printf( "Received event: [%ld] REQUEST_BOARD_EVENT at %d-%c",
  504.                                   event->eventID, event->eventRow, event->eventCol );
  505.       break;
  506.     case MESSAGE_INIT_EVENT :
  507.       pMessage = g_strdup_printf( "Received event: [%ld] MESSAGE_INIT_EVENT from %s",
  508.                                   event->eventID, event->eventData );
  509.       break;
  510.     case MESSAGE_PLAY_EVENT :
  511.       pMessage = g_strdup_printf( "Received event: [%ld] MESSAGE_PLAY_EVENT from %s",
  512.                                   event->eventID, event->eventData );
  513.       break;
  514.     case MESSAGE_BOARD_EVENT :
  515.       pMessage = g_strdup_printf( "Received event: [%ld] MESSAGE_BOARD_EVENT at %d-%c",
  516.                                   event->eventID, event->eventRow, event->eventCol );
  517.       break;
  518.     case MESSAGE_RESPONSE_EVENT :
  519.       pMessage = g_strdup_printf( "Received event: [%ld] MESSAGE_RESPONSE_EVENT at %d-%c",
  520.                                   event->eventID, event->eventRow, event->eventCol );
  521.       break;
  522.     case MESSAGE_END_EVENT :
  523.       pMessage = g_strdup_printf( "Received event: [%ld] MESSAGE_END_EVENT", event->eventID );
  524.       break;
  525.     default :
  526.       pMessage = g_strdup_printf( "Received event: [%ld] UNKNOWN TYPE", event->eventID );
  527.       break;
  528.     }
  529.  
  530.   if ( window == TRUE ) { displayMessage( pMessage ); }
  531.   if ( output == TRUE ) { g_print( pMessage ); g_print( "\n" ); }
  532.   g_free( pMessage );
  533. }
  534. /* end of describeEvent */
  535.  
  536.  
  537.  
  538. /*
  539. * initHost : Open datagram socket to listen to incoming data
  540. *
  541. * Returns TRUE if socket succesfully opened, or FALSE if no available ports
  542. *
  543. */
  544. static gboolean initHost()
  545. {
  546.   struct sockaddr_in sAddr;
  547.  
  548.   /*
  549.    * Close if there is a currently opened socket
  550.    */
  551.   if ( g_iHostSocket != NETWORK_SOCKET_CLOSED ) { close( g_iHostSocket ); }
  552.  
  553.   /*
  554.    * Creates new socket connection
  555.    */
  556.   if ( ( g_iHostSocket = socket( AF_INET, SOCK_DGRAM, 0 ) ) == NETWORK_SOCKET_CLOSED ) { return FALSE; }
  557.  
  558.   /*
  559.    * Attempt to listen on a specific port number
  560.    */
  561.   sAddr.sin_family = AF_INET;
  562.   sAddr.sin_port = htons( g_iHostPort );
  563.   sAddr.sin_addr.s_addr = htonl( INADDR_ANY );
  564.   memset( &(sAddr.sin_zero), '\0', 8 );
  565.   while ( g_iHostPort <= NETWORK_PORT_MAX )
  566.   {
  567.     if ( bind( g_iHostSocket, (struct sockaddr *) &sAddr, sizeof( struct sockaddr ) ) != NETWORK_SOCKET_CLOSED )
  568.     {
  569.       /*
  570.        * Socket is now listening on a valid and available port number
  571.        */
  572.       break;
  573.     }
  574.  
  575.     /*
  576.      * Increment from minimum number until maximum number is reached
  577.      */
  578.     g_iHostPort++;
  579.     sAddr.sin_port = htons( g_iHostPort );
  580.   }
  581.  
  582.   /*
  583.    * Set socket to non-blocking mode
  584.    */
  585.   fcntl( g_iHostSocket, F_SETFL, O_NONBLOCK);
  586.   return TRUE;
  587. }
  588. /* end of initHost */
  589.  
  590.  
  591.  
  592. /*
  593. * cbReadMsg : Callback function to check and read incoming application messages
  594. *
  595. * Returns TRUE to continue receiving events, or FALSE to ignore future events
  596. *
  597. * data        Pointer to user-defined data
  598. *
  599. */
  600. static gboolean cbReadMsg( gpointer data )
  601. {
  602.   struct    sockaddr_in sOppAddr;
  603.   char      pReadBuf[ NETWORK_BUFFER_SIZE ];
  604.   int       iAddrLen, iNumBytes;
  605.   GameEvent *pEvent = NULL;
  606.   gchar     *pMessage = NULL;
  607.   gchar     *pToken = NULL;
  608.   gchar     *pTargetIP = NULL;
  609.   int       iTargetPort = NETWORK_SOCKET_CLOSED;
  610.  
  611.   /*
  612.    * No action required if socket is closed
  613.    */
  614.   if ( g_iHostSocket == NETWORK_SOCKET_CLOSED ) { return TRUE; }
  615.  
  616.   /*
  617.    * Check to see if a datagram is available
  618.    */
  619.   iAddrLen = sizeof( struct sockaddr );
  620.   if ( ( iNumBytes = recvfrom( g_iHostSocket, pReadBuf, NETWORK_BUFFER_SIZE - 1, 0,
  621.                                (struct sockaddr *) &sOppAddr, &iAddrLen ) ) > 0 )
  622.   {
  623.     /*
  624.      * Datagram with data found
  625.      */
  626.     pReadBuf[ iNumBytes ] = '\0';
  627.     g_print( g_strdup_printf( "Network: %d bytes from %s [%s]\n",
  628.                               iNumBytes, inet_ntoa( sOppAddr.sin_addr ), pReadBuf ) );
  629.  
  630.     pMessage = g_strdup_printf( "%s", pReadBuf );
  631.     pToken = (gchar *) strtok( pMessage, NETWORK_ADDR_DELIMITERS );
  632.     if ( pToken != NULL )
  633.     {
  634.       pTargetIP = g_strdup_printf( "%s", pToken );
  635.       pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  636.       if ( pToken != NULL )
  637.       {
  638.         iTargetPort = (int) strtol( pToken, NULL, 0 );
  639.         if ( ( strcmp( pTargetIP, getIPAddress() ) != 0 ) || ( iTargetPort != getPortNumber() ) )
  640.         {
  641.           pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  642.           if ( pToken != NULL )
  643.           {
  644.             pEvent = createGameEvent();
  645.             pEvent->eventType = (int) strtol( pToken, NULL, 0 );
  646.             if ( ( pEvent->eventType == MESSAGE_INIT_EVENT ) || ( pEvent->eventType == MESSAGE_PLAY_EVENT ) )
  647.             {
  648.               pEvent->eventData = g_strdup_printf( "%s:%d", pTargetIP, iTargetPort );
  649.               describeEvent( pEvent, FALSE, TRUE );
  650.               gameEventsDispatcher( pEvent->eventID, pEvent->eventType,
  651.                                     pEvent->eventRow, pEvent->eventCol, pEvent->eventData );
  652.             }
  653.             else if ( ( pEvent->eventType == MESSAGE_BOARD_EVENT ) ||
  654.                       ( pEvent->eventType == MESSAGE_RESPONSE_EVENT ) )
  655.             {
  656.               if ( ( g_pTargetIP != NULL ) && ( strcmp( pTargetIP, g_pTargetIP ) == 0 ) &&
  657.                    ( g_iTargetPort == iTargetPort ) )
  658.               {
  659.                 pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  660.                 if ( pToken != NULL ) { pEvent->eventRow = (int) strtol( pToken, NULL, 0 ); }
  661.                 pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  662.                 if ( pToken != NULL ) { pEvent->eventCol = pToken[ 0 ]; }
  663.                 pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  664.                 if ( pToken != NULL ) { pEvent->eventStatus = (int) strtol( pToken, NULL, 0 ); }
  665.                 describeEvent( pEvent, FALSE, TRUE );
  666.                 gameEventsDispatcher( pEvent->eventID, pEvent->eventType,
  667.                                       pEvent->eventRow, pEvent->eventCol, pEvent->eventData );
  668.               }
  669.             }
  670.             else if ( pEvent->eventType == MESSAGE_END_EVENT )
  671.             {
  672.               if ( ( g_pTargetIP != NULL ) && ( strcmp( pTargetIP, g_pTargetIP ) == 0 ) &&
  673.                    ( g_iTargetPort == iTargetPort ) )
  674.               {
  675.                 describeEvent( pEvent, FALSE, TRUE );
  676.                 gameEventsDispatcher( pEvent->eventID, pEvent->eventType,
  677.                                       pEvent->eventRow, pEvent->eventCol, pEvent->eventData );
  678.               }
  679.             }
  680.           }
  681.         }
  682.       }
  683.       g_free( pTargetIP );
  684.     }
  685.     g_free( pMessage );
  686.   }
  687.  
  688.   return TRUE;
  689. }
  690. /* end of cbReadMsg */
  691.  
  692.  
  693.  
  694. /*
  695. * sendMsg : Sends an application message across the network
  696. *
  697. * Returns TRUE if entire message was sent
  698. *
  699. * host      String container IP address of target host
  700. * port      Number at target host
  701. * data      String of data to send
  702. *
  703. */
  704. static gboolean sendMsg( gpointer host, gint port, gpointer data )
  705. {
  706.   struct sockaddr_in sTargetAddr;
  707.   struct hostent *pTargetHost;
  708.   int iTargetSocket, iDataSent;
  709.  
  710.   /*
  711.    * Check that host IP address is valid
  712.    */
  713.   if ( ( pTargetHost = gethostbyname( host ) ) == NULL ) { return FALSE; }
  714.  
  715.   /*
  716.    * Only need to send if there is data
  717.    */
  718.   if ( ( data == NULL ) || ( strlen( data ) <= 0 ) ) { return TRUE; }
  719.  
  720.   /*
  721.    * Prepare new socket for sending
  722.    */
  723.   if ( ( iTargetSocket = socket( AF_INET, SOCK_DGRAM, 0 ) ) == -1 ) { return FALSE; }
  724.   sTargetAddr.sin_family = AF_INET;
  725.   sTargetAddr.sin_port = htons( port );
  726.   sTargetAddr.sin_addr = *( (struct in_addr *) pTargetHost->h_addr );
  727.   memset( &(sTargetAddr.sin_zero), '\0', 8 );
  728.  
  729.   /*
  730.    * Send data
  731.    */
  732.   if ( ( iDataSent = sendto( iTargetSocket, data, strlen( data ), 0,
  733.                              (struct sockaddr *)&sTargetAddr, sizeof( struct sockaddr ) ) ) <= 0 )
  734.   {
  735.     close( iTargetSocket );
  736.     return FALSE;
  737.   }
  738.  
  739.   g_print( g_strdup_printf( "Network: %d bytes to %s:%d [%s]\n", iDataSent,
  740.                             inet_ntoa( sTargetAddr.sin_addr ), ntohs( sTargetAddr.sin_port ), data ) );
  741.   close( iTargetSocket );
  742.   return TRUE;
  743. }
  744. /* end of sendMsg */
  745.  
  746.  
  747.  
  748. /*
  749. * cbButtonClick : Callback function when any button is clicked
  750. *
  751. * button          Pointer to button
  752. * data            Pointer to user-defined data
  753. *
  754. */
  755. static void cbButtonClick( GtkWidget* button, gpointer data )
  756. {
  757.   int        iCounterA, iCounterB;
  758.   int        iRow = -1;
  759.   char       cColumn = 'A';
  760.   GameEvent  *pEvent = createGameEvent();
  761.   GtkWidget  *pDialogQuestion = NULL;
  762.  
  763.   /*
  764.    * Show dialog to confirm that user really wants to exit application now
  765.    */
  766.   if ( button == g_pButtonExit )
  767.   {
  768.     pDialogQuestion = gtk_label_new( NULL );
  769.     gtk_label_set_text( GTK_LABEL( pDialogQuestion ), APPLICATION_DIALOG_QUESTION );
  770.     g_pDialogNo = gtk_button_new();
  771.     gtk_container_add( GTK_CONTAINER( g_pDialogNo ), createImageLabel( g_pMainWindow,
  772.                                                                        g_pDialogNo,
  773.                                                                        APPLICATION_DIALOG_NO_STR,
  774.                                                                        XPM_BUTTON_DIALOG_NO ) );
  775.     g_pDialogPlay = NULL;
  776.     g_pDialogYes = gtk_button_new();
  777.     gtk_container_add( GTK_CONTAINER( g_pDialogYes ), createImageLabel( g_pMainWindow,
  778.                                                                         g_pDialogYes,
  779.                                                                         APPLICATION_DIALOG_YES_STR,
  780.                                                                         XPM_BUTTON_DIALOG_YES ) );
  781.     showDialogWindow( APPLICATION_DIALOG_STR, pDialogQuestion, g_pDialogNo, g_pDialogYes );
  782.     return;
  783.   }
  784.  
  785.   /*
  786.    * Show dialog to ask for host information when play request is issued
  787.    */
  788.   if ( button == g_pButtonPlay )
  789.   {
  790.     g_pDialogAnswer = gtk_entry_new_with_max_length( TEXT_ENTRY_MAX_LENGTH );
  791.     gtk_entry_set_editable( GTK_ENTRY( g_pDialogAnswer ), TRUE );
  792.     g_pDialogNo = gtk_button_new();
  793.     gtk_container_add( GTK_CONTAINER( g_pDialogNo ), createImageLabel( g_pMainWindow,
  794.                                                                        g_pDialogNo,
  795.                                                                        APPLICATION_DIALOG_HOST_NO,
  796.                                                                        XPM_BUTTON_DIALOG_NO ) );
  797.     g_pDialogYes = NULL;
  798.     g_pDialogPlay = gtk_button_new();
  799.     gtk_container_add( GTK_CONTAINER( g_pDialogPlay ), createImageLabel( g_pMainWindow,
  800.                                                                          g_pDialogPlay,
  801.                                                                          APPLICATION_DIALOG_HOST_YES,
  802.                                                                          XPM_BUTTON_DIALOG_YES ) );
  803.     showDialogWindow( APPLICATION_DIALOG_HOST_STR, g_pDialogAnswer, g_pDialogNo, g_pDialogPlay );
  804.     return;
  805.   }
  806.  
  807.   /*
  808.    * User decided to cancel dialog question, so handle here and no event is dispatched
  809.    */
  810.   if ( button == g_pDialogNo )
  811.   {
  812.     gtk_widget_destroy( g_pDialogWindow );
  813.     return;
  814.   }
  815.  
  816.   /*
  817.    * User confirmed that that they want to exit application
  818.    */
  819.   if ( button == g_pDialogYes ) { endGame( TRUE ); return; }
  820.  
  821.   /*
  822.    * Dispatch these events to student functions
  823.    */
  824.   if ( button == g_pDialogPlay )
  825.   {
  826.     pEvent->eventType = REQUEST_PLAY_EVENT;
  827.     pEvent->eventData = g_strdup_printf( "%s", gtk_entry_get_text( GTK_ENTRY( g_pDialogAnswer ) ) );
  828.     gtk_widget_destroy( g_pDialogWindow );
  829.   }
  830.  
  831.   if ( button == g_pButtonInfo ) { pEvent->eventType = REQUEST_INFO_EVENT; }
  832.  
  833.   for ( iRow = 1, iCounterA = 0; iRow > 0 && iCounterA < BOARD_SIZE; iCounterA++, iRow++ )
  834.   {
  835.     for ( cColumn = 'A', iCounterB = 0; iRow > 0 && iCounterB < BOARD_SIZE; iCounterB++, cColumn++ )
  836.     {
  837.       if ( button == g_pGameBoard[ iCounterA ][ iCounterB ] )
  838.       {
  839.         pEvent->eventType = REQUEST_BOARD_EVENT; pEvent->eventCol = cColumn; pEvent->eventRow = iRow;
  840.         iRow = -1; continue;
  841.       }
  842.     }
  843.   }
  844.  
  845.   describeEvent( pEvent, FALSE, TRUE );
  846.   gameEventsDispatcher( pEvent->eventID, pEvent->eventType,
  847.                         pEvent->eventRow, pEvent->eventCol, pEvent->eventData );
  848. }
  849. /* end of cbButtonClick */
  850.  
  851.  
  852.  
  853. /*
  854. * cbMainWindowClose : Callback function when application windows are closing
  855. *
  856. * Returns TRUE to keep window open and FALSE to close window.
  857. *
  858. * window              Pointer to window
  859. * xevent              Pointer to X event
  860. * data                Pointer to user-defined data
  861. *
  862. */
  863. static gint cbMainWindowClose( GtkWidget* window, GdkEventAny* xevent, gpointer data )
  864. {
  865.   if ( window == g_pMainWindow ) { endGame( TRUE ); return FALSE; }
  866.   return TRUE;
  867. }
  868. /* end of cbMainWindowClose */
  869.  
  870.  
  871.  
  872. /*
  873. * exitMainApp : Perform standard application exit tasks
  874. *
  875. */
  876. static void exitMainApp( void )
  877. {
  878.   gtk_main_quit();
  879.   if ( g_iHostSocket != NETWORK_SOCKET_CLOSED ) { close( g_iHostSocket ); }
  880.   g_print( "\nEND: %s\n\n", APPLICATION_NAME_STR );
  881. }
  882. /* end of exitMainApp */
  883.  
  884.  
  885.  
  886. /*
  887. * replaceContainerWidgets : Remove current widgets in a container and replace with new widget
  888. *
  889. * container                 Parent widget
  890. * child                     New widget
  891. *
  892. */
  893. static void replaceContainerWidgets( GtkWidget *container, GtkWidget *child )
  894. {
  895.   GList *children = NULL;
  896.  
  897.   gtk_widget_hide_all( container );
  898.   children = gtk_container_children( GTK_CONTAINER( container ) );
  899.   while ( children != NULL )
  900.   {
  901.     gtk_container_remove( GTK_CONTAINER( container ), GTK_WIDGET( children->data ) );
  902.     children = children->next;
  903.   }
  904.  
  905.   gtk_container_add( GTK_CONTAINER( container ), child );
  906.   gtk_widget_show_all( container )
  907. }
  908. /* end of replaceContainerWidgets */
  909.  
  910.  
  911.  
  912. /*
  913. * setWidgetColors : Set colors for widget style
  914. *
  915. * widget            Target widget to set colors
  916. * normalColorBG     Color string for normal background
  917. * hoverColorBG      Color string for background when mouse is over widget
  918. * activeColorBG     Color string for background when mouse is pressed
  919. * baseColorBG       Color string for text widget backgrounds
  920. *
  921. */
  922. static void setWidgetColors( GtkWidget *widget,
  923.                              gchar *normalColorBG,
  924.                              gchar *hoverColorBG,
  925.                              gchar *activeColorBG,
  926.                              gchar *baseColorBG )
  927. {
  928.   GdkColor    tNormalColorBG;
  929.   GdkColor    tPrelightColorBG;
  930.   GdkColor    tActiveColorBG;
  931.   GdkColor    tBaseColorBG;
  932.   GtkRcStyle  *pStyle = gtk_rc_style_new();
  933.  
  934.   if ( normalColorBG != NULL )
  935.   {
  936.     gdk_color_parse( normalColorBG, &tNormalColorBG );
  937.     pStyle->bg[ GTK_STATE_NORMAL ] = tNormalColorBG;
  938.     pStyle->color_flags[ GTK_STATE_NORMAL ] |= GTK_RC_BG;
  939.   }
  940.  
  941.   if ( hoverColorBG != NULL )
  942.   {
  943.     gdk_color_parse( hoverColorBG, &tPrelightColorBG );
  944.     pStyle->bg[ GTK_STATE_PRELIGHT ] = tPrelightColorBG;
  945.     pStyle->color_flags[ GTK_STATE_PRELIGHT ] |= GTK_RC_BG;
  946.   }
  947.  
  948.   if ( activeColorBG != NULL )
  949.   {
  950.     gdk_color_parse( activeColorBG, &tActiveColorBG );
  951.     pStyle->bg[ GTK_STATE_ACTIVE ] = tActiveColorBG;
  952.     pStyle->color_flags[ GTK_STATE_ACTIVE ] |= GTK_RC_BG;
  953.   }
  954.  
  955.   if ( baseColorBG != NULL )
  956.   {
  957.     gdk_color_parse( baseColorBG, &tBaseColorBG );
  958.     pStyle->base[ GTK_STATE_NORMAL ] = tBaseColorBG;
  959.     pStyle->color_flags[ GTK_STATE_NORMAL ] |= GTK_RC_BASE;
  960.   }
  961.  
  962.   gtk_widget_modify_style( widget, pStyle );
  963.   gtk_rc_style_unref( pStyle );
  964. }
  965. /* end of setWidgetColors */
  966.  
  967.  
  968.  
  969. /*
  970. * createNetworkMsg : Convert a game event into a string for sending across network
  971. *
  972. * Returns pointer to string
  973. *
  974. * event              Event to be converted
  975. *
  976. */
  977. static char *createNetworkMsg( GameEvent *event )
  978. {
  979.   gchar *pMsg = NULL;
  980.  
  981.   if ( event == NULL ) { return NULL; }
  982.  
  983.   switch ( event->eventType )
  984.     {
  985.     case MESSAGE_INIT_EVENT :
  986.       pMsg = g_strdup_printf( "%s:%d:%d:%d:%c:%d:%ld",
  987.                               getIPAddress(), getPortNumber(),
  988.                               event->eventType, event->eventRow, event->eventCol, event->eventStatus,
  989.                               event->eventID );
  990.       break;
  991.     case MESSAGE_PLAY_EVENT :
  992.       pMsg = g_strdup_printf( "%s:%d:%d:%d:%c:%d:%ld",
  993.                               getIPAddress(), getPortNumber(),
  994.                               event->eventType, event->eventRow, event->eventCol, event->eventStatus,
  995.                               event->eventID );
  996.       break;
  997.     case MESSAGE_BOARD_EVENT :
  998.       pMsg = g_strdup_printf( "%s:%d:%d:%d:%c:%d:%ld",
  999.                               getIPAddress(), getPortNumber(),
  1000.                               event->eventType, event->eventRow, event->eventCol, event->eventStatus,
  1001.                               event->eventID );
  1002.       break;
  1003.     case MESSAGE_RESPONSE_EVENT :
  1004.       pMsg = g_strdup_printf( "%s:%d:%d:%d:%c:%d:%ld",
  1005.                               getIPAddress(), getPortNumber(),
  1006.                               event->eventType, event->eventRow, event->eventCol, event->eventStatus,
  1007.                               event->eventID );
  1008.       break;
  1009.     case MESSAGE_END_EVENT :
  1010.       pMsg = g_strdup_printf( "%s:%d:%d:%d:%c:%d:%ld",
  1011.                               getIPAddress(), getPortNumber(),
  1012.                               event->eventType, event->eventRow, event->eventCol, event->eventStatus,
  1013.                               event->eventID );
  1014.       break;
  1015.     default :
  1016.       break;
  1017.     }
  1018.  
  1019.   return pMsg;
  1020. }
  1021. /* end of createNetworkMsg */
  1022.  
  1023.  
  1024.  
  1025. /*
  1026. * createGameEvent : Create a new game event structure and initialise it
  1027. *
  1028. * Returns pointer to new event structure
  1029. *
  1030. */
  1031. static GameEvent *createGameEvent( void )
  1032. {
  1033.   if ( g_iNextGameEventID == 0 ) { g_iNextGameEventID = time( NULL ); }
  1034.  
  1035.   GameEvent *pEvent = g_new( GameEvent, 1 );
  1036.   pEvent->eventID = ++g_iNextGameEventID;
  1037.   pEvent->eventType = 0;
  1038.   pEvent->eventRow = 0;
  1039.   pEvent->eventCol = 'A';
  1040.   pEvent->eventStatus = EMPTY_SQUARE;
  1041.   pEvent->eventData = NULL;
  1042.   return pEvent;
  1043. }
  1044. /* end of createGameEvent */
  1045.  
  1046.  
  1047.  
  1048. /*
  1049. * createImageLabel : Create image and/or text label for a widget
  1050. *
  1051. * container          Application window to derive
  1052. */
  1053. static GtkWidget *createImageLabel( GtkWidget *appWindow,
  1054.                                     GtkWidget *container,
  1055.                                     gchar     *labelText,
  1056.                                     gchar     *labelImage )
  1057. {
  1058.   GdkBitmap *pMask = NULL;
  1059.   GtkStyle  *pStyle = NULL;
  1060.   GtkWidget *pLayout = gtk_hbox_new( FALSE, 0 );
  1061.  
  1062.   if ( labelImage != NULL )
  1063.   {
  1064.     pStyle = gtk_widget_get_style( container );
  1065.     gtk_box_pack_start( GTK_BOX( pLayout ),
  1066.                         gtk_pixmap_new( gdk_pixmap_create_from_xpm( appWindow->window, &pMask,
  1067.                                                                     &pStyle->bg[ GTK_STATE_NORMAL ],
  1068.                                                                     labelImage ), pMask ),
  1069.                         TRUE, FALSE, 0 );
  1070.   }
  1071.  
  1072.   if ( labelText != NULL )
  1073.   {
  1074.     gtk_box_pack_start( GTK_BOX( pLayout ), gtk_label_new( labelText ), FALSE, FALSE, 5 );
  1075.   }
  1076.  
  1077.   return pLayout;
  1078. }
  1079. /* end of createImageLabel */
  1080.  
  1081.  
  1082.  
  1083. /*
  1084. * parseHostStr : Finds the host IP address and port number
  1085. *
  1086. * data           Combined text string
  1087. * hostIP         Pointer to variable storing host IP string
  1088. * hostPort       Pointer to variable storing host port number
  1089. *
  1090. */
  1091. static void parseHostStr( char* data, char** hostIP, int* hostPort )
  1092. {
  1093.   gchar *pHostTokens = NULL;
  1094.   gchar *pToken = NULL;
  1095.  
  1096.   if ( ( data == NULL ) || ( hostIP == NULL ) || ( hostPort == NULL ) ) { return; }
  1097.   pHostTokens = g_strdup_printf( "%s", data );
  1098.   pToken = (gchar *) strtok( pHostTokens, NETWORK_ADDR_DELIMITERS );
  1099.   if ( pToken != NULL ) { (char *)(*hostIP) = g_strdup_printf( "%s", pToken ); }
  1100.   pToken = (gchar *) strtok( NULL, NETWORK_ADDR_DELIMITERS );
  1101.   if ( pToken != NULL ) { (int)(*hostPort) = (int) strtol( pToken, NULL, 0 ); }
  1102.   g_free( pHostTokens );
  1103. }
  1104. /* end of parseHostStr */
  1105.  
  1106.  
  1107.  
  1108. /*
  1109. * showDialogWindow : Displays modal dialog window
  1110. *
  1111. * title              Text to use as dialog title
  1112. * item               Widget to display on top part of dialog window
  1113. * button1            Button to appear on left side of dialog window
  1114. * button2            Button to appear on right side of dialog window
  1115. *
  1116. */
  1117. static void showDialogWindow( gchar* title, GtkWidget* item, GtkWidget* button1, GtkWidget* button2 )
  1118. {
  1119.   g_pDialogWindow = gtk_dialog_new();
  1120.   gtk_widget_set_usize( g_pDialogWindow, APPLICATION_DIALOG_WIDTH, APPLICATION_DIALOG_HEIGHT );
  1121.   gtk_window_set_policy( GTK_WINDOW( g_pDialogWindow ), FALSE, FALSE, TRUE );
  1122.   gtk_window_set_title( GTK_WINDOW( g_pDialogWindow ), title );
  1123.   gtk_widget_realize( g_pDialogWindow );
  1124.   gtk_window_set_modal( GTK_WINDOW( g_pDialogWindow ), TRUE );
  1125.   gtk_box_pack_start( GTK_BOX( GTK_DIALOG( g_pDialogWindow )->vbox ), item, TRUE, TRUE, 5 );
  1126.   gtk_box_pack_start( GTK_BOX( GTK_DIALOG( g_pDialogWindow )->action_area ), button1, FALSE, FALSE, 5 );
  1127.   gtk_box_pack_start( GTK_BOX( GTK_DIALOG( g_pDialogWindow )->action_area ), button2, FALSE, FALSE, 5 );
  1128.   gtk_widget_show_all( g_pDialogWindow );
  1129.  
  1130.   gtk_signal_connect( GTK_OBJECT( g_pDialogWindow ), "delete_event",
  1131.                       GTK_SIGNAL_FUNC( cbMainWindowClose ), NULL );
  1132.   gtk_signal_connect( GTK_OBJECT( button1 ), "clicked",
  1133.                       GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1134.   gtk_signal_connect( GTK_OBJECT( button2 ), "clicked",
  1135.                       GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1136. }
  1137. /* end of showDialogWindow */
  1138.  
  1139.  
  1140.  
  1141. /*
  1142. * createAppWindow : Create initial application window
  1143. *
  1144. */
  1145. static void createAppWindow( void )
  1146. {
  1147.   /*
  1148.    * Temporary GTK widgets for graphics display
  1149.    */
  1150.   GtkWidget   *pActionFrame = NULL;
  1151.   GtkWidget   *pActionLayout = NULL;
  1152.   GtkWidget   *pGameBoardFrame = NULL;
  1153.   GtkWidget   *pGameBoardTable = NULL;
  1154.   GtkWidget   *pGridsLayout = NULL;
  1155.   GtkWidget   *pMsgBoxScrollBar = NULL;
  1156.   GtkWidget   *pMessageFrame = NULL;
  1157.   GtkWidget   *pMessageLayout = NULL;
  1158.   GtkWidget   *pAppWindowLayout = NULL;
  1159.  
  1160.   /*
  1161.    * Temporary variables to manipulate grid cells
  1162.    */
  1163.   int  iCounterA = 0;
  1164.   int  iCounterB = 0;
  1165.   char cColumnLabel = 'A';
  1166.   int  iRowLabel = 1;
  1167.  
  1168.   /*
  1169.    * Create main application window and set to default color, width and height
  1170.    */
  1171.   g_pMainWindow = gtk_window_new( GTK_WINDOW_TOPLEVEL );
  1172.   gtk_widget_set_usize( g_pMainWindow, APPLICATION_WINDOW_WIDTH, APPLICATION_WINDOW_HEIGHT );
  1173.   gtk_window_set_title( GTK_WINDOW( g_pMainWindow ), APPLICATION_NAME_STR );
  1174.   gtk_window_set_policy( GTK_WINDOW( g_pMainWindow ), FALSE, FALSE, TRUE );
  1175.   setWidgetColors( g_pMainWindow, COLOR_WINDOW_BG, NULL, NULL, NULL );
  1176.   gtk_widget_realize( g_pMainWindow );
  1177.  
  1178.   /*
  1179.    *  Create action buttons toolbar
  1180.    */
  1181.   pActionFrame = gtk_frame_new( NULL );
  1182.   gtk_frame_set_shadow_type( GTK_FRAME( pActionFrame ), GTK_SHADOW_ETCHED_OUT );
  1183.   pActionLayout = gtk_hbox_new( TRUE, 0 );
  1184.   g_pButtonPlay = gtk_button_new();
  1185.   gtk_container_add( GTK_CONTAINER( g_pButtonPlay ), createImageLabel( g_pMainWindow,
  1186.                                                                        g_pButtonPlay,
  1187.                                                                        APPLICATION_BUTTON_PLAY_STR,
  1188.                                                                        XPM_BUTTON_PLAY ) );
  1189.   gtk_box_pack_start( GTK_BOX( pActionLayout ), g_pButtonPlay, FALSE, FALSE, 20 );
  1190.   g_pButtonInfo = gtk_button_new();
  1191.   gtk_container_add( GTK_CONTAINER( g_pButtonInfo ), createImageLabel( g_pMainWindow,
  1192.                                                                        g_pButtonInfo,
  1193.                                                                        APPLICATION_BUTTON_INFO_STR,
  1194.                                                                        XPM_BUTTON_INFO ) );
  1195.   gtk_box_pack_start( GTK_BOX( pActionLayout ), g_pButtonInfo, FALSE, FALSE, 0 );
  1196.   g_pButtonExit = gtk_button_new();
  1197.   gtk_container_add( GTK_CONTAINER( g_pButtonExit ), createImageLabel( g_pMainWindow,
  1198.                                                                        g_pButtonExit,
  1199.                                                                        APPLICATION_BUTTON_EXIT_STR,
  1200.                                                                        XPM_BUTTON_EXIT ) );
  1201.   gtk_box_pack_start( GTK_BOX( pActionLayout ), g_pButtonExit, FALSE, FALSE, 20 );
  1202.   gtk_container_add( GTK_CONTAINER( pActionFrame ), pActionLayout );
  1203.   pActionLayout = gtk_hbox_new( FALSE, 0 );
  1204.   gtk_box_pack_start( GTK_BOX( pActionLayout ), pActionFrame, TRUE, TRUE, 20 );
  1205.  
  1206.   /*
  1207.    * Create game board
  1208.    */
  1209.   pGameBoardFrame = gtk_frame_new( NULL );
  1210.   gtk_frame_set_label( GTK_FRAME( pGameBoardFrame ), APPLICATION_GAME_BOARD );
  1211.   gtk_frame_set_label_align( GTK_FRAME( pGameBoardFrame ), 0.05, 0.0 );
  1212.   gtk_frame_set_shadow_type( GTK_FRAME( pGameBoardFrame ), GTK_SHADOW_ETCHED_OUT );
  1213.   pGameBoardTable = gtk_table_new( BOARD_SIZE + 1, BOARD_SIZE + 1, TRUE );
  1214.   for ( iCounterA = 0, iRowLabel = 1; iCounterA <= BOARD_SIZE; iCounterA++ )
  1215.   {
  1216.     for ( iCounterB = 0, cColumnLabel = 'A'; iCounterB <= BOARD_SIZE; iCounterB++ )
  1217.     {
  1218.       /*
  1219.        * Insert column and row labels
  1220.        */
  1221.       if ( iCounterA == 0 )
  1222.       {
  1223.         if ( iCounterB != 0 )
  1224.         {
  1225.           gtk_table_attach_defaults( GTK_TABLE( pGameBoardTable ),
  1226.                                      gtk_label_new( g_strdup_printf( "%c", cColumnLabel ) ),
  1227.                                      iCounterB, iCounterB + 1, iCounterA, iCounterA + 1 );
  1228.           cColumnLabel++;
  1229.         }
  1230.         else
  1231.         {
  1232.           gtk_table_attach_defaults( GTK_TABLE( pGameBoardTable ), gtk_label_new( NULL ),
  1233.                                      iCounterB, iCounterB + 1, iCounterA, iCounterA + 1 );
  1234.         }
  1235.         continue;
  1236.       }
  1237.       if ( iCounterB == 0 )
  1238.       {
  1239.         gtk_table_attach_defaults( GTK_TABLE( pGameBoardTable ),
  1240.                                    gtk_label_new( g_strdup_printf( "%d", iRowLabel ) ),
  1241.                                    iCounterB, iCounterB + 1, iCounterA, iCounterA + 1 );
  1242.         iRowLabel++;
  1243.         continue;
  1244.       }
  1245.  
  1246.       /*
  1247.        * Add actual grid buttons
  1248.        */
  1249.       g_pGameBoard[ iCounterA - 1 ][ iCounterB - 1 ] = gtk_button_new();
  1250.       setWidgetColors( g_pGameBoard[ iCounterA - 1 ][ iCounterB - 1 ],
  1251.                        COLOR_GRID_BG, COLOR_GRID_HOVER_BG, COLOR_GRID_ACTIVE_BG, NULL );
  1252.       gtk_container_add( GTK_CONTAINER( g_pGameBoard[ iCounterA - 1 ][ iCounterB - 1 ] ),
  1253.                          createImageLabel( g_pMainWindow,
  1254.                                            g_pGameBoard[ iCounterA - 1 ][ iCounterB - 1 ],
  1255.                                            NULL, XPM_SQUARE_EMPTY ) );
  1256.       gtk_table_attach_defaults( GTK_TABLE( pGameBoardTable ),
  1257.                                  g_pGameBoard[ iCounterA - 1 ][ iCounterB - 1 ],
  1258.                                  iCounterB, iCounterB + 1, iCounterA, iCounterA + 1 );
  1259.     }
  1260.   }
  1261.   gtk_container_add( GTK_CONTAINER( pGameBoardFrame ), pGameBoardTable );
  1262.   pGridsLayout = gtk_hbox_new( TRUE, 0 );
  1263.   gtk_box_pack_start( GTK_BOX( pGridsLayout ), pGameBoardFrame, TRUE, TRUE, 20 );
  1264.  
  1265.   /*
  1266.    * Create message display box
  1267.    */
  1268.   pMessageFrame = gtk_frame_new( NULL );
  1269.   gtk_frame_set_label( GTK_FRAME( pMessageFrame ), APPLICATION_MSGBOX_STR );
  1270.   gtk_frame_set_label_align( GTK_FRAME( pMessageFrame ), 0.05, 0.0 );
  1271.   gtk_frame_set_shadow_type( GTK_FRAME( pMessageFrame ), GTK_SHADOW_ETCHED_OUT );
  1272.   g_pMessageBox = gtk_text_new( NULL, NULL );
  1273.   gtk_text_set_editable( GTK_TEXT( g_pMessageBox ), FALSE );
  1274.   gtk_text_set_word_wrap( GTK_TEXT( g_pMessageBox ), TRUE );
  1275.   setWidgetColors( g_pMessageBox, COLOR_MSGBOX_BG, NULL, NULL, COLOR_MSGBOX_BG );
  1276.   pMsgBoxScrollBar = gtk_vscrollbar_new( GTK_TEXT( g_pMessageBox )->vadj );
  1277.   pMessageLayout = gtk_hbox_new( FALSE, 0 );
  1278.   gtk_box_pack_start( GTK_BOX( pMessageLayout ), g_pMessageBox, TRUE, TRUE, 0 );
  1279.   gtk_box_pack_start( GTK_BOX( pMessageLayout ), pMsgBoxScrollBar, FALSE, FALSE, 0 );
  1280.   gtk_container_add( GTK_CONTAINER( pMessageFrame ), pMessageLayout );
  1281.   pMessageLayout = gtk_hbox_new( TRUE, 0 );
  1282.   gtk_box_pack_start( GTK_BOX( pMessageLayout ), pMessageFrame, TRUE, TRUE, 20 );
  1283.  
  1284.   /*
  1285.    * Layout main application window
  1286.    */
  1287.   pAppWindowLayout = gtk_vbox_new( FALSE, 0 );
  1288.   gtk_box_pack_start( GTK_BOX( pAppWindowLayout ), pActionLayout, FALSE, FALSE, 20 );
  1289.   gtk_box_pack_start( GTK_BOX( pAppWindowLayout ), pGridsLayout, TRUE, TRUE, 0 );
  1290.   gtk_box_pack_start( GTK_BOX( pAppWindowLayout ), pMessageLayout, TRUE, TRUE, 20 );
  1291.   gtk_container_add( GTK_CONTAINER( g_pMainWindow ), pAppWindowLayout );
  1292.  
  1293.   /*
  1294.    * Install callback functions
  1295.    */
  1296.   gtk_signal_connect( GTK_OBJECT( g_pMainWindow ), "delete_event",
  1297.                       GTK_SIGNAL_FUNC( cbMainWindowClose ), NULL );
  1298.   gtk_signal_connect( GTK_OBJECT( g_pButtonPlay ), "clicked",
  1299.                       GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1300.   gtk_signal_connect( GTK_OBJECT( g_pButtonInfo ), "clicked",
  1301.                       GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1302.   gtk_signal_connect( GTK_OBJECT( g_pButtonExit ), "clicked",
  1303.                       GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1304.   for ( iCounterA = 0; iCounterA < BOARD_SIZE; iCounterA++ )
  1305.   {
  1306.     for ( iCounterB = 0; iCounterB < BOARD_SIZE; iCounterB++ )
  1307.     {
  1308.       gtk_signal_connect( GTK_OBJECT( g_pGameBoard[ iCounterA ][ iCounterB ] ),
  1309.                           "clicked", GTK_SIGNAL_FUNC( cbButtonClick ), NULL );
  1310.     }
  1311.   }
  1312. }
  1313. /* end of createAppWindow */



othello.c

  1. /*
  2. * othello.c : Student Project Assignment functions for Othello game.
  3. *
  4. * University of Portsmouth, UK
  5. * Introduction to Algorithms and Programming
  6. * Lecturer: C Nguyen
  7. *
  8. * Student Names   :  SAMPLE SOLUTION SOURCE CODE
  9. * Student Numbers :  SAMPLE SOLUTION SOURCE CODE
  10. *
  11. * Updated 8 Apr 2006
  12. *
  13. */
  14.  
  15. #include "othello.h"
  16.  
  17. /*
  18. * Game *usually* operates in these sequential modes
  19. */
  20. #define GAME_MODE_OFF               0
  21. #define GAME_MODE_INIT              1
  22. #define GAME_MODE_READY             2
  23. #define GAME_MODE_PLAYING           3
  24.  
  25. /*
  26. * Game mode for player
  27. */
  28. int g_iMyGame = GAME_MODE_OFF;
  29.  
  30. /*
  31. * Game mode for opponent
  32. * This may be different from game mode of player, since game play is asynchronous
  33. */
  34. int g_iOpponentGame = GAME_MODE_OFF;
  35.  
  36. /*
  37. * Player disc color
  38. */
  39. int g_iMyColor = BLACK_SQUARE;
  40.  
  41. /*
  42. * Opponent disc color
  43. */
  44. int g_iOpponentColor = BLACK_SQUARE;
  45.  
  46. /*
  47. * Turn mode is based on color
  48. */
  49. int g_iGameTurn = BLACK_SQUARE;
  50.  
  51. /*
  52. * Two dimensional array to track status of game board
  53. */
  54. int g_iBoard[ BOARD_SIZE ][ BOARD_SIZE ];
  55.  
  56. /*
  57. * Shared buffer for printing messages to the game console
  58. */
  59. char g_sBuffer[512];
  60.  
  61. /*
  62. * Please see function definitions for description of function prototypes
  63. */
  64. int  rowToIndex( int );
  65. int  colToIndex( char );
  66. int  indexToRow( int );
  67. char indexToCol( int );
  68. void startGame( char* );
  69. void myPlay( int, int, char );
  70. void opponentPlay( int, char );
  71. void playDisc( int, char, int );
  72. int  isMoveAvailable( int );
  73. int  isValidMove( int, char, int );
  74. int  flipDiscs( int, int, int, int );
  75. void flipOneDisc( int, int, int );
  76. int  calcScore( int );
  77. void showScore( void );
  78. void myPrompt( void );
  79. void finishGame( void );
  80.  
  81. /*
  82. * gameEventsDispatcher : Main function to handle game events
  83. *
  84. * eventID              : Unique ID number to identify events and help with debugging
  85. * eventType            : Indicates type of game event
  86. * eventRow             : The row identifier of the active board square
  87. * eventCol             : The column identifier of the active board square
  88. * eventData            : For REQUEST_PLAY_EVENT, MESSAGE_INIT_EVENT, MESSAGE_PLAY_EVENT event types,
  89. *                        this string indicates the IP address and port number of the opponent
  90. *
  91. */
  92. void gameEventsDispatcher( long eventID, int eventType, int eventRow, char eventCol, char *eventData )
  93. {
  94.   /*
  95.    * These events always cause the same activities regardless of current game mode
  96.    */
  97.   switch ( eventType )
  98.   {
  99.     /*
  100.      * Show game information to user and immediately return
  101.      */
  102.     case REQUEST_INFO_EVENT :
  103.       sprintf( g_sBuffer, "Your game instance is: %s:%d", getIPAddress(), getPortNumber() );
  104.       displayMessage( g_sBuffer );
  105.       return;
  106.  
  107.     /*
  108.      * Allow players to immediately start a new game and abandon any current game
  109.      */
  110.     case REQUEST_PLAY_EVENT :
  111.       /* Send courtesy notice to opponent if there is a game in progress */
  112.       if ( g_iOpponentGame != GAME_MODE_OFF )
  113.         endGame( FALSE );
  114.  
  115.       /* Attempt to initiate new game */
  116.       if ( notifyInit( eventData ) )
  117.       {
  118.         g_iMyGame = GAME_MODE_INIT;
  119.         g_iOpponentGame = GAME_MODE_OFF;
  120.  
  121.         /* Player who clicks the play button becomes player one and use black color */
  122.         g_iMyColor = BLACK_SQUARE;
  123.         g_iOpponentColor = WHITE_SQUARE;
  124.       }
  125.       return;
  126.  
  127.     /*
  128.      * Reset game if an end of game message is received
  129.      */
  130.     case MESSAGE_END_EVENT :
  131.       if ( g_iMyGame != GAME_MODE_OFF )
  132.         displayMessage( "Your opponent has given up! You win!" );
  133.  
  134.       g_iMyGame = GAME_MODE_OFF;
  135.       g_iOpponentGame = GAME_MODE_OFF;
  136.       return;
  137.   }
  138.  
  139.   /*
  140.    * For each game mode, some events are processed and all other events are simply ignored
  141.    */
  142.   switch ( g_iMyGame )
  143.   {
  144.     /*
  145.      * This is the default mode which indicates no game is in progress
  146.      */
  147.     case GAME_MODE_OFF :
  148.       if ( eventType == MESSAGE_INIT_EVENT )
  149.       {
  150.         g_iOpponentGame = GAME_MODE_INIT;
  151.         if ( notifyInit( eventData ) )
  152.           g_iMyGame = GAME_MODE_INIT;
  153.  
  154.         /* Player did NOT click play button, so becomes player two and use white color */
  155.         g_iMyColor = WHITE_SQUARE;
  156.         g_iOpponentColor = BLACK_SQUARE;
  157.       }
  158.       else
  159.       {
  160.         displayMessage( "Please click the Play button to start a new game, " );
  161.         displayMessage( "or click the Exit button to end the program." );
  162.       }
  163.       break;
  164.  
  165.     /*
  166.      * This mode indicates that initialisation is in progress
  167.      */
  168.     case GAME_MODE_INIT :
  169.       if ( ( eventType == MESSAGE_INIT_EVENT ) && ( g_iOpponentGame == GAME_MODE_OFF ) )
  170.       {
  171.         g_iOpponentGame = GAME_MODE_INIT;
  172.         if ( notifyPlay() )
  173.           g_iMyGame = GAME_MODE_READY;
  174.       }
  175.       if ( ( eventType == MESSAGE_PLAY_EVENT ) && ( g_iOpponentGame == GAME_MODE_INIT ) )
  176.       {
  177.         g_iOpponentGame = GAME_MODE_PLAYING;
  178.         if ( notifyPlay() )
  179.           startGame( eventData );
  180.       }
  181.       break;
  182.  
  183.     /*
  184.      * This mode indicates that initialisation is complete and waiting for opponent to play
  185.      */
  186.     case GAME_MODE_READY :
  187.       if ( ( eventType == MESSAGE_PLAY_EVENT ) && ( g_iOpponentGame == GAME_MODE_INIT ) )
  188.         startGame( eventData );
  189.       break;
  190.  
  191.     /*
  192.      * This mode indicates that a game is in progress, which should be the common mode for normal operation
  193.      */
  194.     case GAME_MODE_PLAYING :
  195.       switch ( eventType )
  196.       {
  197.         case REQUEST_BOARD_EVENT :
  198.           myPlay( REQUEST_BOARD_EVENT, eventRow, eventCol );
  199.           break;
  200.  
  201.         case MESSAGE_BOARD_EVENT :
  202.           opponentPlay( eventRow, eventCol );
  203.           break;
  204.  
  205.         case MESSAGE_RESPONSE_EVENT :
  206.           myPlay( MESSAGE_RESPONSE_EVENT, eventRow, eventCol );
  207.           break;
  208.       }
  209.       break;
  210.   }
  211. }
  212. /* end of gameEventsDispatcher */
  213.  
  214.  
  215.  
  216. /*
  217. * rowToIndex : Reduce row number by 1 since game board starts at 1, but array index starts at zero
  218. *
  219. * row        : Row number
  220. *
  221. */
  222. int rowToIndex( int row )
  223. {
  224.   return ( ( row ) - 1 );
  225. }
  226. /* end of rowToIndex */
  227.  
  228.  
  229.  
  230. /*
  231. * colToIndex : Convert column character label to numeric index for use with data arrays
  232. *
  233. * col        : Column label
  234. *
  235. */
  236. int colToIndex( char col )
  237. {
  238.   return ( ((int)(col)) - 65 );
  239. }
  240. /* end of colToIndex */
  241.  
  242.  
  243.  
  244. /*
  245. * indexToRow : Converts index number to row number
  246. *
  247. * idx        : Index number
  248. *
  249. */
  250. int indexToRow( int idx )
  251. {
  252.   return ( ( idx ) + 1 );
  253. }
  254. /* end of indexToRow */
  255.  
  256.  
  257.  
  258. /*
  259. * indexToRow : Converts index number to column label
  260. *
  261. * idx        : Index number
  262. *
  263. */
  264. char indexToCol( int idx )
  265. {
  266.   return ( (char)( idx + 65 ) );
  267. }
  268. /* end of indexToCol */
  269.  
  270.  
  271.  
  272. /*
  273. * startGame : Initialise variables and prepare for a new game
  274. *
  275. * opponent  : String containing IP address and port number of opponent
  276. *
  277. */
  278. void startGame( char* opponent )
  279. {
  280.   int iCol, iRow;
  281.  
  282.   /* Set game mode to playing */
  283.   g_iMyGame = GAME_MODE_PLAYING;
  284.   g_iOpponentGame = GAME_MODE_PLAYING;
  285.  
  286.   /* Reset game board array */
  287.   for ( iRow = 0; iRow < BOARD_SIZE; iRow++ )
  288.     for ( iCol = 0; iCol < BOARD_SIZE; iCol++ )
  289.       g_iBoard[ iRow ][ iCol ] = EMPTY_SQUARE;
  290.  
  291.   /* Reset game board display and show starting 4 discs */
  292.   resetBoard();
  293.   playDisc( 4, 'D', WHITE_SQUARE );
  294.   playDisc( 4, 'E', BLACK_SQUARE );
  295.   playDisc( 5, 'D', BLACK_SQUARE );
  296.   playDisc( 5, 'E', WHITE_SQUARE );
  297.  
  298.   /* Black color plays first */
  299.   sprintf( g_sBuffer, "Starting new game with %s", opponent );
  300.   displayMessage( g_sBuffer );
  301.   g_iGameTurn = BLACK_SQUARE;
  302.   if ( g_iMyColor == BLACK_SQUARE )
  303.   {
  304.     displayMessage( "Your game color is BLACK." );
  305.     displayMessage( "Please place a disc on the board." );
  306.   }
  307.   else
  308.   {
  309.     displayMessage( "Your game color is WHITE." );
  310.     displayMessage( "Please wait for your turn to play." );
  311.   }
  312. }
  313. /* end of startGame */
  314.  
  315.  
  316.  
  317. /*
  318. * myPlay : Process the player's move
  319. *
  320. * event  : Event type
  321. * row    : Row number
  322. * col    : Column label
  323. *
  324. */
  325. void myPlay( int event, int row, char col )
  326. {
  327.   /* Only continue if it's player's turn */
  328.   if ( g_iGameTurn != g_iMyColor )
  329.     return;
  330.  
  331.   /* Check if selected game square is a valid location for placement of new disc */
  332.   if ( ! isValidMove( row, col, g_iMyColor ) )
  333.   {
  334.     sprintf( g_sBuffer, "Game square %d-%c is not a valid move.", row, col );
  335.     displayMessage( g_sBuffer );
  336.     return;
  337.   }
  338.  
  339.   /* Send request to opponent before continuing to process new disc */
  340.   if ( event != MESSAGE_RESPONSE_EVENT )
  341.   {
  342.     putNewDisc( row, col );
  343.     return;
  344.   }
  345.  
  346.   /* Place the disc on the board, flip discs and show score */
  347.   playDisc( row, col, g_iMyColor );
  348.   showScore();
  349.  
  350.   /* If a valid move is not available to any color, then it's the end of the game */
  351.   if ( ! ( isMoveAvailable( BLACK_SQUARE ) || isMoveAvailable( WHITE_SQUARE ) ) )
  352.   {
  353.     finishGame();
  354.     return;
  355.   }
  356.  
  357.   /* Check if there is a move available to the opponent */
  358.   if ( ! isMoveAvailable( g_iOpponentColor ) )
  359.   {
  360.     displayMessage( "It's your turn again!" );
  361.     myPrompt();
  362.     return;
  363.   }
  364.  
  365.   /* Update game turn */
  366.   g_iGameTurn = g_iOpponentColor;
  367.   displayMessage( "Please wait for your opponent to play." );
  368. }
  369. /* end of myPlay */
  370.  
  371.  
  372.  
  373. /*
  374. * opponentPlay : Process the opponent's move
  375. *
  376. * row          : Row number
  377. * col          : Column label
  378. *
  379. */
  380. void opponentPlay( int row, char col )
  381. {
  382.   /* Send confirmation response to opponent */
  383.   respond( row, col );
  384.  
  385.   /* Only continue if it's the opponent's turn */
  386.   if ( g_iGameTurn != g_iOpponentColor )
  387.     return;
  388.  
  389.   /* Check if selected game square is a valid location for placement of new opponent disc */
  390.   if ( ! isValidMove( row, col, g_iOpponentColor ) )
  391.   {
  392.     sprintf( g_sBuffer, "Opponent move at %d-%c is not valid!", row, col );
  393.     displayMessage( g_sBuffer );
  394.     return;
  395.   }
  396.  
  397.   /* Place the disc on the board, flip discs and show score */
  398.   playDisc( row, col, g_iOpponentColor );
  399.   showScore();
  400.  
  401.   /* If a valid move is not available to any color, then it's the end of the game */
  402.   if ( ! ( isMoveAvailable( BLACK_SQUARE ) || isMoveAvailable( WHITE_SQUARE ) ) )
  403.   {
  404.     finishGame();
  405.     return;
  406.   }
  407.  
  408.   /* Check if there is a move available for player */
  409.   if ( ! isMoveAvailable( g_iMyColor ) )
  410.   {
  411.     displayMessage( "It's your opponent's turn again!" );
  412.     return;
  413.   }
  414.  
  415.   /* Update game turn */
  416.   g_iGameTurn = g_iMyColor;
  417.   myPrompt();
  418. }
  419. /* end of opponentPlay */
  420.  
  421.  
  422. /*
  423. * playDisc : Update game board and data array for one disc
  424. *
  425. * row      : Row number
  426. * col      : Column label
  427. * color    : New disc color
  428. *
  429. */
  430. void playDisc( int row, char col, int color )
  431. {
  432.   /* Place the disc on the board */
  433.   updateBoard( row, col, color );
  434.   g_iBoard[ rowToIndex( row ) ][ colToIndex( col ) ] = color;
  435.  
  436.   /* Flip all necessary discs */
  437.   flipDiscs( rowToIndex( row ), colToIndex( col ), color, TRUE );
  438. }
  439. /* end of playDisc */
  440.  
  441.  
  442.  
  443. /*
  444. * isMoveAvailable : Returns FALSE if there are no possible move for that color
  445. *
  446. * color           : Color to check for available move
  447. *
  448. */
  449. int isMoveAvailable( int color )
  450. {
  451.   int iRow, iCol;
  452.  
  453.   /* Check all empty squares to see if a valid move can be played */
  454.   for ( iRow = 0; iRow < BOARD_SIZE; iRow++ )
  455.     for ( iCol = 0; iCol < BOARD_SIZE; iCol++ )
  456.       if ( g_iBoard[ iRow ][ iCol ] == EMPTY_SQUARE )
  457.         if ( isValidMove( indexToRow( iRow ), indexToCol( iCol ), color ) )
  458.           return TRUE;
  459.  
  460.   /* Valid move not found */
  461.   return FALSE;
  462. }
  463. /* end of isMoveAvailable */
  464.  
  465.  
  466.  
  467. /*
  468. * isValidMove : Returns FALSE if the game board is not valid placement for that color
  469. *
  470. * row         : Row number
  471. * col         : Column label
  472. * color       : New disc color
  473. *
  474. */
  475. int isValidMove( int row, char col, int color )
  476. {
  477.   /* Square must be empty */
  478.   if ( g_iBoard[ rowToIndex( row ) ][ colToIndex( col ) ] != EMPTY_SQUARE )
  479.     return FALSE;
  480.  
  481.   /* Valid move requires that at least one disc can be flipped by placing at this square */
  482.   return flipDiscs( rowToIndex( row ), colToIndex( col ), color, FALSE );
  483. }
  484. /* end of isValidMove */
  485.  
  486.  
  487.  
  488. /*
  489. * flipDiscs : Check all directions to see if there are any discs to flip
  490. *
  491. * row       : Index to row of data array
  492. * col       : Index to column of data array
  493. * color     : Color of disc to check
  494. * flip      : TRUE if discs actually should be flipped, otherwise just check if there are discs to flip
  495. *
  496. */
  497. int flipDiscs( int row, int col, int color, int flip )
  498. {
  499.   int iRowIdx, iColIdx, iFlipRow, iFlipCol;
  500.  
  501.   /* Look at row to the left of the square */
  502.   for ( iColIdx = col - 1; iColIdx >= 0; iColIdx-- )
  503.   {
  504.     if ( g_iBoard[ row ][ iColIdx ] == EMPTY_SQUARE )
  505.       break;
  506.  
  507.     if ( g_iBoard[ row ][ iColIdx ] == color )
  508.     {
  509.       for ( iFlipCol = iColIdx + 1; iFlipCol != col; iFlipCol++ )
  510.       {
  511.         if ( flip ) { flipOneDisc( row, iFlipCol, color ); }
  512.         else { return TRUE; }
  513.       }
  514.       break;
  515.     }
  516.   }
  517.  
  518.   /* Look at row to the right of the square */
  519.   for ( iColIdx = col + 1; iColIdx < BOARD_SIZE; iColIdx++ )
  520.   {
  521.     if ( g_iBoard[ row ][ iColIdx ] == EMPTY_SQUARE )
  522.       break;
  523.  
  524.     if ( g_iBoard[ row ][ iColIdx ] == color )
  525.     {
  526.       for ( iFlipCol = iColIdx - 1; iFlipCol != col; iFlipCol-- )
  527.       {
  528.         if ( flip ) { flipOneDisc( row, iFlipCol, color ); }
  529.         else { return TRUE; }
  530.       }
  531.       break;
  532.     }
  533.   }
  534.  
  535.   /* Look at vertical column above the square */
  536.   for ( iRowIdx = row - 1; iRowIdx >= 0; iRowIdx-- )
  537.   {
  538.     if ( g_iBoard[ iRowIdx ][ col ] == EMPTY_SQUARE )
  539.       break;
  540.  
  541.     if ( g_iBoard[ iRowIdx ][ col ] == color )
  542.     {
  543.       for ( iFlipRow = iRowIdx + 1; iFlipRow != row; iFlipRow++ )
  544.       {
  545.         if ( flip ) { flipOneDisc( iFlipRow, col, color ); }
  546.         else { return TRUE; }
  547.       }
  548.       break;
  549.     }
  550.   }
  551.  
  552.   /* Look at vertical column below the square */
  553.   for ( iRowIdx = row + 1; iRowIdx < BOARD_SIZE; iRowIdx++ )
  554.   {
  555.     if ( g_iBoard[ iRowIdx ][ col ] == EMPTY_SQUARE )
  556.       break;
  557.  
  558.     if ( g_iBoard[ iRowIdx ][ col ] == color )
  559.     {
  560.       for ( iFlipRow = iRowIdx - 1; iFlipRow != row; iFlipRow-- )
  561.       {
  562.         if ( flip ) { flipOneDisc( iFlipRow, col, color ); }
  563.         else { return TRUE; }
  564.       }
  565.       break;
  566.     }
  567.   }
  568.  
  569.   /* Look at diagonal to top left */
  570.   for ( iRowIdx = row - 1, iColIdx = col - 1; (iRowIdx >= 0) && (iColIdx >= 0); iRowIdx--, iColIdx-- )
  571.   {
  572.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == EMPTY_SQUARE )
  573.       break;
  574.  
  575.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == color )
  576.     {
  577.       iFlipRow = iRowIdx + 1;
  578.       iFlipCol = iColIdx + 1;
  579.       while ( ( iFlipRow != row ) && ( iFlipCol != col ) )
  580.       {
  581.         if ( flip ) { flipOneDisc( iFlipRow, iFlipCol, color ); }
  582.         else { return TRUE; }
  583.         iFlipRow++;
  584.         iFlipCol++;
  585.       }
  586.       break;
  587.     }
  588.   }
  589.  
  590.   /* Look at diagonal to top right */
  591.   for ( iRowIdx = row - 1, iColIdx = col + 1; (iRowIdx >= 0) && (iColIdx < BOARD_SIZE); iRowIdx--, iColIdx++ )
  592.   {
  593.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == EMPTY_SQUARE )
  594.       break;
  595.  
  596.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == color )
  597.     {
  598.       iFlipRow = iRowIdx + 1;
  599.       iFlipCol = iColIdx - 1;
  600.       while ( ( iFlipRow != row ) && ( iFlipCol != col ) )
  601.       {
  602.         if ( flip ) { flipOneDisc( iFlipRow, iFlipCol, color ); }
  603.         else { return TRUE; }
  604.         iFlipRow++;
  605.         iFlipCol--;
  606.       }
  607.       break;
  608.     }
  609.   }
  610.  
  611.   /* Look at diagonal to bottom left */
  612.   for ( iRowIdx=row+1, iColIdx=col-1; (iRowIdx < BOARD_SIZE) && (iColIdx >= 0); iRowIdx++, iColIdx-- )
  613.   {
  614.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == EMPTY_SQUARE )
  615.       break;
  616.  
  617.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == color )
  618.     {
  619.       iFlipRow = iRowIdx - 1;
  620.       iFlipCol = iColIdx + 1;
  621.       while ( ( iFlipRow != row ) && ( iFlipCol != col ) )
  622.       {
  623.         if ( flip ) { flipOneDisc( iFlipRow, iFlipCol, color ); }
  624.         else { return TRUE; }
  625.         iFlipRow--;
  626.         iFlipCol++;
  627.       }
  628.       break;
  629.     }
  630.   }
  631.  
  632.   /* Look at diagonal to bottom right */
  633.   for ( iRowIdx=row+1, iColIdx=col+1; (iRowIdx < BOARD_SIZE) && (iColIdx < BOARD_SIZE); iRowIdx++, iColIdx++ )
  634.   {
  635.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == EMPTY_SQUARE )
  636.       break;
  637.  
  638.     if ( g_iBoard[ iRowIdx ][ iColIdx ] == color )
  639.     {
  640.       iFlipRow = iRowIdx - 1;
  641.       iFlipCol = iColIdx - 1;
  642.       while ( ( iFlipRow != row ) && ( iFlipCol != col ) )
  643.       {
  644.         if ( flip ) { flipOneDisc( iFlipRow, iFlipCol, color ); }
  645.         else { return TRUE; }
  646.         iFlipRow--;
  647.         iFlipCol--;
  648.       }
  649.       break;
  650.     }
  651.   }
  652.  
  653.   /* Returns FALSE to indicate that no valid flank line has been found */
  654.   return FALSE;
  655. }
  656. /* end of flipDiscs */
  657.  
  658.  
  659.  
  660. /*
  661. * flipOneDisc : Flips one disc and update both data array and game board display
  662. *
  663. * row         : Index to row of data array
  664. * col         : Index to column of data array
  665. * color       : Color of disc to check
  666. *
  667. */
  668. void flipOneDisc( int row, int col, int color )
  669. {
  670.   g_iBoard[ row ][ col ] = color;
  671.   updateBoard( indexToRow( row ), indexToCol( col ), color );
  672. }
  673. /* end of flipOneDisc */
  674.  
  675.  
  676.  
  677. /*
  678. * calcScore : Returns current score for a color
  679. *
  680. * color     : Color to find score
  681. *
  682. */
  683. int calcScore( int color )
  684. {
  685.   int iRow, iCol, iScore;
  686.  
  687.   for ( iScore = 0, iRow = 0; iRow < BOARD_SIZE; iRow++ )
  688.     for ( iCol = 0; iCol < BOARD_SIZE; iCol++ )
  689.       if ( g_iBoard[ iRow ][ iCol ] == color )
  690.         iScore++;
  691.  
  692.   return iScore;
  693. }
  694. /* end of calcScore */
  695.  
  696.  
  697.  
  698. /*
  699. * showScore : Display score on game console
  700. *
  701. */
  702. void showScore( void )
  703. {
  704.   sprintf( g_sBuffer, "Score is:   Black-%d   White-%d", calcScore( BLACK_SQUARE ), calcScore( WHITE_SQUARE ) );
  705.   displayMessage( g_sBuffer );
  706. }
  707. /* end of showScore */
  708.  
  709.  
  710.  
  711. /*
  712. * myPrompt : Show descriptive prompt for player
  713. *
  714. */
  715. void myPrompt( void )
  716. {
  717.   if ( g_iMyColor == BLACK_SQUARE )
  718.     displayMessage( "Please place a BLACK disc on the board." );
  719.   else
  720.     displayMessage( "Please place a WHITE disc on the board." );
  721. }
  722. /* end of myPrompt */
  723.  
  724.  
  725.  
  726. /*
  727. * finishGame : Show end of game information
  728. *
  729. */
  730. void finishGame( void )
  731. {
  732.   /* Reset game mode */
  733.   g_iMyGame = GAME_MODE_OFF;
  734.   g_iOpponentGame = GAME_MODE_OFF;
  735.  
  736.   /* Display descriptive message */
  737.   if ( calcScore( BLACK_SQUARE ) > calcScore( WHITE_SQUARE ) )
  738.   {
  739.     if ( g_iMyColor == BLACK_SQUARE )
  740.       displayMessage( "*** Congratulations! You win! ***" );
  741.     else
  742.       displayMessage( "Sorry, you've lost this game." );
  743.   }
  744.   else
  745.   {
  746.     if ( calcScore( BLACK_SQUARE ) < calcScore( WHITE_SQUARE ) )
  747.     {
  748.       if ( g_iMyColor == BLACK_SQUARE )
  749.         displayMessage( "Sorry, you've lost this game." );
  750.       else
  751.         displayMessage( "*** Congratulations! You win! ***" );
  752.     }
  753.     else
  754.       displayMessage( "*** The game is a draw ***" );
  755.   }
  756. }
  757. /* end of finishGame */