Finished Tic Tac Toe in C - Feedback on my source?

Member
Posts: 131
Joined: 2004.10
Post: #16
I would also mention that, even though this is such a tiny program, one file, it could have been compartmentalized better. If you code a more difficult game, this stream of consciousness style of coding will soon cause you a great deal of pain. My suggestion would be to isolate areas of the program into logical modules/units/engines/whatever so that modification of the game at a later date becomes oh so much easier. Debugging and enhancements to the game become much more trivial to make. Less and less copy code will result which mean less and less errors. Bugs are easier to find as you will most likely know where the bug is happening (Ok not always but usually it'll give you a focus on where to look.)

If I was to refactor the game I would split it up into 3 or 4 modules. (ttt prefix for Tic-Tac-Toe)

ttt - module for data manipulation and management.
tttui - module for user interface handling.
tttdisplay - module for displaying of ttt information.
tttai - module for the artificial intelligence.

In your case, with just the use of console IO, I'd probably combine tttui and tttdisplay since tttdisplay is so very trivial. However in a more complex game, the display of the board may be complicated enough to warrent it's own module, like say, drawing to OpenGL which could possibly be quite unique from the cocoa/carbon ui code.

These modules would look something like this in my world... (Just the headers not the actual code but the idea will hopefully shine through. There would be a matching .c file for each header which you can probably guess what's on the inside of those functions. Also note, I'm assuming a C program not C++ or Obj-C)
Code:
/* ttt header */

/* In case this header file is included more than once via via...
** This will ensure that the header is only processed once.  It
** avoids compiler warnings and errors.*/
#if !defined(TTT_HEADER)
#define TTT_HEADER

/* In case this module is ever included in a C++ project.*/
/* C++ code.********************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/* C++ code.********************************************************/

/* Constants */
typedef enum {
   tttPositionTOP_LEFT,
   tttPositionTOP_CENTER,
   tttPositionTOP_RIGHT,
   tttPositionMID_LEFT,
   tttPositionMID_CENTER,
   tttPositionMID_RIGHT,
   tttPositionBOT_LEFT,
   tttPositionBOT_CENTER,
   tttPositionBOT_RIGHT,

   tttPositionCOUNT
} TttPosition;

typedef enum {
   tttValueNONE,
   tttValueO_PLAYER,
   tttValueX_PLAYER
} TttValue

/* Types */
/* I put the types in the header just so debugging is simple.
** no code outside the c module associated with this header
** will ever touch the guts of the types.  Everything done
** via the API to the type.  The reason for that is that it
** becomes easier to find out where somethign is changing.
** if internal data is changing, it should only ever be in
** an API call so only 1 break point is ever needed.*/
typedef struct {
   char board[tttPositionCOUNT];
} Ttt;

/* (api) Functions */
/* Create a new board. */
Ttt     *tttCreate(     void);

/* Clean up. */
void     tttDestroy(    Ttt *ttt);

/* Get the value at a cell.*/
TttValue tttGet(        Ttt *ttt, TttPosition pos);

/* Test if there is a winner.*/
TttValue tttIsWon(      Ttt *ttt);
/* Test if there is a stalemate/cats */
int      tttIsStaleMate(Ttt *ttt);
/* Something I like to do with modules for proper startup.*/
int      tttIsStarted(  Ttt *ttt);

/* Set the value at a cell.*/
void     tttSet(        Ttt *ttt, TttPosition pos, TttValue value);
/* Start the ttt module.  Set up any internal states if needed.*/
int      tttStart(      void);
/* Stop the ttt module.  Clean up. */
void     tttStop(       void);

/* Matching the code near the top. */
/* C++ code.********************************************************/
#ifdef __cplusplus
}
#endif
/* C++ code.********************************************************/

/* Matching the #if !defined(TTT_HEADER) above.*/
#endif

Code:
/* tttui header */
#if !defined(TTTUI_HEADER)
#define TTTUI_HEADER

/* includes */
/* Needed for the Ttt typedef.*/
#include "ttt.h"

/* C++ code.********************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/* C++ code.********************************************************/

