cpluspluscom / ChessPlusPlus

cplusplus.com Community Project: Modular Chess with support for more types of pieces than traditional Chess
https://cpluspluscom.GitHub.IO/ChessPlusPlus/
36 stars 26 forks source link

Design and Flexibility #2

Closed computerquip closed 10 years ago

computerquip commented 11 years ago

So, I've been thinking of something to come up with in design. To begin, let's get some terminology out of the way:

Board: A grid consisting of valid locations for a piece to move.

Piece: An entity that contains logic on how to move and other entities like itself.

Game: Generally consisting of a board and a set amount of pieces (although not always the case), it handles global logic such as when the game ends, when a piece changes (such as when a pawn reaches the end of the board in a default game), and so on.

Please note that these definitions may need to be extended later since it's difficult coming up with defined terminology to fit the humongous amount of chess variations.

Moving on, there are some things that we know for sure:

So using, this... we get pretty much nothing. :8ball: So, my idea so far consists of implementing the logic of the game using generic virtual types. In example:

class Location {
    int8_t x;
    int8_t y;
    //int8_t z; //Perhaps a 3D variation?
}

class Piece {
    virtual bool move(short location) = 0; //Returns true on success, fail on failure..
    virtual bool attack(short location) = 0;

    std::string name;
    Location location;
};

class Board{
    virtual void place(Piece* piece, Location location) = 0; //This should throw exception on fail.
    virtual int move(Location curLoc, Location newLoc) = 0; //No exception, returns error or 0 on success.
};

namespace default {

//We have no reason to make this class virtual that I can see 
class Game {
    Game(Board* board) { /* Place pieces on the board. */ }

    /* 
    Returns number of moves made. No exception.
    This also checks for "rules" such as a check or checkmate and whether the game has a next turn.
    */
    int next();
}
}

Obvious flaws are whether the piece should move or the board should move the piece or the game should move the piece. I like the idea of virtual interfaces but I'm not so sure about the interface I'm introducing... this is where I fall short most of the time anyways. Any ideas would be lovely.

Also, I'm starting to think there's no reason to seperate "Game" and "Board" since game needs to know the dimensions of the board anyways.

Thumperrr commented 11 years ago

I like what you've started. Throwing out some of my opinions

The board, in my opinion, should be responsible for movement of pieces. The rationale behind that being it is more intuitive for the board, a central object, to keep track of where all of the pieces are placed. If the pieces were allowed to move themselves, it would create complications for things such as detecting whether or not another piece is already in the same coordinate and such. That said, a global enumeration of piece types is required, and each piece must have a type member.

//naming can be changed in the future.
enum piece_type {
    TYPE_PAWN = 0,
    TYPE_KNIGHT,
    TYPE_BISHOP,
    TYPE_ROOK,
    TYPE_QUEEN,
    TYPE_KING
}
class Piece {
    virtual bool move(short location) = 0; //Returns true on success, fail on failure..
    virtual bool attack(short location) = 0;

    std::string name;
    Location location;
    piece_type type;

    sf::Sprite sprite; //I'll elaborate on this
};

