// tetromino.h #pragma once #ifndef ROTAERK__TETRIS__TETROMINO_H #define ROTAERK__TETRIS__TETROMINO_H #include namespace rotaerk { namespace tetris { // A tetromino is a set of 4 blocks chained together into a shape fit into a grid. // These blocks occupy cells in the grid, and one of those cells is the tetromino's // center, or origin. Thus all the blocks are specified in terms of the offsets of // their cells with respect to the origin. The offsets of all four blocks are // together called the tetromino's block configuration. // A tetromino can be pseudo-rotated, in that it is not true rotation around an axis, // but rather a cycle through a set of block configurations. These block // configurations are the orientations across which the tetromino rotates. struct tetromino { enum shape { J_SHAPE , L_SHAPE , T_SHAPE , I_SHAPE , S_SHAPE , Z_SHAPE , O_SHAPE , FIRST_SHAPE = J_SHAPE , LAST_SHAPE = O_SHAPE }; // The offset of a cell relative to the origin. // +X is to the right, and +Y is upwards. struct cell_offset { signed char X, Y; }; // The offsets of the tetromino's 4 blocks. typedef boost::array< cell_offset, 4 > block_configuration; tetromino( shape Shape ); shape get_shape() const; block_configuration const * get_block_configuration() const; void rotate_to_next_orientation(); void rotate_to_prev_orientation(); private: struct shape_data { unsigned int NumOrientations; block_configuration const * Orientations; }; static shape_data get_shape_data( shape Shape ); shape Shape; shape_data ShapeData; unsigned int OrientationIndex; }; } } #endif // tetromino.cpp #include "tetromino.h" #include #include namespace rotaerk { namespace tetris { namespace { // The orientations through which the tetromino rotates. template< unsigned int NumOrientations > struct orientation_cycle { boost::array< tetromino::block_configuration, NumOrientations > Orientations; }; } tetromino::tetromino( shape Shape ) : Shape( Shape ) , ShapeData( get_shape_data( Shape ) ) , OrientationIndex( 0 ) { } tetromino::shape tetromino::get_shape() const { return Shape; } tetromino::block_configuration const * tetromino::get_block_configuration() const { assert( ShapeData.Orientations != NULL ); assert( OrientationIndex < ShapeData.NumOrientations ); return ShapeData.Orientations + OrientationIndex; } void tetromino::rotate_to_next_orientation() { std::size_t NextOrientationIndex = ( OrientationIndex + 1 ) % ShapeData.NumOrientations; assert( NextOrientationIndex < ShapeData.NumOrientations ); OrientationIndex = NextOrientationIndex; } void tetromino::rotate_to_prev_orientation() { std::size_t NextOrientationIndex = ( OrientationIndex + ShapeData.NumOrientations - 1 ) % ShapeData.NumOrientations; assert( NextOrientationIndex < ShapeData.NumOrientations ); OrientationIndex = NextOrientationIndex; } tetromino::shape_data tetromino::get_shape_data( shape Shape ) { static orientation_cycle< 4 > const JOrientationCycle = { {{ {{ { 1, -1 }, { 1, 0 }, { 0, 0 }, { -1, 0 } }}, {{ { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }}, {{ { -1, 1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } }}, {{ { 1, 1 }, { 0, 1 }, { 0, 0 }, { 0, -1 } }} }} }; static orientation_cycle< 4 > const LOrientationCycle = { {{ {{ { -1, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } }}, {{ { -1, 1 }, { 0, 1 }, { 0, 0 }, { 0, -1 } }}, {{ { 1, 1 }, { 1, 0 }, { 0, 0 }, { -1, 0 } }}, {{ { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }} }} }; static orientation_cycle< 4 > const TOrientationCycle = { {{ {{ { 0, -1 }, { -1, 0 }, { 0, 0 }, { 1, 0 } }}, {{ { -1, 0 }, { 0, 1 }, { 0, 0 }, { 0, -1 } }}, {{ { 0, 1 }, { 1, 0 }, { 0, 0 }, { -1, 0 } }}, {{ { 1, 0 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }} }} }; static orientation_cycle< 2 > const IOrientationCycle = { {{ {{ { -1, 0 }, { 0, 0 }, { 1, 0 }, { 2, 0 } }}, {{ { 0, 1 }, { 0, 0 }, { 0, -1 }, { 0, -2 } }} }} }; static orientation_cycle< 2 > const SOrientationCycle = { {{ {{ { 1, 0 }, { 0, 0 }, { 0, -1 }, { -1, -1 } }}, {{ { 0, 1 }, { 0, 0 }, { 1, 0 }, { 1, -1 } }} }} }; static orientation_cycle< 2 > const ZOrientationCycle = { {{ {{ { -1, 0 }, { 0, 0 }, { 0, -1 }, { 1, -1 } }}, {{ { 0, 1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } }} }} }; static orientation_cycle< 1 > const OOrientationCycle = { {{ {{ { 0, 0 }, { 1, 0 }, { 1, -1 }, { 0, -1 } }} }} }; std::size_t OrientationsSize = 0; block_configuration const * Orientations = NULL; switch( Shape ) { case J_SHAPE: OrientationsSize = JOrientationCycle.Orientations.size(); Orientations = JOrientationCycle.Orientations.data(); break; case L_SHAPE: OrientationsSize = LOrientationCycle.Orientations.size(); Orientations = LOrientationCycle.Orientations.data(); break; case T_SHAPE: OrientationsSize = TOrientationCycle.Orientations.size(); Orientations = TOrientationCycle.Orientations.data(); break; case I_SHAPE: OrientationsSize = IOrientationCycle.Orientations.size(); Orientations = IOrientationCycle.Orientations.data(); break; case S_SHAPE: OrientationsSize = SOrientationCycle.Orientations.size(); Orientations = SOrientationCycle.Orientations.data(); break; case Z_SHAPE: OrientationsSize = ZOrientationCycle.Orientations.size(); Orientations = ZOrientationCycle.Orientations.data(); break; case O_SHAPE: OrientationsSize = OOrientationCycle.Orientations.size(); Orientations = OOrientationCycle.Orientations.data(); break; default: assert( ( "Unhandled tetromino shape.", false ) ); } shape_data ShapeData = { static_cast< unsigned int >( OrientationsSize ), Orientations }; return ShapeData; } } }