/* Types */
typedef struct {
   char name[50];
} Tttui;

/* Functions */
/* I could put down, tttuiCreate and tttuiDestroy but since there will only
** be one ui element ever then might as well just make it a static inside
** the .c module and manipulated via the other functions here.*/

/* Module started state */
int         tttuiIsStarted(      void);

/* Print the board. */
void        tttuiProcessBoard(   Ttt *ttt);
/* Get the player's move */
TttPosition tttuiProcessMove(    void);
/* Get the player name */
int         tttuiProcessName(    void);
/* Print the title/welcome message */
void        tttuiProcessWelcome( void);

/* Maybe other functions depending on what I missed. */

/* Start up the internal routines.  I.E. initialize a Tttui variable.*/
int         tttuiStart(          void);
/* Clean up the module stuff.*/
void        tttuiStop(           void);

/* C++ code.********************************************************/
#ifdef __cplusplus
}
#endif
/* C++ code.********************************************************/

#endif

Code:
/* tttai header */
#if !defined(TTTAI_HEADER)
#define TTTAI_HEADER

/* includes */
#include "ttt.h"

/* C++ code.********************************************************/
#ifdef __cplusplus
extern "C" {
#endif
/* C++ code.********************************************************/

/* Functions */
/* You get the picture */
int  tttaiIsStarted( void);

/* The computer makes a move. */
int  tttaiProcess(   Ttt *ttt);

int  tttaiStart(     void);
void tttaiStop(      void);

/* C++ code.********************************************************/
#ifdef __cplusplus
}
#endif
/* C++ code.********************************************************/

#endif

Now a C++ and Obj-C solution may use some niceties of their language but in general the idea is the same. Break the program down into logical components. For larger programs it will be a must or else you will find yourself freaking out with it's complexity and unmaintainability.
Quote this message in a reply
Member
Posts: 245
Joined: 2005.11
Post: #17
Achithyn Wrote:I'm not sure, though would the following code actually work? Is 'board' a pointer? I just thought that you couldn't assign char's like that, less it was when the char was defined and initialized. So, something like userName = "Foo"; wouldn't work, right?
Code:
void setupBoard()
{
    board = { '1', '2', '3',
              '4', '5', '6',
              '7', '8', '9'
            };
}
You're absolutely right here. board could be declared like that when it was initialised, but then couldn't be changed later, and would therefore be useless for your purposes. Also although board is a pointer, it was declared as an array, and so cannot be changed to point at another part of memory later (which is what that function would do if it was legal). The function you would need instead is:
Code:
void setupBoard(void)
    {
    memcpy(board, "123456789", 9);
    }
Using memcpy() rather than strcpy() means you don't need extra space in board[] to store the '\0' that would be on the end of the string in the function call. Ofcourse, calling a function with only one line of code in it is a bit of a waste of time. Wink
Quote this message in a reply
Member
Posts: 21
Joined: 2009.05
Post: #18
Greetings everyone,
You've all blown me away with your help. Smile I'm not sure where to comment and where not to... though I can say that I've learned a lot from the examples you've all provided. I'm almost tempted to reprogram my version of Tic Tac Toe, to include the things that I've learned, though I'm kind of sick of TTT now, looking to work on something new.

I'll be working on a new program that records the users thoughts concerning anime and stores them into a file. The only problem is the book I'm reading: Learn C on the Mac by Dave Mark, 10th chapter on files, source code, does not work as it should. I'm sure I can find the correct way in one of my other books (library seems to be my new friend...). Rasp

Thanks for the detailed source examples you guys cooked up for me. Some of it seems so practical, while other parts seems confusing. Right now I'm having problems understanding structs, with pointers, in... functions... etc. As I've heard, pointers can often be the most confusing. Well, I'm slowly working my way through them. Smile

Anyway, I've bookmarked this thread so I can come back and check out the code and comments you've all left. there's a lot of good content here, and will take several times of reading to understand it clearly.

Well, thanks again! I need to be off to get some reading a programming done today. Smile So, you all take care.

~Achithyn
Quote this message in a reply
Post Reply