8
\$\begingroup\$

I coded a (more or less) simple Tetris game as an example for some of the other trainees in my company. Since I am used to my coding style I would like to have some other people to look over it and maybe give me some advices to improve the readability of the code.

Some of the class names in the code (such as actor) are chosen to give some more general ideas about the parts in a video game (not that I am a pro in that area) so if I had in mind to just develop a tetris game maybe I would have chosen different names so please keep that in mind.

Yes, I am planning to split the code in header/cpp files, and yes I left out the header guards.

typedefs.h:

//The pixelUtility isn't included in the uploaded code since that is what i provide others as a "blackbox" library to work with, as well as the mangolib.h
//Please focus at the game code and not the naming of the mango:: or the graphic:: classes/structs, they will be explained to the people who use them pretty extensive
//The mango library is used since it implements some c++11 features I want to use and our IDE does not provide (shared_ptr and lambda for instance and later threading)
//btw. feel free to use this code as you please, as general example or if necessary (I hope it's not that bad ;) ) as negativ example, but I won't provide
//a code sample without my library, but im pretty sure it won't be much work to change the code to regular c++11 for whoever wants to use it.

#define NO_THREAD_ALLOC //disables thread_safety in my customallocator for better performance since this project is single threaded and locks are not needed
#include <Gaming/pixelutility.h> //header which contains graphic::Character, the GraphicController the KatCoord struct etc, includes <mangolib.h> --> mango::

namespace engine
{
    typedef typename graphic::Character                                    actor; //a simple graphic class with a position, animations and collision
    typedef typename mango::shared_ptr<graphic::Character>                 actor_handle;
    typedef typename mango::vector<mango::shared_ptr<graphic::Character> > actor_list;
    typedef typename graphic::KatCoord                                     point; //simple point class p(int32 x, int32 y)
    typedef typename mango::vector<graphic::KatCoord>                      point_list;
    typedef mango::int32                                                   layerID;
    typedef mango::int32                                                   counter;
}

stone.h:

#include "typedefs.h"

#define Rot0   0
#define Rot90  1
#define Rot180 2
#define Rot270 3

namespace engine
{
    namespace tetris
    {
        namespace shapes
        {
            enum shape
            {
                RLShape,
                LLShape,
                NTShape,
                NIShape,
                RZShape,
                LZShape,
                SQShape
            };
        }

        //Shapes
        //
        // RL: #
        //     #
        //     # #
        //
        // LL:   #
        //       #
        //     # #
        //
        // NT: # # #
        //       #
        //
        // NI: #
        //     #
        //     #
        //     #
        //
        // RZ: # 
        //     # #
        //       #
        //
        // LZ:   #
        //     # #
        //     #
        //
        // SQ: # #
        //     # #
        //
        //Shapes

        typedef typename shapes::shape shape;

        class RotationProcessor //this class is meant to make the Stone class smaller so I outsourced the rotation to this class
        {
        public:
            void Rotate(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, shape sh, bool counterClockwise)
            {
                switch (sh)
                {
                case shapes::RLShape:
                    RotateRL(segments, state, counterClockwise);
                    break;
                case shapes::LLShape:
                    RotateLL(segments, state, counterClockwise);
                    break;
                case shapes::NTShape:
                    RotateNT(segments, state, counterClockwise);
                    break;
                case shapes::NIShape:
                    RotateNI(segments, state, counterClockwise);
                    break;
                case shapes::RZShape:
                    RotateRZ(segments, state, counterClockwise);
                    break;
                case shapes::LZShape:
                    RotateLZ(segments, state, counterClockwise);
                    break;
                default:
                    break;
                }
            }

