Hi I'm currently writing an Othello game using the Windows API.

I have 3 classes in my program, the first class is simply called "Othello" and it handles almost everything. I also have a class called Field that's used to control which fields are black which are white etc. My last class is the class player which is used to store the player's name and score etc.

My question is this, if I wanted to to add the ability to play games online would I rather:

1. Add a new class called Network or something similar which handles everything, send and receive packets, establishing a connection etc and make Othello derive from that class.

2. As above but instead make class Network a member of class Othello.

3. Just add all the network functions to the class Othello.

4. Something else.

I'm also wondering if the best way to write somewhat small applications (say 2 - 3k lines of code) like this game using a class that basically has the name of the game or application that handles everything.

I'm sorry if I've missed any good texts or books regarding this topic or if my questions are too obvious or general.

Thanks
Mattias

Recommended Answers

All 2 Replies

I think the best way to look at this problem is by dividing the tasks properly. I think your Othello class seems to be ill-defined, as you said it does "almost everything" that's a bad sign that you can't even clearly say what it does or what it is responsible for. In this type of simple game logic, things are usually separated as (or at least from my experience):
Data handling:
1) Global Housekeeping: That means keeping track of the environment and the list of objects in it (like the Othello board with the list of Fields)
2) Local Housekeeping: That means keeping record of the change of state of individual objects (like black or white for a Field)
3) Player Status: That means keeping track of scores, name, etc.
Functionality:
4) Game Logic: Handles all the rules by which objects interact (deciding what moves are legal, automatically reversing fields after a valid move, establishing the winning condition, etc.).
5) Player Controller: Handles how moves by a player is generated and provided to the game logic (is it a command-line prompt to the user? is it a GUI button? is it obtained from a network connection? is it generated by an artificial intelligence (bot)? etc.).
6) Display: Handles how the game is displayed (in 3D gaming world, that is called the Renderer) (is it a piece of ASCII art in a command-line? is it a windows form? is it OpenGL or DirectX? etc.). This could also include a synchronization through a network to client instances of the game.

So, basically, most games will have their source code more-or-less divided in that fashion (some might merge the player and its controller). For a simple game like Othello, this would essentially correspond to a class for each of these things. You have already structured your code with the first three points in mind, which is a good start (and it is classic, at first, to think of object-oriented as purely a separation of data handling, but it is much more than that).

You can see in the above the difference between what is referred to, by 3D gaming professionals, as the game engine (points 1, 2, and 6) and the game logic (points 3, 4, and 5). The reason for this divide is that point 1, 2, and 6 have nothing to do with what type of game you are making and can thus be developed very generically for multiple purposes (which is economical for game developing companies). Also, the game engine is where most of the heavy stuff is (like 3D models, textures, shaders, file management, memory management, 3D effects, physics, etc.) so it makes sense to reuse it. Typically, the game logic part is quite a bit easier and quicker to develop.

Anyhow, to bring it back to your topic. As for this being a small application, it doesn't make much of a difference as for the basic architecture of it: big or small a good architecture is a good architecture (and it is good for learning too!). In your case, I would suggest you make the Othello class be little more than a container of the record of the board and the list of Fields (and you are right, "Othello" is probably not a good name, "Board" would be better). Make separate classes to prescribe the game logic and the player moves. For example, this would be a good structure (note that I just put simple prototypes and no implementation, just to illustrate the structure):

enum Field {
  black,
  white,
  empty
};

class Board {
  private:
    std::vector<Field> squares; //holds the squares of the board (fields).
  public:
    Field operator()(int i,int j) const; //returns the state of the square at (i,j)
    friend class BoardLogic; //allow a base class "BoardLogic" to act freely on the squares of the board (control the game). 
};

//now define the base class for the Board logic (game logic).
class BoardLogic {
  protected:
    Board& board; //this will refer to the board that is currently in play.
    virtual bool PlayTurn() = 0; //virtual method to play one turn.
  public:
    void Run() { //a method to run the game logic.
      while(PlayTurn()) { }; //Run until PlayTurn returns false (end of game).
    };
    BoardLogic(Board& aBoard) : board(aBoard) { }; //you need a constructor to define what board is in play.
    virtual ~BoardLogic() { }; //you need a virtual destructor because this is a polymorphic base class.
};

