#include "stdafx.h"

//==============================================================================================

//  team                        C<>DE_INSIDE.RND

//  file name                   textris_demo.cpp

//  completion level            2                              ( basic, moderate, complete )

//  author                      codesafer

//  date                        2017.06.28

/// since                       2017.06.28

//------------------------------+---------------------------------------------------------------


#include "tex_framework.h"


constexpr   int                 block_size  = 4;

const       TEX                 empty_tile{ C4::silver / C4::black, ' ' };


enum    class                   KEY : int

{

    space   = 32,

    left    = 75,

    right   = 77,

    up      = 72,

    down    = 80,

};


//------------------------------+---------------------------------------------------------------


struct                          TBLOCK

{

    using                       TYPE  = unsigned short;


    int                         index = 0;


    const   TYPE                bits_set[ 8 ]

    {

        0xFFFF, 0xA808, 0x840A, 0x8888, 0xC808, 0xA088, 0x8848, 0xA880,

    };


    TYPE                        bits = bits_set[ 0 ];


    const   TYPE                spin_geometry[ 16 ]

    {

        0x0001, 0x0002, 0x0004, 0x0010,

        0x4000, 0x0008, 0x0080, 0x0020,

        0x2000, 0x8000, 0x0800, 0x0040,

        0x1000, 0x0400, 0x0200, 0x0100,

    };


    auto&                       operator=( const TBLOCK& other )

    {

        bits    = other.bits;

        index   = other.index;

        return  *this;

    }


    auto&                       operator=( const int index )

    {

        bits = bits_set[ this->index = index ];

        return  *this;

    }


                                TBLOCK() = default;

                                TBLOCK( const int index ) : index( index )

    {

        *this   = index;                

    }

    

    auto                        rotate_cw()

    {

        bits    = ( bits << 4 ) & 0xFFF0 | ( bits >> 12 ) & 0x000F;

    }


    auto                        rotate_ccw()

    {

        bits    = ( bits >> 4 ) & 0x0FFF | ( bits << 12 ) & 0xF000;

    }


    template< class T >

    auto                        put( T& page, const int x, const int y, bool show = true )

    {

        const   TEX block_tile { C4( index ) / C4( index | 8 ), ' ' };

        const   TEX tile = show ? block_tile : empty_tile;

        for( int v = 0; v < block_size; ++v )

            for( int u = 0; u < block_size; ++u )

                if( bits & spin_geometry[ v * block_size + u ] )

                    page.put( ( x + u ) << 1, y + v, tile ),

                    page.put( ( ( x + u ) << 1 ) + 1, y + v, tile );

    }


    template< class T >

    auto                        remove( T& page, const int x, const int y )

    {

        put( page, x, y, false );

    }


};

//------------------------------+---------------------------------------------------------------


template< int x_size = 12 * 2, int y_size = 29 >

struct                          BOARD

{

    PAGE< x_size, y_size >      page;


    auto                        initialize()

    {

        const   TEX wall{ C4::silver / C4::gray,  ' ' };

        page.fill( empty_tile );

        page.vert_line( 0, 0, y_size - 1, wall );

        page.vert_line( 1, 0, y_size - 1, wall );

        page.vert_line( x_size - 2, 0, y_size - 1, wall );

        page.vert_line( x_size - 1, 0, y_size - 1, wall );

        page.horz_line( 0, x_size, y_size - 1, wall );

    }


    auto                        is_blocked( const int x, const int y, const TBLOCK block ) const

    {

        for( int v = 0; v < block_size; ++v )

            for( int u = 0; u < block_size; ++u )

                if( page.cells[ y + v ][ ( x + u ) << 1 ] != empty_tile &&

                    block.bits & block.spin_geometry[ v * block_size + u ] )

                    return  true;

        return  false;

    }


    CRE                         BOARD()

    {

        initialize();

    }

};

//------------------------------+---------------------------------------------------------------


#include <random>

#include <time.h>

class                           TEXTRIS

{

    SCREEN<>                    screen;

    BOARD<>                     board;

    PAGE< ( block_size + 2 ) * 2, block_size + 2 >

                                next_page;

    TBLOCK                      next_block;

    TBLOCK                      block;

    int                         x, y;

    int                         level;

    int                         score;

    int                         lines;


    std::mt19937                rng;

    std::uniform_int_distribution< int >

                                get_next;


    auto                        title()

    {

        screen.top_page.fill( empty_tile );


        const   C4  color_set1[] { C4::white, C4::silver, C4::gray };

        const   C4  color_set2[] { C4::red, C4::white, C4::white, C4::white };


        screen.waitfor_press( [ this, &color_set1, &color_set2 ]

        {

            static  int i = 0;

            screen.top_page.puts( 7, "TeXtris  1", color_set1, i++ / 100 );

            screen.top_page.puts( 11, "Codesafer's C++ guide", color_set2,

                                  sizeof color_set2 - i++ / 50 );

            screen.top_page.puts( 15, "Press enter to start" );

            screen.flush();

        } );

    }