            void RotateNI(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
            {
                if (counterClockwise)
                {
                    RotateNI(segments, state, false);
                    RotateNI(segments, state, false);
                }

                if (!state)
                {
                    segments->at(0)->Move(  0,  20);
                    segments->at(1)->Move( 10,  10);
                    segments->at(2)->Move( 20,   0);
                    segments->at(3)->Move( 30, -10);

                    state = !state;

                    return;
                }

                segments->at(0)->Move(  0, -20);
                segments->at(1)->Move(-10, -10);
                segments->at(2)->Move(-20,   0);
                segments->at(3)->Move(-30,  10);

                state = !state;

                return;
            }

            void RotateRZ(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
            {
                if (counterClockwise)
                {
                    RotateRZ(segments, state, false);
                    RotateRZ(segments, state, false);
                }

                if(!state)
                {
                    segments->at(0)->Move( 20,  10);
                    segments->at(1)->Move( 10,   0);
                    segments->at(2)->Move( 0 ,  10);
                    segments->at(3)->Move(-10,   0);

                    state = !state;

                    return;
                }

                segments->at(0)->Move(-20, -10);
                segments->at(1)->Move(-10,   0);
                segments->at(2)->Move(  0, -10);
                segments->at(3)->Move( 10,   0);

                state = !state;

                return;
            }

            void RotateLZ(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
            {
                if (counterClockwise)
                {
                    RotateLZ(segments, state, false);
                    RotateLZ(segments, state, false);
                }

                if (!state)
                {
                    segments->at(0)->Move(-10, 10);
                    segments->at(1)->Move(  0,  0);
                    segments->at(2)->Move( 10, 10);
                    segments->at(3)->Move( 20,  0);

                    state = !state;

                    return;
                }

                segments->at(0)->Move( 10, -10);
                segments->at(1)->Move(  0,   0);
                segments->at(2)->Move(-10, -10);
                segments->at(3)->Move(-20,   0);

                state = !state;

                return;
            }

            void RotateLL(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
            {
                if (counterClockwise)
                {
                    RotateLL(segments, state, false);
                    RotateLL(segments, state, false);
                }

                if (Rot0 == state)
                {
                    segments->at(0)->Move( 10,  20);
                    segments->at(1)->Move(  0,  10);
                    segments->at(2)->Move(-10,   0);
                    segments->at(3)->Move(  0, -10);

                    ++state;

                    return;
                }
                if (Rot90 == state)
                {
                    segments->at(0)->Move(-20,   0);
                    segments->at(1)->Move(-10, -10);
                    segments->at(2)->Move(  0, -20);
                    segments->at(3)->Move( 10, -10);

                    ++state;

                    return;
                }

                if (Rot180 == state)
                {
                    segments->at(0)->Move(  0, -10);
                    segments->at(1)->Move( 10,   0);
                    segments->at(2)->Move( 20,  10);
                    segments->at(3)->Move( 10,  20);    

                    ++state;

                    return;
                }

                if (Rot270 == state)
                {
                    segments->at(0)->Move( 10, -10);
                    segments->at(1)->Move(  0,   0);
                    segments->at(2)->Move(-10,  10);
                    segments->at(3)->Move(-20,   0);    

                    state = Rot0;

                    return;
                }

                return;
            }

            void RotateRL(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise) //GOTO: 
            {
                if (counterClockwise)
                {
                    RotateRL(segments, state, false);
                    RotateRL(segments, state, false);
                }

                if (Rot0 == state)
                {
                    segments->at(0)->Move( 20,  10);
                    segments->at(1)->Move( 10,   0);
                    segments->at(2)->Move(  0, -10);
                    segments->at(3)->Move(-10,   0);

                    ++state;

                    return;
                }
                if (Rot90 == state)
                {
                    segments->at(0)->Move(-10,  10);
                    segments->at(1)->Move(  0,   0);
                    segments->at(2)->Move( 10, -10);
                    segments->at(3)->Move(  0, -20);

                    ++state;

                    return;
                }

                if (Rot180 == state)
                {
                    segments->at(0)->Move(-10,   0);
                    segments->at(1)->Move(  0,  10);
                    segments->at(2)->Move( 10,  20);
                    segments->at(3)->Move( 20,  10);

                    ++state;

                    return;
                }

                if (Rot270 == state)
                {
                    segments->at(0)->Move(  0, -20);
                    segments->at(1)->Move(-10, -10);
                    segments->at(2)->Move(-20,   0);
                    segments->at(3)->Move(-10,  10);

                    state = Rot0;

                    return;
                }

                return;
            }

            void RotateNT(mango::shared_ptr<engine::actor_list> segments, mango::int32& state, bool counterClockwise)
            {
                if (counterClockwise)
                {
                    RotateNT(segments, state, false);
                    RotateNT(segments, state, false);
                }

                if (Rot0 == state)
                {
                    segments->at(0)->Move(  0,   0);
                    segments->at(1)->Move(-10, -10);
                    segments->at(2)->Move(  0, -20);
                    segments->at(3)->Move(-20,   0);

                    ++state;

                    return;
                }
                if (Rot90 == state)
                {
                    segments->at(0)->Move(  0,  10);
                    segments->at(1)->Move( 10,   0);
                    segments->at(2)->Move( 20,  10);
                    segments->at(3)->Move(  0, -10);

                    ++state;

                    return;
                }

                if (Rot180 == state)
                {
                    segments->at(0)->Move(-10, -10);
                    segments->at(1)->Move(  0,   0);
                    segments->at(2)->Move(-10,  10);
                    segments->at(3)->Move( 10, -10);

                    ++state;

                    return;
                }

                if (Rot270 == state)
                {
                    segments->at(0)->Move( 10,  0);
                    segments->at(1)->Move(  0, 10);
                    segments->at(2)->Move(-10,  0);
                    segments->at(3)->Move( 10, 20);

                    state = Rot0;

                    return;
                }

                return;
            }
        };

        class Stone
        {
        public:
            Stone(mango::shared_ptr<engine::actor_list> segments, mango::shared_ptr<engine::point_list> collisionPoints, engine::point pos, shape sh)
                : m_Segments(segments), m_CollisionPoints(collisionPoints), m_Position(pos), m_Shape(sh), m_State(0)
            {}

            void Draw(Gdiplus::Graphics* graphic, mango::mutex& lock)
            {
                lambda((engine::actor_handle actor, Gdiplus::Graphics* graphic, mango::mutex& lock) { actor->Draw(graphic, lock); return 0; }, 1);
                mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, graphic, lock);
            }

            void Rotate(bool counterClockwise)
            {
                RotationProcessor re;

                re.Rotate(m_Segments, m_State, m_Shape, counterClockwise);

                mango::shared_ptr<engine::point_list> tmpCollisionPoints = GetPoints(m_Segments);

                m_CollisionPoints->swap(*tmpCollisionPoints);
            }

            void Move(mango::int32 x, mango::int32 y)
            {
                m_Position.X += x;
                m_Position.Y += y;

                lambda((engine::actor_handle actor, mango::int32 x, mango::int32 y) { actor->Move(x, y); return 0; }, 1);
                mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, x, y);

                m_CollisionPoints = GetPoints(m_Segments);
            }

            bool Contains(mango::shared_ptr<Stone> actor)
            {
                mango::int32 result = 0;
                mango::int32* resultHandle = &result;
                lambda((engine::actor_handle actor, const engine::point_list& points, mango::int32* result)
                {
                    for (mango::int32 current = 0; points.size() > current; ++current)
                    {
                        *result += actor->Contains(points.at(current));
                    }

                    return 0; 
                }, 1);
                mango::for_each(m_Segments->begin(), m_Segments->end(), exp1, *actor->m_CollisionPoints, resultHandle);

                return result != 0;
            }

            bool ContainedBy(mango::shared_ptr<engine::actor> actor)
            {
                mango::int32 result = 0;
                for (mango::int32 current = 0; m_CollisionPoints->size() > current; ++current)
                {
                    result += actor->Contains(m_CollisionPoints->at(current));
                }

                return result != 0;
            }

            mango::shared_ptr<engine::actor_list> GetSegments()
            {
                mango::shared_ptr<engine::actor_list> tmp = m_Segments;
                m_Segments.reset();

                return tmp;
            }

            bool GetVisibility()
            {
                return true;
            }

            static mango::shared_ptr<engine::point_list> GetPoints(mango::shared_ptr<engine::actor_list> actors)
            {
                mango::shared_ptr<engine::point_list> tmpCP = new engine::point_list();

                for (mango::int32 current = 0; actors->size() > current; ++current)
                {
                    engine::point pos = actors->at(current)->GetPosition();

                    tmpCP->push_back(engine::point(pos.X +  1, pos.Y +  1));
                    tmpCP->push_back(engine::point(pos.X +  9, pos.Y +  1));
                    tmpCP->push_back(engine::point(pos.X +  9, pos.Y +  9));
                    tmpCP->push_back(engine::point(pos.X +  1, pos.Y +  9));

                    tmpCP->push_back(engine::point(pos.X +  3, pos.Y +  3));
                    tmpCP->push_back(engine::point(pos.X +  7, pos.Y +  7));
                    tmpCP->push_back(engine::point(pos.X +  7, pos.Y +  3));
                    tmpCP->push_back(engine::point(pos.X +  3, pos.Y +  7));
                }

                return tmpCP;
            }

        private:
            mango::shared_ptr<engine::actor_list> m_Segments;
            mango::shared_ptr<engine::point_list> m_CollisionPoints;
            engine::point                         m_Position;
            shape                                 m_Shape;
            mango::int32                          m_State;
        };

        typedef typename Stone                                    t_actor; 
        typedef typename mango::shared_ptr<Stone>                 t_actor_handle;
        typedef typename mango::vector<mango::shared_ptr<Stone> > t_actor_list;
        //not optimal naming ---> t_ => tetris_ to make it easier to spot if you have an engine::actor or a engine::tetris::actor

        class StoneFactory
        {
        public:
            StoneFactory(mango::int32 stoneSize)
                : m_StoneSize(stoneSize)
            {}

            t_actor_handle BuildStone(shape stoneShape, engine::point pos)
            {
                switch (stoneShape)
                {
                    case shapes::RLShape:
                        return BuildRLStone(pos, stoneShape);
                        break;
                    case shapes::LLShape:
                        return BuildLLStone(pos, stoneShape);
                        break;
                    case shapes::NTShape:
                        return BuildNTStone(pos, stoneShape);
                        break;
                    case shapes::NIShape:
                        return BuildNIStone(pos, stoneShape);
                        break;
                    case shapes::RZShape:
                        return BuildRZStone(pos, stoneShape);
                        break;
                    case shapes::LZShape:
                        return BuildLZStone(pos, stoneShape);
                        break;
                    case shapes::SQShape:
                        return BuildSQStone(pos, stoneShape);
                        break;
                    default:
                        return nullptr;
                        break;
                }

                return nullptr;
            }

        private:

            t_actor_handle BuildSQStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos1.Y += 10;
                pos2.Y += 20;

                pos3.Y += 10;
                pos4.Y += 20;

                pos3.X += 10;
                pos4.X += 10;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_vm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_vm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_vm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_vm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildRLStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos2.Y += 10;
                pos3.Y += 20;

                pos4.X += 10;
                pos4.Y += 20;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_bm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_bm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_bm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_bm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildLLStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos1.X += 10;
                pos2.Y += 10;
                pos2.X += 10;
                pos3.Y += 20;
                pos3.X += 10;
                pos4.Y += 20;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_rm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_rm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_rm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_rm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildNTStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos1.X += 10;
                pos1.Y += 10;
                pos2.X += 10;
                pos2.Y += 20;
                pos3.Y += 20;
                pos4.X += 20;
                pos4.Y += 20;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_gm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_gm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_gm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_gm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildNIStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos2.Y += 10;
                pos3.Y += 20;
                pos4.Y += 30;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_ym", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_ym", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_ym", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_ym", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildRZStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos2.Y += 10;
                pos3.X += 10;
                pos3.Y += 10;
                pos4.X += 10;
                pos4.Y += 20;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_pm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_pm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_pm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_pm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            t_actor_handle BuildLZStone(engine::point pos, shape sh)
            {
                engine::point pos1 = pos;
                engine::point pos2 = pos;
                engine::point pos3 = pos;
                engine::point pos4 = pos;

                pos1.X += 10;
                pos2.X += 10;
                pos2.Y += 10;
                pos3.Y += 10;
                pos4.Y += 20;

                mango::shared_ptr<engine::actor_list> actors = new engine::actor_list();

                engine::actor_handle tmp = new engine::actor(pos1, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_tm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos2, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_tm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos3, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_tm", 1);
                actors->push_back(tmp);

                tmp = new engine::actor(pos4, m_StoneSize, m_StoneSize);
                tmp->LoadAnimation(0, "square_tm", 1);
                actors->push_back(tmp);

                t_actor_handle tmpActor = new t_actor(actors, t_actor::GetPoints(actors), pos, sh);

                return tmpActor;
            }

            mango::int32 m_StoneSize;
        };

        void rotatePoint(engine::point& p, const engine::point& center)
        {
            mango::int32 tmpX = p.X - center.X;
            mango::int32 tmpY = p.Y - center.Y;

            p.X = center.X + tmpY;
            p.Y = center.Y - tmpX;
        }
    }
}

engine.h:

#include "typedefs.h"
#include "stone.h"

namespace engine
{
    class Engine
    {
    public:
        Engine()
            : m_Graphic(new graphic::GraphicController()), m_Factory(10), m_UpdateInterval(0.5f), m_SoundDuration(83.0f)
        {
            srand((mango::uns32)time(nullptr));

            CreateNewPlayerActor();

            //the magic numbers will be wrapped in const values when I split the files in header/cpp

            m_Ground      = new engine::actor(engine::point(0, 290), 640, 50, 0, false); //engine::actor(point position, int32 width, int32 height, int32 renderLayer, bool visibility);
            m_Ground->LoadAnimation(0, "default", 1); //LoadAnimation(int32 animationID, string fileName, int32 numberOfFrames); //"default" is an empty transparent sprite

            m_Background  = new engine::actor(engine::point(8, -8), 204, 291, 0, true);
            m_Background->LoadAnimation(0, "Background", 1);

            m_LeftBorder  = new engine::actor(engine::point(0, 0), 10, 290, 0, false);
            m_LeftBorder->LoadAnimation(0, "default", 1);

            m_RightBorder = new engine::actor(engine::point(210, 0), 10, 290, 0, false);
            m_RightBorder->LoadAnimation(0, "default", 1);

            for (mango::int32 current = 0; 290 > current * 10; ++current)
            {
                m_Layers.push_back(mango::key_pair<layerID, counter>(current, 0));
            }
        }

