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

Coursework Project – Battle Ship Game



During the academic year 2004/5, students were assigned the task of developing a Battle Ship 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 ship.h and ship.c at the start of the project to have graphics and networking functionalities. The project required implementation of the game logic. The game.c file was provided to students at the end of the project as a sample solution.

ship.h

  1. /*
  2. * ship.h : Project Assignment header file for Battleship game.
  3. *
  4. * University of Portsmouth, UK
  5. * Introduction to Algorithms and Programming
  6. * Lecturer: C Nguyen
  7. *
  8. * Updated 12 May 2005
  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        "Battleship v1.0e"
  26.  
  27. /*
  28. * Descriptive string to indicate the action buttons
  29. */
  30. #define APPLICATION_ACTION_STR      "SHIP CONTROLS"
  31.  
  32. /*
  33. * Descriptive string to indicate Player side
  34. */
  35. #define APPLICATION_FLEET_STR       "MY FLEET"
  36.  
  37. /*
  38. * Descriptive string to indicate the Opponent side
  39. */
  40. #define APPLICATION_RADAR_STR       "SHIP RADAR"
  41.  
  42. /*
  43. * Descriptive string to indicate the message display
  44. */
  45. #define APPLICATION_MSGBOX_STR      "SHIP MESSAGES"
  46.  
  47. /*
  48. * Descriptive string for button to start a game
  49. */
  50. #define APPLICATION_BUTTON_PLAY_STR "Play"
  51.  
  52. /*
  53. * Descriptive string for button to display information about current game
  54. */
  55. #define APPLICATION_BUTTON_INFO_STR "Info"
  56.  
  57. /*
  58. * Descriptive string for button to end a game and exit application
  59. */
  60. #define APPLICATION_BUTTON_EXIT_STR "Exit"
  61.  
  62. /*
  63. * Descriptive string for title of dialog window
  64. */
  65. #define APPLICATION_DIALOG_STR      "Continue?"
  66.  
  67. /*
  68. * Descriptive string for dialog button for user response Yes
  69. */
  70. #define APPLICATION_DIALOG_YES_STR  "Yes"
  71.  
  72. /*
  73. * Descriptive string for dialog button for user response No
  74. */
  75. #define APPLICATION_DIALOG_NO_STR   "No"
  76.  
  77. /*
  78. * Descriptive string for dialog asking whether player wants to exit
  79. */
  80. #define APPLICATION_DIALOG_QUESTION "Are you sure you want to exit now?"
  81.  
  82. /*
  83. * Descriptive string for title of dialog window when asking for opponent host info
  84. */
  85. #define APPLICATION_DIALOG_HOST_STR "Opponent Host"
  86.  
  87. /*
  88. * Descriptive string for dialog button to connect to opponent host
  89. */
  90. #define APPLICATION_DIALOG_HOST_YES "Play"
  91.  
  92. /*
  93. * Descriptive string for dialog button for cancel play request
  94. */
  95. #define APPLICATION_DIALOG_HOST_NO  "Cancel"
  96.  
  97. /*
  98. * Max length permitted in text entry field
  99. */
  100. #define TEXT_ENTRY_MAX_LENGTH       128
  101.  
  102. /*
  103. * Default width of application window
  104. */
  105. #define APPLICATION_WINDOW_WIDTH    900
  106.  
  107. /*
  108. * Default height of application window
  109. */
  110. #define APPLICATION_WINDOW_HEIGHT   500
  111.  
  112. /*
  113. * Default width of dialog window
  114. */
  115. #define APPLICATION_DIALOG_WIDTH    250
  116.  
  117. /*
  118. * Default height of dialog window
  119. */
  120. #define APPLICATION_DIALOG_HEIGHT   80
  121.  
  122. /*
  123. * Size of fleet and radar grids
  124. */
  125. #define GRID_SIZE                   10
  126.  
  127. /*
  128. * Number of ships (cells) allowed for each game
  129. */
  130. #define FLEET_SIZE                  10
  131.  
  132. /*
  133. * Default value if network socket is invalid or not connected
  134. */
  135. #define NETWORK_SOCKET_CLOSED       -1
  136.  
  137. /*
  138. * Color of main window background
  139. */
  140. #define COLOR_WINDOW_BG             "steelblue"
  141.  
  142. /*
  143. * Color of normal grid backgrounds
  144. */
  145. #define COLOR_GRID_BG               "aquamarine"
  146.  
  147. /*
  148. * Color of grid backgrounds when mouse is hovering above
  149. */
  150. #define COLOR_GRID_HOVER_BG         "yellow"
  151.  
  152. /*
  153. * Color of grid backgrounds when mouse button is pressed
  154. */
  155. #define COLOR_GRID_ACTIVE_BG        "gray70"
  156.  
  157. /*
  158. * Color of message display background
  159. */
  160. #define COLOR_MSGBOX_BG             "gray"
  161.  
  162. /*
  163. * Name of XPM image for use with Play button
  164. * Requires image file to be in same directory as application
  165. */
  166. #define XPM_BUTTON_PLAY             "g_play.xpm"
  167.  
  168. /*
  169. * Name of XPM image for use with Info button
  170. * Requires image file to be in same directory as application
  171. */
  172. #define XPM_BUTTON_INFO             "g_info.xpm"
  173.  
  174. /*
  175. * Name of XPM image for use with Exit button
  176. * Requires image file to be in same directory as application
  177. */
  178. #define XPM_BUTTON_EXIT             "g_exit.xpm"
  179.  
  180. /*
  181. * Name of XPM image to indicate Fleet location with Ship that is not Hit
  182. * Requires image file to be in same directory as application
  183. */
  184. #define XPM_GRID_SHIP               "g_ship.xpm"
  185.  
  186. /*
  187. * Name of XPM image to indicate Fleet location with Ship that has been Hit
  188. * Requires image file to be in same directory as application
  189. */
  190. #define XPM_GRID_SHIP_HIT           "g_ship_hit.xpm"
  191.  
  192. /*
  193. * Name of XPM image to indicate Radar location with opponent Ship that has been Hit
  194. * Requires image file to be in same directory as application
  195. */
  196. #define XPM_GRID_FIRE_HIT           "g_fire_hit.xpm"
  197.  
  198. /*
  199. * Name of XPM image to indicate Fleet or Radar location with a Missed shot
  200. * Requires image file to be in same directory as application
  201. */
  202. #define XPM_GRID_MISS               "g_miss.xpm"
  203.  
  204. /*
  205. * Name of XPM image to indicate blank/empty Fleet or Radar location
  206. * Requires image file to be in same directory as application
  207. */
  208. #define XPM_GRID_BLANK              "g_blank.xpm"
  209.  
  210. /*
  211. * Name of XPM image to indicate Yes response on dialog window
  212. * Requires image file to be in same directory as application
  213. */
  214. #define XPM_BUTTON_DIALOG_YES       "g_yes.xpm"
  215.  
  216. /*
  217. * Name of XPM image to indicate No response on dialog window
  218. * Requires image file to be in same directory as application
  219. */
  220. #define XPM_BUTTON_DIALOG_NO        "g_no.xpm"
  221.  
  222. /*
  223. * Different types of game events
  224. *
  225. * unknownType        = Uninitialised type value
  226. * requestPlay        = Player pressed on Play button
  227. * requestInfo        = Player pressed on Info button
  228. * requestFleet       = Player pressed on a cell in the Fleet grid
  229. * requestRadar       = Player pressed on a cell in the Radar grid
  230. * messagePlayInit    = Opponent sent message to start a new game
  231. * messagePlayReady   = Opponent sent message to start a new game
  232. * messageAttack      = Opponent sent message to attack player's Fleet
  233. * messageResponse    = Opponent sent response to your attack
  234. * messageVictory     = Opponent sent message to indicate that player has won!
  235. * messageAbort       = Opponent disconnected during game
  236. *
  237. */
  238. enum _gameEventType
  239. {
  240.   unknownType,
  241.   requestPlay,
  242.   requestInfo,
  243.   requestFleet,
  244.   requestRadar,
  245.   messagePlayInit,
  246.   messagePlayReady,
  247.   messageAttack,
  248.   messageResponse,
  249.   messageVictory,
  250.   messageAbort
  251. };
  252. typedef enum _gameEventType GameEventType;
  253.  
  254. /*
  255. * Allowed status values for a grid cell location
  256. *
  257. * normal      = Blank cell
  258. * ship_ok     = Ship at location and in OK status
  259. * ship_hit    = Ship at location and hit by attack
  260. * attack_hit  = Attack hit opponent ship at this location
  261. * miss        = Missed attack at this location
  262. *
  263. */
  264. enum _gridLocationStatus
  265. {
  266.   normal,
  267.   ship_ok,
  268.   ship_hit,
  269.   attack_hit,
  270.   miss
  271. };
  272. typedef enum _gridLocationStatus GridLocationStatus;
  273.  
  274. /*
  275. * Common structure for game events
  276. *
  277. * eventID     = Unique ID number to identify events and help with debugging
  278. * eventType   = Indicates type of event
  279. * eventCol    = For requestFleet, requestRadar, messageAttack event types, this
  280. *               is the Column identifier of the active grid location
  281. * eventRow    = For requestFleet, requestRadar, messageAttack event types, this
  282. *               is the Row identifier of the active grid location
  283. * eventStatus = For messageResponse event type, this indicates the status of the attack
  284. * eventData   = For requestPlay, messagePlayInit and messagePlayReady event types,
  285. *               this string indicates the IP address and port number of the opponent
  286. *
  287. */
  288. struct _gameEvent
  289. {
  290.   long                eventID;
  291.   GameEventType       eventType;
  292.   int                 eventRow;
  293.   char                eventCol;
  294.   GridLocationStatus  eventStatus;
  295.   char               *eventData;
  296. };
  297. typedef struct _gameEvent GameEvent;
  298.  
  299. /*
  300. * gameEventsDispatcher : Main function to handle game events
  301. *
  302. * Parameters           : Game event structure
  303. *
  304. */
  305. void gameEventsDispatcher( GameEvent* );
  306.  
  307. /*
  308. * updateFleet : Show status of Fleet grid at specific location
  309. *
  310. * Parameters  : Row label
  311. *               Column label
  312. *               Location status
  313. *
  314. */
  315. void updateFleet( int, char, GridLocationStatus );
  316.  
  317. /*
  318. * updateRadar : Show status of Radar grid at specific location
  319. *
  320. * Parameters  : Row label
  321. *               Column label
  322. *               Location status
  323. *
  324. */
  325. void updateRadar( int, char, GridLocationStatus );
  326.  
  327. /*
  328. * resetGrids : Reset all grid locations to normal display
  329. *
  330. */
  331. void resetGrids();
  332.  
  333. /*
  334. * displayMessage : Appends message to ship message console
  335. *
  336. * Parameters     : Text message to display
  337. *
  338. */
  339. void displayMessage( char* );
  340.  
  341. /*
  342. * notifyPlayInit : Send message to opponent initiating a game
  343. *
  344. * Parameters     : String of opponent host IP address
  345. *
  346. */
  347. int notifyPlayInit( char* );
  348.  
  349. /*
  350. * notifyPlayReady : Send message to opponent that you are ready start attacks
  351. *
  352. * Parameters      : String of opponent host IP address
  353. *
  354. */
  355. int notifyPlayReady( char* );
  356.  
  357. /*
  358. * attack      : Fire a shot at specific location on opponent's Fleet
  359. *
  360. * Parameters  : Row label
  361. *               Column label
  362. */
  363. void attack( int, char );
  364.  
  365. /*
  366. * respondToAttack : Send response to opponent's attack on specific location
  367. *
  368. * Parameters      : Row label
  369. *                   Column label
  370. *                   Location status
  371. *
  372. */
  373. void respondToAttack( int, char, GridLocationStatus );
  374.  
  375. /*
  376. * getPortNumber : Returns port number of current game server
  377. *
  378. * Returns port number if server is connected, else returns NETWORK_SOCKET_CLOSED value
  379. *
  380. */
  381. int getPortNumber( void );
  382.  
  383. /*
  384. * getIPAddress : Returns IP address of current computer
  385. *
  386. * Returns string representing the computer IP address
  387. *
  388. */
  389. char* getIPAddress( void );
  390.  
  391. /*
  392. * endGame    : Send message to opponent indicating that your fleet has been sunk
  393. *
  394. * Parameters : TRUE if application should also exit after defeat message has been sent
  395. *
  396. */
  397. void endGame( int );
  398.  
  399. /*
  400. * describeEvent : Display text description of event data
  401. *
  402. * Parameters    : Game event structure
  403. *                 TRUE if description should appear in window message console
  404. *                 TRUE if description should be printed to standard output
  405. *
  406. */
  407. void describeEvent( GameEvent*, int, int );
  408.  
  409. /*
  410. * OPTIONAL GTK MACROS THAT MAY BE USEFUL
  411. *
  412. *   g_return_if_fail( condition )
  413. *   g_return_val_if_fail( condition, retval )
  414. *
  415. *   g_assert( condition )
  416. *   g_assert_not_reached( void )
  417. *
  418. *   MAX( a,  b )
  419. *   MIN( a, b )
  420. *   ABS( x )
  421. *   CLAMP( x, low, high )
  422. */