    enum    class               JUDGE : int

    {

        gameover,

        blocked,

        changed = 1,

        movable,

    };


    auto                        draw_indicators()

    {

        screen.top_page.text_color = C4::lime / C4::black;

        screen.top_page.put_number( 5, 2, "SCORE : ", ++score );

        screen.top_page.put_number( 5, 4, "LEVEL : ", level );

        screen.top_page.put_number( 5, 6, "LINES : ", lines );

    }


    auto                        draw_next_block()

    {

        next_page.fill( empty_tile );

        next_page.frame_bold( 0, 0, next_page.xsize(), next_page.ysize(),

                              { C4::silver / C4::navy, ' ' } );

        TBLOCK( next_block ).put( next_page, 1, 1 );

        screen.top_page.text_color = C4::lime / C4::navy;

        screen.top_page.puts( 56, 0, " NEXT BLOCK " );

        screen.top_page.draw( 56, 1, next_page );

    }


    auto                        change_block()

    {

        block       = next_block;

        next_block  = get_next( rng );


        x = board.page.center_x( block_size << 1 ) >> 1;

        y = 0;

        if( board.is_blocked( x, y, block ) )

            return  JUDGE::gameover;


        draw_indicators();

        draw_next_block();


        return  JUDGE::changed;

    }


    auto                        collapse_line( const int y )

    {

        for( int x = 1; x < 11; ++x )

            board.page.cells[ y ][ x << 1 ] =

                board.page.cells[ y - 1 ][ x << 1 ],

            board.page.cells[ y ][ x << 1 | 1 ] =

                board.page.cells[ y - 1 ][ x << 1 | 1 ];

    }


    auto                        clear_line( const int y )

    {

        for( int u = 1; u < 11; ++u )

            board.page.cells[ y ][ u << 1 ] = empty_tile,

            board.page.cells[ y ][ u << 1 | 1 ] = empty_tile;

        lines++;

        level = lines / 30 + 1;

    }


    auto                        remove_line()

    {

        const   int v_end   = min( y + block_size, board.page.ysize() - 1 );

        for( int v = y, bonus = 0; v < v_end; ++v )

        {

            int count = 0;

            for( int u = 1; u < 11; ++u )

                count += board.page.cells[ v ][ u << 1 ] != empty_tile;

            if( count == 10 )

            {

                for( int i = v; i > 0; --i )

                    collapse_line( i );

                score += bonus += 10;

                clear_line( 0 );

                lines++;

            }

        }

    } 


    auto                        move_down()

    {

        block.remove( board.page, x, y );

        if( board.is_blocked( x, ++y, block ) )

        {

            block.put( board.page, x, y - 1 );

            remove_line();

            return  change_block();

        }

        return  JUDGE::movable;

    }


public:

    CRE                         TEXTRIS()

    :   rng( clock() )

    ,   get_next( 1, 7 )

    ,   level( 1 )

    ,   score( 0 )

    ,   lines( 0 )

    {

        next_block = get_next( rng );

    }


    auto                        process_key()

    {

        if( kbhit() )

        {

            KEY key = (KEY)getch();

            if( (int)key & 0x80 ) key = (KEY)getch();

            switch( key )

            {

            case KEY::left:

                block.remove( board.page, x, y );

                if( !board.is_blocked( x - 1, y, block ) )

                    x--;

                break;

            case KEY::right:

                block.remove( board.page, x, y );

                if( !board.is_blocked( x + 1, y, block ) )

                    x++;

                break;

            case KEY::up:

                block.remove( board.page, x, y );

                block.rotate_cw();

                if( board.is_blocked( x, y, block ) )

                    block.rotate_ccw();

                break;

            case KEY::down:

                move_down();

                break;

            case KEY::space:

                while( move_down() == JUDGE::movable );

                break;

            }

        }

    }


    auto                        run()

    {

        title();

        screen.fadein( 500 );

        change_block();

        while( move_down() != JUDGE::gameover )

        {

            const int waiting = 30 - level;

            for( int i = 0; i < waiting; ++i )

            {

                block.put( board.page, x, y );

                screen.top_page.draw(

                    ( screen.top_page.xsize() - board.page.xsize() ) >> 1,

                    -block_size, board.page );

                screen.flush();

                process_key();

                Sleep( 1 );

            }

        }

        screen.fadeout( 500 );

    }

};

//------------------------------+---------------------------------------------------------------


int main()

{

    TEXTRIS textris;

    textris.run();


    return  0;

}

//==============================================================================================


점심시간에 얼렁뚱땅 하는거라 리팩토링할 시간이 없다.

두칸 블럭으로 그리고 있어서 그려지는 좌표가 실제 좌표 x 의 2배인 점을 유의.


주석 적을 시간이 없으니 알아서들 보시게~