        void Play()
        {
            bool isRunning = true;

            m_Timer.start();
            m_SpeedTimer.start();
            m_SoundTimer.start();

            audio::playSound("Sounds\\sound.wav");

            while (isRunning && nullptr != m_PlayerActor)
            {
                Audio();
                Move();
                Controls(isRunning);
                Draw();
                Present();
            }

            audio::stopSound();
        }

    private:
        void Audio()
        {
            m_SoundTimer.tick();
            if (m_SoundDuration <= m_SoundTimer.totalTime())
            {
                audio::stopSound();
                audio::playSound("Sounds\\sound.wav");
                ResetTimer(m_SoundTimer);
            }
        }

        void CreateNewPlayerActor()
        {
            while (nullptr == m_PlayerActor)
            {
                mango::int32 shape = rand() % 40; //I chose a higher value to get more different shapes spawned
                m_PlayerActor = m_Factory.BuildStone(*reinterpret_cast<engine::tetris::shape*>(&shape), engine::point(150, 1));
            }
        }

        void RemoveLayer(mango::int32 layer)
        {
            for (mango::int32 current = 0; m_Actors.size() > current; ++current) //erase actors in layer
            {
                if ((mango::int32)(m_Actors.at(current)->GetPosition().Y / 10) == layer)
                {
                    m_Actors.erase(current);
                    --current;
                }
            }

            for (mango::int32 current = 0; m_Actors.size() > current; ++current)  //move down actors in higher layers
            {
                if ((mango::int32)(m_Actors.at(current)->GetPosition().Y / 10) < layer)
                {
                    m_Actors.at(current)->Move(0, 10);
                }
            }

            for (mango::int32 current = layer; 0 < current; --current)
            {
                m_Layers.at(current).a = m_Layers.at(current - 1).a; // mango::key_pair<class KEY, class A> x; ---> x.a; to access the value;
            }

            m_Layers.front().a = 0;
        }