I added an sf::Sprite instance to the parent class Piece because, as far as I can see, it will be necessary to have in all base classes as well. The constructor of the base class will be responsible for filling out the sprite with the required image, which leads to the proposal of a resource manager (which I've already mostly designed).

Firstly, I'll get out of the way some more global definitions for image locations.

//in some header yet to be named
const std::string img_pawn_black("res/img/pawn_black.png");
const std::string img_pawn_white("res/img/pawn_white.png");
//... and so on.

The resource manager (as a whole) will

I've split this into two different concepts. The first being an abstract base class, ResourceManager, which handles the storage/retrieval of a resource, and provides a virtual method for the loading of resources. The base class, in this case, sfTextureManager (working title), will

The following is a short example to help push the point.

template<class T, class deleter_type = std::default_delete<T>>
class ResourceManager
{
protected:
    virtual T *onLoadResource(const std::string &location) {} //pure virtual method; base class is responsible for implementing.

public:
    std::shared_ptr<T> Load(const std::string &key) {
        //pseudo code
        if exists in memory
            return shared_ptr to the resource
        else
            call OnLoadResource to load it
            store the loaded resource in memory
            return the resource        
    }
    void Free(const std::string &key) {
        //pseudo code
        free the resource from ResourceManager memory (namely, delete the shared_ptr)
        //resource will not actually deallocate unless the reference count is 0, which it should be before calling Free.
    }
};

class TextureManager : public ResourceManager<sf::Texture>
{
public:
    static TextureManager &getInstance() { //singleton class
        static TextureManager instance;
        return instance;
    }
protected:
   sf::Texture *onLoadResource(const std::string &location) {
       sf::Texture *ret = new sf::Texture();
       ret->loadFromFile(location);
       return ret;
   }
};

//usage:
TextureManager::getInstance().Load(img_pawn_black); //img_pawn_black defined earlier.

So then every instance of a piece can call Load, and multiple allocations of the image will not occur. So that's what I have to say about that.

I'm working on the full code, which I'll push to the master either tonight or tomorrow afternoon.

computerquip commented 11 years ago

That's one thing I don't want though is hard coded pieces! There are thousands of variations of chess, some containing pieces that aren't what you're used to: http://en.wikipedia.org/wiki/Chess_variant

Since this isn't a very heavy application of algorithms, I thought using "name" to identify piece types would suffice.

cire3791 commented 11 years ago

It would be beneficial to keep the design of boards, pieces, moves and game logic free of things like sf::Texture and sf::Sprite. There's no reason those things should be coupled to the graphics library.

Thumperrr commented 11 years ago

computerquip, it would be impossible to code one game that fits every variant of chess. Which is why they're called variants. We could have XML setting files containing names of piece files, so they could be changed externally?

cire, That makes way too much sense. Thanks, definitely keeping that in mind. Drop what I said about including an sf::Sprite in piece.

computerquip commented 11 years ago

I'm not so sure. It's like having different gametypes in one game. For instance, having Deathmatch, Capture the Flag, and Domination in Modern Warfare 2. Or having different gametypes of tetris. Just like any other console game really...

I'm not saying make one that fits every variant but just enough to be able to implement most with minimal problems.

Thumperrr commented 11 years ago

Then, as mentioned earlier, we could have external configuration files that we can change in the future. Either way we need to have the paths to all of the pieces coded somewhere in the game, whether we use those pieces or not.

jaredready commented 11 years ago

I think this should be kept as the classic chess that everyone is used to. And yes we should also definitely keep the graphics components outside of the internal classes (piece, board, space, etc). That should be handled separately.

What about having the Piece class have its own internal representation of the grid, with valid places that given piece can move to?

Thumperrr commented 11 years ago

Agree on that.

Disagree on the second point. There are <= 32 pieces on the board at a given time. So up to 32 pieces have to have a representation of the grid, and every time the grid is altered up to 32 representations of it have to be altered as well. I think a better idea would be to give board a member function or something called bool isFree(int xpos, int ypos), which returns true if the position is free to move to, and returns false if it isn't. Then piece can call that function and find out whether the move is valid or not.

jaredready commented 11 years ago

But each piece moves differently. Should the board be responsible for handling each piece's movement behavior, or should the piece be responsible for that? It makes more sense for the piece to, but like you said that leads to lots of recalculations. Granted, it would only need to recalculate when a piece moves. As far as the piece is concerned it just knows where it can move. The board can keep track of if that place is free or not (be it line of sight issue, or preoccupied by allied piece).

jaredready commented 11 years ago

Sorry, each piece would only have to recalculate when it moves. The other pieces don't care. I'm just saying each piece keeps basically the movement behavior of itself. The board can handle whether or not that movement leads to an attack, is impossible due to line of sight, or the space is occupied.

naraku9333 commented 11 years ago

Maybe a piece can hold a std::vector<std::list<Point>> where each list is a possible move path and Point is a point relative to the pieces position.

Thumperrr commented 11 years ago

Okay, yeah I take back what I said about the board handling movement patterns. Unique pieces should know where it's allowed to move. Up two positions, over three up 2, diagonally, etc. The board should have member function 'isOpen" which the piece can use to see whether or not a specific movement spot is open. Yeah?

jaredready commented 11 years ago

Are we against having an abstract base class Piece, with derived classes for each type of piece? We could then store a std::vector<Piece> in class Board and populate it with all the pieces. Then we can just iterate through the vector each turn checking for validity and what not.

I realize this won't be as flexible as adding a new type of piece will require recompilation, but it's a thought.

computerquip commented 11 years ago

I have an example I'll post up here in a few moments... I just need to finish basic game logic.

Thumperrr commented 11 years ago

That was the original idea (i think). Piece would be the base class, derived classes for pawn, rook, bishop, knight, etc would be specific to that piece type.

I'll wait on your example, computerquip. In the mean time, I'm going to start a new issue on the topic of resource management.

computerquip commented 11 years ago

So, this turned out a lot more fucked up than I really (ever) wanted. It's pretty nasty.. but try and look at it from an overall picture than the implementation details... I've clearly been sitting too long in C and some other similar languages... I apologize if I messed up some syntax seriously somewhere:

http://codepad.org/nGqZQaL0

However, note that this isn't a working example. I don't have time for that right now, this is just a sample of the direction I was thinking of. Criticize the crap out of it but like I said, try and ignore the implementation details, more towards the use of the interface. Good night!

computerquip commented 11 years ago

Eh, I just noticed the namespace name "default" doesn't work because of keyword collision. Just ignore that. >.>

Thumperrr commented 11 years ago

I like how you separated the pawn piece into a separate namespace for white. I didn't think of it before, but black pawns will have a different movement method than white pawns because of the way the board is oriented. (white will move up, black will move down). Would there have to be two entirely separate pawn classes for that?

Thumperrr commented 11 years ago

And that's what the close button does.. lol

jaredready commented 11 years ago

Ooh good point. I knew the white and black should be distinguished, but I hadn't thought of the fact that their movements would be reversed. At least for pawns. The rest can move forward and backwards.

jaredready commented 11 years ago

BTW, I have an account on LucidChart. I can give some people access to a project. It's nice for collaboration and planning/design if you haven't used it before.

Thumperrr commented 11 years ago

Or, again with the enumeration

enum piece_color {
    black = 0,
    white = 1;
}

class Piece
{
    bool move(int x, int y) {
        if(color = black) //do one thing
        else if(color == white) //do the other
        else //blow the computer up because what the hell.
    piece_color color;
};

EDIT: I haven't heard of LucidChart before. What's it used for?

jaredready commented 11 years ago

http://www.lucidchart.com/invitations/accept/5125b5a1-9494-4c78-8338-21570a004d18

This will give you owner rights to the project page (I think). There's nothing there yet. This might work for only one person, if so I can generate more of these/email them

EDIT: LucidChart is a pretty handy online diagramming tool. It has a bunch of stuff you can do with it (think Visio online, but easier) but I've only ever used it to diagram projects and throw together mock UIs.

jaredready commented 11 years ago

Thumper, that's kind of what I was thinking. I just threw a bool in my Piece class. An enum is a little cleaner though.

Thumperrr commented 11 years ago

I like enumerations, they do look clean. I think doing that instead of separating white and black pawns into two different classes will save so much redundancy. Btw, I've posted up another issue thread about resource management if you get the opportunity to run by it.

jaredready commented 11 years ago

From reading through it, it looks good to me. I like the simplicity of it. Doesn't have anything it doesn't need.

computerquip commented 11 years ago

The only thing that would need to actually differ between white and black are the pawns. Outside of that, they would be the same on both sides.

While it may be slightly redundant, its the only type of piece that will require the redundancy plus the logic of it isn't complex at all. So far, here's my todo list:

Thumperrr commented 11 years ago

Wouldn't it be important to differentiate between white and black for every piece? Especially if you intend on calculating score. There has to be a way to tell one teams knight apart from the others. Otherwise, by that logic, you would be able to move the enemies pieces while it's your turn :P

Speaking of networking, have you got any ideas how that should be structured? Like what exactly is going to be accomplished. Will there be a server application? Or will the one application be able to act as both a client and a server depending on whether it's starting the game or not? What will be the protocol for sending information? Will the users be able to chat in the game?

computerquip commented 11 years ago

Networking can use whatever we want. We aren't creating an FPS game so we can't fuck up nearly as bad as Minecraft did.

Here's what I don't want. I don't want to see direct use of POSIX or BSD socket APIs. These differ in behavior from platform to platform and there are tons of alternatives in this day and age.

I wouldn't mind using boost::asio either but most people have problems with how strange the API is.

We are also not limited to just using basic TCP sockets. I have no problem with using something like zeromq (which I've read is quite interesting and simplistic with C++ bindings as well).

I don't like direct peer-to-peer systems since this can be a minor security threat giving out address info like that. It forces people to want to use proxies and torify which I don't like. So, I'd like to eventually have some master server software that hosts the game for them while masking the location of each client like a sane server. Unfortunately, I'll be implementing the simple direct peer-to-peer system since it's quick to implement. I'll try the server idea later.

For the protocol, packets will probably have a packet structure like the following:

This packet above moves the furthest to the left pawn on white up two spaces. Well also have a version system so each system can sync, probably using a string for each "version" of each type of chess.

Where 0x00 is the header, 0x0001 is the version of the gametype, 0x0D is the size of the following string, and "DefaultChess" (which is 13 characters long) is the gametype identifier.

But be reminded that I'm not the only mind in this project and there are probably better solutions so I can't say just by myself.

jaredready commented 11 years ago

Can we not just use the networking capabilities that come with SFML? I've never done any sort of network programming so I'm no help here.

Thumperrr commented 11 years ago

I would use sf sockets as well. I'm not great at the networking thing either though. Also, check out my pull request. I've laid out some framework. Need opinions.

jaredready commented 11 years ago

It looks pretty solid, you've been busy.

Thumperrr commented 11 years ago

I found out last night that I didn't have to go into work today, so I was up until 3am or so drafting it, and finished it up this morning with all this spare time I have today. :) I should probably get to doing some coursework that's due soon... Nah.

cire3791 commented 11 years ago

I don't think inheritance for the pieces is desirable.

Using the strategy pattern (each type of piece has it's own strategy for moving) seems more appropriate to me.

A strategy can be a functor taking the position of the piece, the last move made by the opponent (necessary to determine the validity of en passant moves) and a reference to the current board state, which returns a list of all valid destinations for a piece. It may be useful to have the list contain not only a list of valid destinations, but a list of corresponding consequences if the piece is moved to that particular location, such as pawn at location XX captured for an en passant move, or Rook at position XX moved to position YY for a castling move.

When I say list here, it's in an implemenation agnostic sort of way.

Not only does this deal with validating move, it makes it very easy to indicate what valid moves are available for particular pieces as the user selects them.

jaredready commented 11 years ago

I think we need to break this all down into a list of what needs done. I feel like we're gonna start stepping on each other if we don't define what exactly needs done.

ghost commented 11 years ago

I won't be much help during the designing phase of this project, since I'm probably the least experienced here so I will take a back seat on that part. But when you do get a list of things to do I'll chip in what I can and where I can.

Thumperrr commented 11 years ago

I agree with ResidentBiscuit. The repo's got a bunch of game framework in it that I've put together this morning. Once the logic behind the chess game is put together it can be implemented in the AppStateGame class. I think that's what needs to be done most at the moment; finishing chess logic that computerquip started. I'm taking a bit of a break. Mentally exhausted at the moment. https://github.com/Thumperrr/ChessPlusPlus/blob/master/Framework%20Flow.png That pretty lightly explains how everything is flowing together.

jaredready commented 11 years ago

I don't know how much help I'll be during the design either. I've never designed or even worked on a game before, so all this is new to me. And I'm kind of just lost :/

Thumperrr commented 11 years ago

I've never worked in a group on a project before. I've only ever done solo things. So I know how you feel. Where are you stuck at? I'll clear up anything I can.

Also, if you're looking for something to do, a fully working implementation of chess (purely logical, no graphics or anything involved) is what I think needs to be worked on the most. Computerquip had a good start from what he posted last night. Once that gets done I or someone else can integrate it into AppStateGame and get the ball rolling.

jaredready commented 11 years ago

I'm just stuck in my head haha. I go to write, but then I have several ideas for how it should all interact and get stuck debating with myself and end up shooting down every idea. I don't know enough about how games actually work to be able to provide useful input in the design and planning of this, I'm afraid.

I can work on the chess logic though.

jaredready commented 11 years ago

BTW, I just noticed LucidChart has a chat functionality. We can also exchange skype info or something if we wish to have some sort of IM set up. We could also just IRC.

ghost commented 11 years ago

I could also set up a chat client on my website that we could use if its needed (Ignore the shameless plug to get more vistors ;p).

jaredready commented 11 years ago

I'm all for having a chat system to use. Would make communication much easier

naraku9333 commented 11 years ago

I'd vote IRC, I think it will make it easier for anyone new to join in the conversation.

jaredready commented 11 years ago

Sounds good, know of a server we can use for this?

naraku9333 commented 11 years ago

ChessPlusPlus on irc.freenode.net

LukeLeber commented 11 years ago

I'm a little late to the party, but if an asio networking core class set is still needed I can write something up.

LukeLeber commented 11 years ago

I don't see an edit button, but I'll just say that this seems like a situation where a central server is required between clients to act as the undeniable arbiter of truth.

naraku9333 commented 11 years ago

Would the server be an intermediary to all communication between clients or just facilitate a peer to peer connection?

computerquip commented 11 years ago

The server would "mask" clients and make sure each client isn't cheating (which means more secure). It can also be a querying system that allows us to search for an opponent instead of having to know the other clients address. But it's difficult to make since we're having to communicate with multiple sockets at once.