ship.c

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



game.c

  1. /*
  2. * game.c : Student Project Assignment functions for Battleship game.
  3. *
  4. * University of Portsmouth, UK
  5. * Introduction to Algorithms and Programming
  6. * Lecturer: C Nguyen
  7. *
  8. * Student Name   :  SAMPLE SOLUTION SOURCE CODE
  9. * Student Number :  SAMPLE SOLUTION SOURCE CODE
  10. *
  11. * Updated 5 Apr 2005
  12. *
  13. */
  14.  
  15. #include "ship.h"
  16.  
  17. /*
  18. * Set the macro name to DEBUG_ON to enable description of event data structures
  19. */
  20. #define DEBUG_OFF                   1
  21.  
  22. /*
  23. * Reduce row label by 1 since grid row starts at 1, but array index starts at zero
  24. */
  25. #define ROW_INDEX(x)                ( (x) - 1 )
  26.  
  27. /*
  28. * Convert column character label to numeric index for use with data arrays
  29. */
  30. #define COL_INDEX(x)                ( ((int)(x)) - 65 )
  31.  
  32. /*
  33. * Check if player and opponent have both finished placing ships so everyone may start playing
  34. */
  35. #define CHECK_SETUP(p,o)            if ( ((p)==mode_ready) && ((o)==mode_ready) ) { startPlaying(); }
  36.  
  37. /*
  38. * Reset to new game turn if player and opponent has each made one attack
  39. */
  40. #define UPDATE_TURN(p,o)            if ( ((p)==turn_end) && ((o)==turn_end) ) { (p)=turn_open; (o)=turn_open; }
  41.  
  42. /*
  43. * Game *usually* operates in these sequential modes
  44. */
  45. enum _gameMode
  46. {
  47.   mode_no_game,
  48.   mode_initialising,
  49.   mode_ready,
  50.   mode_playing,
  51. };
  52. typedef enum _gameMode GameMode;
  53.  
  54. /*
  55. * One attack is allowed per turn
  56. */
  57. enum _turnMode
  58. {
  59.   turn_end,
  60.   turn_open
  61. };
  62. typedef enum _turnMode TurnMode;
  63.  
  64. /*
  65. * Game mode for player
  66. */
  67. GameMode g_iMyGame = mode_no_game;
  68.  
  69. /*
  70. * Game mode for opponent
  71. * This may be different from game mode of player, since game play is asynchronous
  72. */
  73. GameMode g_iOpponentGame = mode_no_game;
  74.  
  75. /*
  76. * Turn mode for player
  77. * This is reset for each new turn
  78. */
  79. TurnMode g_iMyTurn = turn_open;
  80.  
  81. /*
  82. * Turn mode for opponent
  83. * This may be different from turn mode of player, since game play is asynchronous
  84. */
  85. TurnMode g_iOpponentTurn = turn_open;
  86.  
  87. /*
  88. * Two dimensional array to track status of player's ships
  89. */
  90. GridLocationStatus g_iFleet[ GRID_SIZE ][ GRID_SIZE ];
  91.  
  92. /*
  93. * Two dimensional array to track status of opponent's ships
  94. */
  95. GridLocationStatus g_iRadar[ GRID_SIZE ][ GRID_SIZE ];
  96.  
  97. /*
  98. * Shared buffer for printing messages to the game console
  99. */
  100. char g_sBuffer[512];
  101.  
  102. /*
  103. * Forward function declarations
  104. * Please see function definitions for description of function usage
  105. */
  106. void resetGame( GameEvent* );
  107. void startInitialising( GameEvent* );
  108. void placeShip( GameEvent* );
  109. void startPlaying( void );
  110. void attackShip( GameEvent* );
  111. void incomingFire( GameEvent* );
  112. void attackReport( GameEvent* );
  113. void checkVictoryStatus( GameEvent* );
  114. int countMyShips( void );
  115. int countOpponentShips( void );
  116.  
  117.  
  118.  
  119. /*
  120. * gameEventsDispatcher : Main function to handle game events
  121. *
  122. * event                : Pointer to game event structure
  123. *
  124. */
  125. void gameEventsDispatcher( GameEvent *event )
  126. {
  127.   /*
  128.    * This is the entry to this module, so checking for NULL here prevents the need to check elsewhere
  129.    */
  130.   if( event == NULL ) { return; }
  131.  
  132. #ifdef DEBUG_ON
  133.   /*
  134.    * Show descriptive information of all events
  135.    */
  136.   describeEvent( event, FALSE, TRUE );
  137. #endif
  138.  
  139.   /*
  140.    * These events always cause the same activities regardless of current game mode
  141.    */
  142.   switch ( event->eventType )
  143.     {
  144.     /*
  145.      * Show game information to user and immediately return
  146.      */
  147.     case requestInfo :
  148.       sprintf( g_sBuffer, "Your game is on host: %s:%d", getIPAddress(), getPortNumber() );
  149.       displayMessage( g_sBuffer );
  150.       return;
  151.  
  152.     /*
  153.      * By choosing to process this event here, we intentionally allow players
  154.      * to immediately start a new game and abandon any current game
  155.      */
  156.     case requestPlay :
  157.       if ( notifyPlayInit( event->eventData ) ) { resetGame( event ); }
  158.       return;
  159.  
  160.     /*
  161.      * Reset game if an abort message is received
  162.      */
  163.     case messageAbort :
  164.       resetGame( event );
  165.       return;
  166.     }
  167.  
  168.   /*
  169.    * Normal game operation is primarily driven by the player game mode (not the opponent game mode)
  170.    * For each game mode, only certain events are processed
  171.    * Otherwise, all other events are simply ignored
  172.    */
  173.   switch ( g_iMyGame )
  174.     {
  175.     /*
  176.      * This is the default mode which indicates no game is in progress
  177.      */
  178.     case mode_no_game :
  179.       if ( event->eventType == messagePlayInit )
  180.       {
  181.         if ( notifyPlayInit( event->eventData ) ) { startInitialising( event ); }
  182.       }
  183.       else
  184.       {
  185.         displayMessage( "Please click Play button to start a new game, or Exit button to end the program" );
  186.       }
  187.       break;
  188.  
  189.     /*
  190.      * This mode indicates that the player is placing their ships on the fleet grid
  191.      */
  192.     case mode_initialising :
  193.       switch ( event->eventType )
  194.         {
  195.         case requestFleet :
  196.           placeShip( event );
  197.           break;
  198.         case messagePlayReady :
  199.           g_iOpponentGame = mode_ready;
  200.           break;
  201.         case messageVictory :
  202.           checkVictoryStatus( event );
  203.           return;
  204.         }
  205.       CHECK_SETUP( g_iMyGame, g_iOpponentGame ) ;
  206.       break;
  207.  
  208.     /*
  209.      * This mode indicates that the player is done placing their ships and now waiting
  210.      * for the opponent to indicate that their setup is done as well
  211.      */
  212.     case mode_ready :
  213.       if ( event->eventType == messageVictory ) { checkVictoryStatus( event ); return; }
  214.       if ( event->eventType == messagePlayReady ) { g_iOpponentGame = mode_ready; }
  215.       CHECK_SETUP( g_iMyGame, g_iOpponentGame ) ;
  216.       break;
  217.  
  218.     /*
  219.      * For normal game operation, most of the time should be spent in this mode
  220.      */
  221.     case mode_playing :
  222.       switch ( event->eventType )
  223.         {
  224.         case requestRadar :
  225.           attackShip( event );
  226.           break;
  227.         case messageAttack :
  228.           incomingFire( event );
  229.           break;
  230.         case messageResponse :
  231.           attackReport( event );
  232.           break;
  233.         }
  234.  
  235.       UPDATE_TURN( g_iMyTurn, g_iOpponentTurn );
  236.       checkVictoryStatus( event );
  237.       break;
  238.     }
  239. }
  240. /* end of gameEventsDispatcher */
  241.  
  242.  
  243.  
  244. /*
  245. * resetGame : Attempt to initiate a new game with new opponent
  246. *             Any current game is abandoned without further notice to player
  247. *
  248. * event     : Current game event
  249. *
  250. */
  251. void resetGame( GameEvent* event )
  252. {
  253.   /*
  254.    * Only need to reset game mode, since turn mode and ship arrays are reset during initialisation
  255.    */
  256.   g_iMyGame = mode_no_game;
  257.   g_iOpponentGame = mode_no_game;
  258.  
  259.   /*
  260.    * No need to continue processing if event data is not provided
  261.    */
  262.   if ( event == NULL ) { return; }
  263.  
  264.   switch ( event->eventType )
  265.     {
  266.     /*
  267.      * Normal player request by clicking on play button
  268.      */
  269.     case requestPlay :
  270.       /*
  271.        * Out of courtesy, notify the opponent if a current game is abandoned
  272.        */
  273.       if ( g_iMyGame == mode_playing ) { endGame( FALSE ); }
  274.       sprintf( g_sBuffer, "Invitation to play has been sent to %s", event->eventData );
  275.       displayMessage( g_sBuffer );
  276.       break;
  277.  
  278.     /*
  279.      * Unexpected system abort message, so notify player
  280.      */
  281.     case messageAbort :
  282.       sprintf( g_sBuffer, "Encountered unexpected error, please click on Play button to start a new game" );
  283.       displayMessage( g_sBuffer );
  284.       break;
  285.     }
  286. }
  287. /* end of resetGame */
  288.  
  289.  
  290.  
  291. /*
  292. * startInitialising : Start steps to actually play a new game
  293. *
  294. * event             : Current game event
  295. *
  296. */
  297. void startInitialising( GameEvent* event )
  298. {
  299.   int iCol, iRow;
  300.  
  301.   g_iMyGame = mode_initialising;
  302.   g_iOpponentGame = mode_initialising;
  303.   sprintf( g_sBuffer, "Invitation has been accepted to play with %s", event->eventData );
  304.   displayMessage( g_sBuffer );
  305.  
  306.   /*
  307.    * Reset data variables related to the grids
  308.    */
  309.   resetGrids();
  310.   for ( iRow = 0; iRow < GRID_SIZE; iRow++ )
  311.   {
  312.     for ( iCol = 0; iCol < GRID_SIZE; iCol++ )
  313.     {
  314.       g_iFleet[ iRow ][ iCol ] = normal;
  315.       g_iRadar[ iRow ][ iCol ] = normal;
  316.     }
  317.   }
  318.  
  319.   sprintf( g_sBuffer, "Please start by placing %d ships on the Fleet grid", FLEET_SIZE );
  320.   displayMessage( g_sBuffer );
  321. }
  322. /* end of startInitialising */
  323.  
  324.  
  325.  
  326. /*
  327. * placeShip : Put new ship on fleet grid for player
  328. *
  329. * event     : Current game event
  330. *
  331. */
  332. void placeShip( GameEvent* event )
  333. {
  334.   /*
  335.    * Ignore if ship has already been placed in this cell
  336.    */
  337.   if ( g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] == ship_ok ) { return; }
  338.  
  339.   /*
  340.    * If there are still ships remaining to be placed, then do it
  341.    */
  342.   if ( countMyShips() < FLEET_SIZE )
  343.   {
  344.     g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] = ship_ok;
  345.     updateFleet( event->eventRow, event->eventCol, ship_ok );
  346.   }
  347.  
  348.   if ( ( countMyShips() >= FLEET_SIZE ) && notifyPlayReady( NULL ) )
  349.   {
  350.     /*
  351.      * All player ships have been placed
  352.      */
  353.     g_iMyGame = mode_ready;
  354.     displayMessage( "Your ships are ready ... please wait for the opponent to place their ships" );
  355.   }
  356.   else
  357.   {
  358.     /*
  359.      * Tell the player how many ships they have remaining to place on the fleet grid
  360.      */
  361.     sprintf( g_sBuffer, "You have %d ships remaining to place on the Fleet grid", FLEET_SIZE - countMyShips() );
  362.     displayMessage( g_sBuffer );
  363.   }
  364. }
  365. /* end of placeShip */
  366.  
  367.  
  368.  
  369. /*
  370. * startPlaying : All setup is done and normal game play may start
  371. *
  372. */
  373. void startPlaying( void )
  374. {
  375.   /*
  376.    * Update game and turn modes
  377.    * Show some basic instructions to player to get the game play started
  378.    */
  379.   g_iMyGame       = mode_playing;
  380.   g_iOpponentGame = mode_playing;
  381.   g_iMyTurn       = turn_open;
  382.   g_iOpponentTurn = turn_open;
  383.   displayMessage( "The opponent is now ready. Let the battle start!" );
  384.   displayMessage( "Please click a location on the Radar to fire at the opponent's ships" );
  385. }
  386. /* end of startPlaying */
  387.  
  388.  
  389.  
  390. /*
  391. * attackShip : Player wants to fire at a specific opponent location
  392. *
  393. * event      : Current game event
  394. *
  395. */
  396. void attackShip( GameEvent* event )
  397. {
  398.   /*
  399.    * Ignore if it's not the player's turn
  400.    */
  401.   if ( g_iMyTurn == turn_open )
  402.   {
  403.     /*
  404.      * Only attack if player has not already tried attacking this opponent location
  405.      */
  406.     if ( g_iRadar[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] == normal )
  407.     {
  408.       /*
  409.        * Note that the player turn does *not* end when they attack
  410.        * Player turn only ends when a response to the attack is received
  411.        * This is due to the asynchronous nature of the game operation
  412.        */
  413.       attack( event->eventRow, event->eventCol );
  414.       sprintf( g_sBuffer, "Attacking location: %d-%c", event->eventRow, event->eventCol );
  415.       displayMessage( g_sBuffer );
  416.       return;
  417.     }
  418.  
  419.     displayMessage( "You've already fired at this location, please attack a different location" );
  420.   }
  421. }
  422. /* end of attackShip */
  423.  
  424.  
  425.  
  426. /*
  427. * incomingFire : Opponent is attacking a specific location of the player's fleet
  428. *
  429. * event        : Current game event
  430. *
  431. */
  432. void incomingFire( GameEvent* event )
  433. {
  434.   /*
  435.    * Out of courtesy, re-send response if cell under attack has already been attacked and hit
  436.    */
  437.   if ( g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] == ship_hit )
  438.   {
  439.     respondToAttack( event->eventRow, event->eventCol, attack_hit );
  440.     return;
  441.   }
  442.  
  443.   /*
  444.    * Out of courtesy, re-send response if cell under attack has already been attacked and missed
  445.    */
  446.   if ( g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] == miss )
  447.   {
  448.     respondToAttack( event->eventRow, event->eventCol, miss );
  449.     return;
  450.   }
  451.  
  452.   /*
  453.    * Only process further if the opponent's turn is still open
  454.    */
  455.   if ( g_iOpponentTurn == turn_open )
  456.   {
  457.     /*
  458.      * Note that opponent turn immediately ends when valid attack is made
  459.      */
  460.     g_iOpponentTurn = turn_end;
  461.  
  462.     /*
  463.      * Respond to opponent if the attack was successful
  464.      */
  465.     if ( g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] == ship_ok )
  466.     {
  467.       g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] = ship_hit;
  468.       updateFleet( event->eventRow, event->eventCol, ship_hit );
  469.       respondToAttack( event->eventRow, event->eventCol, attack_hit );
  470.  
  471.       /*
  472.        * Tell player that they have been hit
  473.        */
  474.       sprintf( g_sBuffer, "Your ship has been hit at location: %d-%c", event->eventRow, event->eventCol );
  475.       displayMessage( g_sBuffer );
  476.       sprintf( g_sBuffer, "You have %d ships remaining and the opponent has %d ships remaining",
  477.                countMyShips(), countOpponentShips() );
  478.       displayMessage( g_sBuffer );
  479.  
  480.       return;
  481.     }
  482.  
  483.     /*
  484.      * Notify opponent that the attack was not successful
  485.      */
  486.     g_iFleet[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] = miss;
  487.     updateFleet( event->eventRow, event->eventCol, miss );
  488.     respondToAttack( event->eventRow, event->eventCol, miss );
  489.   }
  490. }
  491. /* end of incomingFire */
  492.  
  493.  
  494.  
  495. /*
  496. * attackReport : Outcome has been received about the status of an attack to the opponent
  497. *
  498. * event        : Current game event
  499. *
  500. */
  501. void attackReport( GameEvent* event )
  502. {
  503.   /*
  504.    * Ignore event if response was previously received
  505.    * This does not change the player's game turn
  506.    */
  507.   if ( g_iRadar[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] != normal ) { return; }
  508.  
  509.   /*
  510.    * Only process further if the player's turn is still open
  511.    */
  512.   if ( g_iMyTurn == turn_open )
  513.   {
  514.     /*
  515.      * End player's turn and update the screen
  516.      */
  517.     g_iMyTurn = turn_end;
  518.     g_iRadar[ ROW_INDEX( event->eventRow ) ][ COL_INDEX( event->eventCol ) ] = event->eventStatus;
  519.     updateRadar( event->eventRow, event->eventCol, event->eventStatus );
  520.  
  521.     /*
  522.      * Tell player if the opponent has been hit
  523.      */
  524.     if ( event->eventStatus == attack_hit )
  525.     {
  526.       sprintf( g_sBuffer, "You've hit the enemy ship at location: %d-%c", event->eventRow, event->eventCol );
  527.       displayMessage( g_sBuffer );
  528.       sprintf( g_sBuffer, "You have %d ships remaining and the opponent has %d ships remaining",
  529.                countMyShips(), countOpponentShips() );
  530.       displayMessage( g_sBuffer );
  531.     }
  532.   }
  533. }
  534. /* end of attackReport */
  535.  
  536.  
  537.  
  538. /*
  539. * checkVictoryStatus : Check if victory conditions have been met for player or opponent
  540. *
  541. * event              : Current game event
  542. *
  543. */
  544. void checkVictoryStatus( GameEvent* event )
  545. {
  546.   /*
  547.    * Check for victory conditions first
  548.    */
  549.   if ( ( countOpponentShips() < 1 ) || ( event->eventType == messageVictory ) )
  550.   {
  551.     displayMessage( "VICTORY! Congratulations Admiral, your opponent has been defeated!" );
  552.     resetGame( NULL );
  553.     return;
  554.   }
  555.  
  556.   /*
  557.    * If all ships have been sunk then game is lost
  558.    */
  559.   if ( countMyShips() < 1 )
  560.   {
  561.     displayMessage( "Sorry mate, you've lost the battle because all of your ships have been sunk" );
  562.     endGame( FALSE );
  563.     resetGame( NULL );
  564.   }
  565. }
  566. /* end of checkVictoryStatus */
  567.  
  568.  
  569.  
  570. /*
  571. * countMyShips : Counts the number of ships in player's fleet that have not been hit
  572. *
  573. * Returns number of healthy ships
  574. *
  575. */
  576. int countMyShips( void )
  577. {
  578.   int iRow, iCol, iActiveShips;
  579.  
  580.   /*
  581.    * Count for OK ships so that it can be used to count during initialisation and game play
  582.    */
  583.   iActiveShips = 0;
  584.   for ( iRow = 0; iRow < GRID_SIZE; iRow++ )
  585.   {
  586.     for ( iCol = 0; iCol < GRID_SIZE; iCol++ )
  587.     {
  588.       if ( g_iFleet[ iRow ][ iCol ] == ship_ok ) { iActiveShips++; }
  589.     }
  590.   }
  591.  
  592.   return iActiveShips;
  593. }
  594. /* end of countMyShips */
  595.  
  596.  
  597.  
  598. /*
  599. * countOpponentShips : Counts the number of ships in opponent's fleet that have not been hit
  600. *
  601. * Returns number of healthy ships
  602. *
  603. */
  604. int countOpponentShips( void )
  605. {
  606.   int iRow, iCol, iActiveShips;
  607.  
  608.   /*
  609.    * By counting down from the maximum fleet size, the number of healthy ships can be inferred
  610.    */
  611.   iActiveShips = FLEET_SIZE;
  612.   for ( iRow = 0; iRow < GRID_SIZE; iRow++ )
  613.   {
  614.     for ( iCol = 0; iCol < GRID_SIZE; iCol++ )
  615.     {
  616.       if ( g_iRadar[ iRow ][ iCol ] == attack_hit ) { iActiveShips--; }
  617.     }
  618.   }
  619.  
  620.   return iActiveShips;
  621. }
  622. /* end of countOpponentShips */