        void Collision()
        {
            if (0 < m_Layers.at(4).a) //Layer 4 is the highest layer to reach until game over
            {
                m_Actors.clear();
                m_PlayerActor.reset();

                m_Background = new engine::actor(engine::point(10, 100), 600, 100);
                m_Background->LoadAnimation(0, "game_over", 1);

                return;
            }

            //lambda((parameters){function}, int32 ID); --> exp + ID is used to call the expression the id has to be unique in its scope
            mango::int32 result = 0;
            lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result) 
            {
                if (playerActor->ContainedBy(actor))
                {
                    ++result;
                }

                return 0;
            }, 2);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
            }

            if (m_PlayerActor->ContainedBy(m_Ground) || 0 != result)
            {
                m_PlayerActor->Move(0, -10);

                mango::shared_ptr<engine::actor_list> actors = m_PlayerActor->GetSegments();

                for (mango::int32 current = 0; actors->size() > current; ++current) //transfer segments to global actor pipeline
                {
                    m_Actors.push_back(actors->at(current));
                    ++m_Layers.at((actors->at(current)->GetPosition().Y) / 10).a;
                }

                m_PlayerActor.reset();
                CreateNewPlayerActor();

                const mango::int32 layerWidth = 20;
                for (mango::int32 current = m_Layers.size() - 1; 0 <= current; --current) //Remove Filled layers
                {
                    if (layerWidth == m_Layers.at(current).a)
                    {
                        RemoveLayer(current);
                        current = m_Layers.size() - 1;
                    }
                }
            }
        }