//now define a base class for player control (or user input).
class PlayerController {
  public:
    enum MoveType { //have a type for the next move.
      AddTile,
      Error,
      Capitulate,
    };
    struct PlayerMove { //type for a player's move
      MoveType type; //what happened?
      int i, j; //if "AddTile", what is the position.
      Field tile; //if "AddTile" what is the tile (black or white).
    };
    virtual PlayerMove getNewMove() = 0; //virtual method to get the next move.

    virtual ~PlayerController() { }; //again, virtual destructor for the base class. 
};

//now derive the BoardLogic for your Othello game:
class Othello : public BoardLogic {
  private:
    PlayerController& player1; //polymorphic reference to player1.
    PlayerController& player2; //polymorphic reference to player2.
  protected:
    bool PlayTurn() {
      //make first player's move.
      while(true) {
        PlayerController::PlayerMove move = player1.getNewMove();
        if(move.type == PlayerController::Error) { 
          //notify about the error
          return false; //end game.
        } else if(move.type == PlayerController::Capitulate) {
          //notify about the win/lose situation.
          return false; //end game.
        } else if(move.type == PlayerController::AddTile) {
          if (/* check validity of the move */) {
            //change the appropriate fields on the board.
            //check wining situation (notify and return false if there is a winner).
            break; //get out of the loop if the move is valid.
          };
        };
      };
      //make second player's move.
      while(true) {
        PlayerController::PlayerMove move = player2.getNewMove();
        //... idem ...
      };
      return true; //signify that the turn went well and that the next turn can start.
    };
  public:
    Othello(Board& aBoard, PlayerController& aPlayer1, PlayerController& aPlayer2) : BoardLogic(aBoard), player1(aPlayer1), player2(aPlayer2) { }; 
};

//now, you could have another game on the same board with different rules as another class too.
class Reverti : public BoardLogic {
 ...
};

//now, for the interesting part, you create different player controllers:
class LocalController : public PlayerController {
  private:
    Player& player; //holds reference to the player's data.
  public:
    PlayerMove getNewMove() {
      //prompt user for next move either with a Windows menu or buttons or a command-line option table, etc.
      //return the next move. 
    };

    LocalController(Player& aPlayer) : player(aPlayer) { };
};

//now, for a network structure (client and server):
class ControllerClient : public PlayerController {
  private: 
    //some socket or other network information necessary to connect to the server.
  public:
    PlayerMove getNewMove() {
      //prompt the server for next move.
      //get the reply
      //if you got a reply:
        //return the next move.
      //if timed-out or something: 
        //return "error" move type.
    };

    ControllerClient(/* pass connection info here */);
};

//now the server is also a local controller (prompts user) so derive from it.
class ControllerServer : public LocalController {
  private: 
    //some socket or other network information necessary to connect to the client.
  public:
    PlayerMove getNewMove() {
      //prompt the user for the next move
      //send it over the network.
    };

    //Now, here, note that would will have to implement some thread of execution or some other method to wait for the client to ask for a new move.

    ControllerClient(/* pass connection info here */);
    
};

//now for the usage:
int main() {
  //for a local game:
  {
  Board my_board; //create a fresh board for the new game.
  Player player1(/* .. */); //create a player 1 (with initial info).
  Player player2(/* .. */); //create a player 2 (with initial info).
  LocalController ctrl1(player1);
  LocalController ctrl2(player2);
  Othello my_game(my_board, ctrl1, ctrl2); //create the game logic for Othello.
  my_game.Run(); //run it!
  }
  
  //for a network game:
  
  {
  Board my_board; //create a fresh board for the new game.
  Player player1(/* .. */); //create a player 1 (with initial info).
  ControllerServer ctrl1(player1, /* network info */);
  ControllerClient ctrl2(/* network info */); //this should connect to the server or something like that.
  Othello my_game(my_board, ctrl1, ctrl2); //create the game logic for Othello.
  my_game.Run(); //run it!
  }
};

Please note that I have skipped the display part here, I leave it to you to figure it out. I hope you see in the above why it is nice to split things up in that way, it makes it very easy to substitute parts for different ones.

commented: Nice logic and tightly written up as well, props to you. +13
commented: Great post +3

Thanks a lot for your time and help, you have definitely given me something to think about.

Thanks again.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.