        void ResetTimer(mango::delta_timer& timer)
        {
            timer.stop();
            timer.reset();
            timer.start();
        }

        void ChangeSpeed()
        {
            m_SpeedTimer.tick();

            if (30.0f <= m_SpeedTimer.totalTime() && m_UpdateInterval > 0.1f)
            {
                ResetTimer(m_SpeedTimer);

                m_UpdateInterval -= 0.05f; //increase game speed over time
            }
        }

        void Move()
        {
            m_Timer.tick();
            if (m_UpdateInterval <= m_Timer.totalTime())
            {
                ResetTimer(m_Timer);
                ChangeSpeed();

                m_PlayerActor->Move(0, 10); //move player down

                Collision();
            }
        }

        void TryRotationLeft()
        {
            m_PlayerActor->Rotate(true);
            mango::int32 result = 0;
            lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
            {
                if (playerActor->ContainedBy(actor))
                {
                    ++result;
                }

                return 0;
            }, 2);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
            }

            if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
            {
                m_PlayerActor->Rotate(false); //undo rotation if collision detected
            }
        }

        void TryRotationRight()
        {
            m_PlayerActor->Rotate(false);
            mango::int32 result = 0;
            lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
            {
                if (playerActor->ContainedBy(actor))
                {
                    ++result;
                }

                return 0;
            }, 2);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
            }

            if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
            {
                m_PlayerActor->Rotate(true); //undo rotation if collision detected
            }
        }

        void TryMoveLeft()
        {
            m_PlayerActor->Move(-10, 0);
            mango::int32 result = 0;
            lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
            {
                if (playerActor->ContainedBy(actor))
                {
                    ++result;
                }

                return 0;
            }, 2);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
            }

            if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
            {
                m_PlayerActor->Move(10, 0);
            }
        }

        void TryMoveRight()
        {
            m_PlayerActor->Move(10, 0);
            mango::int32 result = 0;
            lambda((engine::actor_handle actor, engine::tetris::t_actor_handle playerActor, mango::int32& result)
            {
                if (playerActor->ContainedBy(actor))
                {
                    ++result;
                }

                return 0;
            }, 2);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp2, m_PlayerActor, result);
            }

            if (0 != result || m_PlayerActor->ContainedBy(m_LeftBorder) || m_PlayerActor->ContainedBy(m_RightBorder))
            {
                m_PlayerActor->Move(-10, 0);
            }
        }

        void TryMoveDown()
        {
            m_PlayerActor->Move(0, 10);
            Collision();
        }

        void Controls(bool& isRunning)
        {
            mango::Byte input = ' ';

            if (_kbhit())
            {
                input = mango::archive::mgetch();

                if ('q' == input)
                {
                    TryRotationLeft();
                }
                if ('e' == input)
                {
                    TryRotationRight();
                }
                if ('a' == input)
                {
                    TryMoveLeft();
                }
                if ('d' == input)
                {
                    TryMoveRight();
                }
                if ('s' == input)
                {
                    TryMoveDown();
                }
                if ('X' == input)
                {
                    isRunning = false;
                }
            }
        }

        void Draw()
        {
            m_Graphic->ClearBuffer();
            m_Graphic->Draw(m_Background.get()); //Background is drawn first

            lambda((engine::actor_handle actor, graphic::GraphicController* gc) { gc->Draw(actor.get()); return 0; }, 1);

            if (0 != m_Actors.size())
            {
                mango::for_each(m_Actors.begin(), m_Actors.end(), exp1, m_Graphic);
            }

            if (nullptr != m_PlayerActor) //Player is drawn last
            {
                m_Graphic->Draw(m_PlayerActor.get());
            }
        }

        void Present()
        {
            m_Graphic->Present();
        }

        mango::vector<mango::key_pair<mango::int32, mango::int32> > m_Layers;
        engine::actor_handle                                        m_RightBorder;    //Special basic  Actor
        engine::actor_handle                                        m_LeftBorder;     //Special basic  Actor
        engine::actor_handle                                        m_Background;     //Special basic  Actor
        engine::actor_handle                                        m_Ground;         //Special basic  Actor
        engine::tetris::t_actor_handle                              m_PlayerActor;    //Special tetris Actor (t_actor --> tetris_actor)
        mango::shared_ptr<graphic::GraphicController>               m_Graphic;        //Graphic controller which contains "swap_chain" buffers and draw capabilities
        engine::tetris::StoneFactory                                m_Factory;        //Creates stones (t_actors)
        engine::actor_list                                          m_Actors;         //All other Actors
        mango::delta_timer                                          m_Timer;          //Core game timer to specify update rate
        mango::delta_timer                                          m_SpeedTimer;     //Timer which indicates the increase game speed over time
        mango::delta_timer::seconds                                 m_UpdateInterval; //Interval witch controls the update rate --> higher update right faster game
        mango::delta_timer                                          m_SoundTimer;     //Timer which tracks the duration of the background sound
        mango::delta_timer::seconds                                 m_SoundDuration;  //duration of the background sound file
    };
}

main.cpp:

#include "engine.h"

mango::int32 main()
{
    mango::shared_ptr<engine::Engine> game(new engine::Engine);

    game->Play();

    mango::archive::mgetch(); //Wrapper for _getch()

    return 0;
}

And here is a picture of the running game:

Screenshot

As I already mentioned, feel free to reuse the code.

\$\endgroup\$
5
  • \$\begingroup\$ Which game engine you have used? \$\endgroup\$
    – Sumeet
    Commented Mar 23, 2017 at 9:25
  • \$\begingroup\$ my own one (based on gdiplus) \$\endgroup\$
    – Mango
    Commented Mar 23, 2017 at 9:26
  • \$\begingroup\$ Have you forwarded namespace std into namespace mango? Or are you using custom versions of shared_ptr, etc? \$\endgroup\$ Commented Mar 23, 2017 at 9:38
  • \$\begingroup\$ custom versions. some of them are just convenient, some of them are for performance reasons. \$\endgroup\$
    – Mango
    Commented Mar 23, 2017 at 9:44
  • 1
    \$\begingroup\$ I have rolled back the last edit. Please see what you may and may not do after receiving answers. \$\endgroup\$
    – Heslacher
    Commented Mar 23, 2017 at 10:38

2 Answers 2

5
\$\begingroup\$

Well, imho there are some C constructs in there, while you are trying to write C++.

For instance

typedef typename graphic::Character actor;

would be nicer like this:

using actor=graphic::Character;

But maybe it's just a coding style thing.

Then typedef typename shapes::shape shape; can be simplified to using shape::shape;.

Then for example void Rotate(mango::shared_ptr<engine::actor_list> segments, As you are already in the namespace engine, it would probably be sufficient to write mango::shared_ptr<actor_list>. And if you define using mango::shared_ptr; earlier, it can even be simplified to

void Rotate(shared_ptr<actor_list> segments

I would put the class RotationProcessor and StoneFactory into a separate files.

About files: don't write your implementation in the header file. The header file should only contain definitions. The c-file contains the implementations. (The only exception is for template classes and -functions.)

About templates: The RotateXX functions seem to have a lot in common. Is there a way you could extract the commonalities and write a single template function? Then you will have a lot less code to maintain.

Also about the RotateXX functions: currently these functions are implemented as member functions: you require an object to use them. However, they do not depend on the RotationProcessor object. So why not make them static. That way you could just call them using RotationProcessor::Rotate(...);.

I was wondering about this function:

mango::shared_ptr<engine::actor_list> GetSegments()
{
    mango::shared_ptr<engine::actor_list> tmp = m_Segments;
    m_Segments.reset();

    return tmp;
}

So ownership of the contents of m_Segments is transferred to the caller of GetSegments(). To me that sounds more like a unique_ptr then a shared_ptr.

Finally, try to remove all the magic numbers from your code. For example:

m_PlayerActor->Move(0, -10);

contains two magic numbers. Better is to replace the line by a(n inline) function call (C++ style) or at least a define (C-style).

There are more thing, but I'll leave that for the others ;)

edit: ok one more thing:

if ('q' == input)
{
    TryRotationLeft();
}
[...etc.etc...]
if ('X' == input)
{
    isRunning = false;
}

How about using a switch statement. E.g.:

switch (input) {
    case 'q': TryRotationLeft(); break;
    case 'e': TryRotationRight(); break;
    case 'a': TryMoveLeft(); break;
    case 'd': TryMoveRight(); break;
    case 's': TryMoveDown(); break;
    case 'X': isRunning = false; break;
}
\$\endgroup\$
3
  • \$\begingroup\$ well, where to start... first thank you for answering, I will consider use using and putting the rotate function in a template. \$\endgroup\$
    – Mango
    Commented Mar 23, 2017 at 10:44
  • \$\begingroup\$ the transferred ownership thing is true, but i simply haven't implemented an unique pointer and since I am stucking without c++11 i would have to use boost or implement one, neither of this is an option for me. And as I mentioned in my question, yes i am going to split the files ;) \$\endgroup\$
    – Mango
    Commented Mar 23, 2017 at 10:47
  • \$\begingroup\$ yep the switch case is a more than valid point, i will do so \$\endgroup\$
    – Mango
    Commented Mar 23, 2017 at 10:55
2
\$\begingroup\$

The only thing I would say is to use comment blocks, I usually have the standard pre-programmed into my IDE so that it auto-fills parts of it and puts a date and time stamp on each method/function as required:

/**
 * Removes white spaces from string
 * 
 * @param   string
 * @author  sbebbington
 * @date    23 Mar 2017 - 09:31:35
 * @version 0.0.1
 * @return  string
 * @todo
 */
public function trimString(string $string = ''){
    return trim($string);
}

This is for PHP, so have a look at the C++ standard for comment blocks.

I've found that using this format forces me to clearly and concisely say something useful about the methods in my PHP scripts. Each time I update a method, I then update the date/time and version as required so at least if I look back on something I know when I last edited it. This saves trawling through my GIT history to see when I changed something, and why.

\$\endgroup\$
1
  • \$\begingroup\$ I suppose it all depends on how your brain works, but being disciplined about comments has made me re-evaluate, refactor and clean many methods in the past on revisiting them, and has thus made me a better developer for it. \$\endgroup\$ Commented Mar 23, 2017 at 14:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.