// file: point.h -- by BOGDAN BUTNARU - CHECKERS #ifndef _CHECKERS_H_ #define _CHECKERS_H_ #define ATTACK_COLOR 2 // color of one of the player's pieces #define DEFENSE_COLOR 3 // color of other player's pieces #define BACKGROUND_COLOR 4 #define ODD_PLATE_COLOR 5 // odd plate # color #define EVEN_PLATE_COLOR 6 #define HIGHLIGHT_COLOR 7 // color used to highlight pieces #define ARRAY_SIZE 32 // only keep record of even plates since // pieces will only move on plates with even #'s #define BOARD_SIDE 800 // board = 800 x 800 pixels #define SQUARES 8 // 8 squares on a side (total 8x8) #define ONE_PLATE 100 // one plate = 100 x 100 pixels #define TWO_PLATES 200 #define THREE_PLATES 300 #define PIECE_SIDE 80 // piece = 80 x 80 pixels #define NONE -99 // flag #define MAX_BUTTON_COUNT 3 // the mouse has 3 buttons typedef enum logical {FALSE, TRUE}; typedef struct { int x; int y; } point; typedef struct { point bottomLeft; point topRight; } rectangle; /* // just for reference typedef struct { point position; enum {UP, DOWN} buttonChord[MAX_BUTTON_COUNT]; int buttonOfMostRecentTransition; } locatorMeasure; */ typedef struct { point pos; // bottom left corner of plate int color; // color # of piece situated on plate at that time } plateType; class Checkers { public: void ResetGameBoard(); void ExamineAction( point ); Checkers() // constructor { SRGP_loadCommonColor(BACKGROUND_COLOR, "DarkGrey"); SRGP_loadCommonColor(ODD_PLATE_COLOR, "DarkOliveGreen1"); SRGP_loadCommonColor(EVEN_PLATE_COLOR, "ivory"); SRGP_loadCommonColor(ATTACK_COLOR, "red1"); SRGP_loadCommonColor(DEFENSE_COLOR, "Indigo"); SRGP_loadCommonColor(HIGHLIGHT_COLOR, "gold1"); Plate = new plateType [ARRAY_SIZE]; // initialization // ID of plate that is already selected when we click SelPlateID = NONE; // ID of plate where we want to move the selected plate MoveToID = NONE; // ID of plate that is being jumped by the moving piece (if Jump) JumpedPlateID = NONE; Origin = SRGP_defPoint(0, 0); // constant Origin point at (0, 0) Even_Color = EVEN_PLATE_COLOR; Odd_Color = ODD_PLATE_COLOR; Def_Color = DEFENSE_COLOR; Att_Color = ATTACK_COLOR; // ID of canvas of a piece of the specified color Def_Canvas = CreatePieceCanvas( Def_Color ); Att_Canvas = CreatePieceCanvas( Att_Color ); // ID of canvas of a highlighted piece of the specified color High_Def_Canvas = CreateHighlightCanvas( Def_Color ); High_Att_Canvas = CreateHighlightCanvas( Att_Color ); // constant rectangle of size ONE_PLATE x ONE_PLATE OneSquare = SRGP_defRectangle(0, 0, ONE_PLATE - 1, ONE_PLATE - 1); // repaint plates and pieces for a new game ResetGameBoard(); } // end constructor ~Checkers() // destructor { delete [] Plate; } // free up the space taken by 'Plate' private: plateType *Plate; int SelPlateID; // = NONE if no piece (or plate) is selected int MoveToID; // ID of destination plate when move or jump int JumpedPlateID; // ID of plate being jumped (piece is deleted after) int Even_Color; int Odd_Color; int Def_Color; int Att_Color; point Origin; // constant point of origin at (0, 0) point MaskOrigin; // bottom left point of Mask Rectangle rectangle MaskRect; // rect. used to copy from the SCREEN to // the Mask Canvas and vice versa int Mask_Canvas; // holds mask while moving pieces (animation) int Att_Canvas; // holds a piece of 'Attack' color int Def_Canvas; int High_Att_Canvas; // holds a highlighted piece of 'Attack' color int High_Def_Canvas; rectangle OneSquare; // constant rect. of size ONE_PLATE x ONE_PLATE // functions int CorrelateGameBoard( point ); // returns ID of plate selected void DrawBoardPlates( int ); // param. specifies color of even # plates int ResetPlateAttrib( int, int, int ); // first argument is plate ID // second and third are the x and y coordinates of its new position // returns color of the plate based on the plate ID # (even or odd) void DrawPieces( int ); // receives color ID of 'Defense' player void UpdatePlateColor( int, int ); // plate ID and color, respectively // receives color for canvas piece, returns ID of canvas created int CreatePieceCanvas( int ); int CreateHighlightCanvas( int ); // receives point of origin of mask rectangle and the number of sides (int k) void CreateMaskCanvas( point, int ); // checks for illegal moves (but if legal executes move or jump) logical CheckMoveRequest(); // receives X displacement and Y displacement and // calculates dx & dy to be used when moving the piece void DefineMoveDirection( int, int ); // moves object from point (1) to the point (2) by // 'dx' units on the horizontal axis and by 'dy' units on the vertical axis void PerformMove( point, point, int, int ); // updates ID's and colors of plates after a move (or jump) is performed void UpdatePlatesAfterMove( logical ); }; // end class Checkers() //------------------------------------------------------------------------- void Checkers::ResetGameBoard() { SelPlateID = NONE; MoveToID = NONE; JumpedPlateID = NONE; DrawBoardPlates( Even_Color ); DrawPieces( Def_Color ); } // end ResetGameBoard() //========================================================================= void Checkers::ExamineAction( point pt ) { int CurrentID = CorrelateGameBoard( pt ); int CanvasID; // do something only if an even plate # was selected if ( fmod(CurrentID, 2.0) == 0.0 ) // if even plate # { if ( SelPlateID != NONE && Plate[CurrentID/2].color == NONE ) { // if someting selected and current plate has NO piece on top MoveToID = CurrentID; CheckMoveRequest( ); } else if ( SelPlateID == NONE && Plate[CurrentID/2].color != NONE ) { // if nothing selected and current plate HAS piece on top // highlight current plate // select canvas if ( Plate[CurrentID/2].color == Def_Color ) CanvasID = High_Def_Canvas; else CanvasID = High_Att_Canvas; SRGP_copyPixel( CanvasID, OneSquare, Plate[CurrentID/2].pos ); // update SelPlateID SelPlateID = CurrentID; } else if ( SelPlateID != NONE && Plate[CurrentID/2].color != NONE ) { // if something selected and current plate HAS piece on top if ( CurrentID == SelPlateID ) { // if same plate // deselect the selected piece // select canvas if ( Plate[SelPlateID/2].color == Def_Color ) CanvasID = Def_Canvas; else CanvasID = Att_Canvas; SRGP_copyPixel( CanvasID, OneSquare, Plate[SelPlateID/2].pos); // update SelPlateID SelPlateID = NONE; } else { // deselect the selected plate // select canvas if ( Plate[SelPlateID/2].color == Def_Color ) CanvasID = Def_Canvas; else CanvasID = Att_Canvas; SRGP_copyPixel( CanvasID, OneSquare, Plate[SelPlateID/2].pos ); // highlight current plate // select canvas if ( Plate[CurrentID/2].color == Def_Color ) CanvasID = High_Def_Canvas; else CanvasID = High_Att_Canvas; SRGP_copyPixel( CanvasID, OneSquare, Plate[CurrentID/2].pos ); // update SelPlateID SelPlateID = CurrentID; } // end else } // end else } // end if } // end ExamineAction() //======================================================================== int Checkers::CorrelateGameBoard( point pt ) { int tempX = pt.x / ONE_PLATE; int tempY = pt.y / ONE_PLATE; int ID = NONE; if ( fmod(tempY, 2.0) != 0.0 ) // if odd row # ID = ( SQUARES * tempY ) - tempX + (SQUARES - 1); else // if even row # ID = ( SQUARES * tempY ) + tempX; return ID; } // end CorrelateGameBoard() //========================================================================= logical Checkers::CheckMoveRequest() { int xDisp = Plate[MoveToID/2].pos.x - Plate[SelPlateID/2].pos.x; int yDisp = Plate[MoveToID/2].pos.y - Plate[SelPlateID/2].pos.y; // checking for correct distance to move // Displacement = 0 = NO MOVE, Displacement > TWO_PLATES = too long of a move if ( xDisp != 0 && yDisp != 0 && abs(xDisp) <= TWO_PLATES && abs(yDisp) <= TWO_PLATES ) { // if above is true then check for move in opposite direction if ( Plate[SelPlateID/2].color == Att_Color && yDisp > 0 || Plate[SelPlateID/2].color == Def_Color && yDisp < 0 ) { return FALSE; // backwards movement not allowed } // end if // if it's a jump then check if it's a legal jump by taking in account // the surrounding pieces and their colors if ( abs(yDisp) == TWO_PLATES) { point JumpedPlateCoord; // use midpoint formula to find its exact coordinates JumpedPlateCoord.x = ( Plate[MoveToID/2].pos.x + Plate[SelPlateID/2].pos.x ) / 2; JumpedPlateCoord.y = ( Plate[MoveToID/2].pos.y + Plate[SelPlateID/2].pos.y ) / 2; JumpedPlateID = CorrelateGameBoard( JumpedPlateCoord ); // then if plate jumped is empty or of same color as // the selected plate color, that's an illegal jump if ( Plate[JumpedPlateID/2].color == NONE || Plate[JumpedPlateID/2].color == Plate[SelPlateID/2].color ) { return FALSE; } // end if } // end if // once here it's a SURE MOVE DefineMoveDirection( xDisp, yDisp ); // and also move return TRUE; } // end if else return FALSE; } // end CheckMoveRequest() //========================================================================= void Checkers::DrawBoardPlates( int A_COLOR ) { Even_Color = A_COLOR; if ( Even_Color == ODD_PLATE_COLOR ) Odd_Color = EVEN_PLATE_COLOR; const int Limit = BOARD_SIDE - ONE_PLATE; int Temp = ONE_PLATE * 2; int Plate_Color = NONE; int ID = 0; int xi; int yi; for ( yi = 0; yi <= Limit; yi += ONE_PLATE ) { if ( fmod(yi, Temp) == 0.0 ) // if even row # { for ( xi = 0; xi <= Limit; xi += ONE_PLATE ) { Plate_Color = ResetPlateAttrib( ID, xi, yi ); SRGP_setColor( Plate_Color ); SRGP_fillRectangleCoord( xi, yi, xi + ONE_PLATE - 1, yi + ONE_PLATE - 1 ); ++ID; } // end for xi } // end if else // if odd row # { for ( xi = Limit; xi >= 0; xi -= ONE_PLATE ) { Plate_Color = ResetPlateAttrib( ID, xi, yi ); SRGP_setColor( Plate_Color ); SRGP_fillRectangleCoord( xi, yi, xi + ONE_PLATE - 1, yi + ONE_PLATE - 1 ); ++ID; } // end for xi } // end else } // end for yi } // end DrawBoardPlates() //========================================================================= int Checkers::ResetPlateAttrib( int ID, int xi, int yi) { int Index = ID / 2; if ( fmod(ID, 2.0) == 0.0 ) // if even plate # { Plate[Index].pos.x = xi; Plate[Index].pos.x = yi; Plate[Index].color = NONE; return Even_Color; } // end if else return Odd_Color; } // end ResetPlateAttrib() //========================================================================= void Checkers::DrawPieces( int A_COLOR ) { Def_Color = A_COLOR; if ( Def_Color == ATTACK_COLOR ) Att_Color = DEFENSE_COLOR; // swap colors so they are not the same for ( int ID = 0; ID <= 22; ID += 2 ) // Draw bottom pieces { SRGP_copyPixel( Def_Canvas, OneSquare, Plate[ID/2].pos ); UpdatePlateColor( ID, Def_Color ); } // end for for ( ID = 40; ID <= 62; ID += 2 ) { SRGP_copyPixel( Att_Canvas, OneSquare, Plate[ID/2].pos ); UpdatePlateColor( ID, Att_Color ); } // end for } // end DrawPieces() //========================================================================= void Checkers::UpdatePlateColor( int ID, int A_COLOR ) { int Index = ID / 2; Plate[Index].color = A_COLOR; } // end UpdatePlateColor() //========================================================================= int Checkers::CreatePieceCanvas( int A_COLOR ) { int Saved_Canvas = SRGP_inquireActiveCanvas(); // save ID of current canvas int CanvasID = SRGP_createCanvas( ONE_PLATE, ONE_PLATE ); SRGP_useCanvas( CanvasID ); SRGP_setColor( Even_Color ); SRGP_fillRectangle( OneSquare ); rectangle PieceRect = SRGP_defRectangle( (ONE_PLATE - PIECE_SIDE) / 2, (ONE_PLATE - PIECE_SIDE) / 2, ((ONE_PLATE - PIECE_SIDE) / 2) + PIECE_SIDE - 1, ((ONE_PLATE - PIECE_SIDE) / 2) + PIECE_SIDE - 1 ); SRGP_setColor( A_COLOR ); SRGP_fillEllipse( PieceRect ); SRGP_useCanvas( Saved_Canvas ); return CanvasID; } // end CreatePieceCanvas() //========================================================================= int Checkers::CreateHighlightCanvas( int A_COLOR ) { int Saved_Canvas = SRGP_inquireActiveCanvas(); // save ID of current canvas int CanvasID = SRGP_createCanvas( ONE_PLATE, ONE_PLATE ); SRGP_useCanvas( CanvasID ); rectangle PieceRect = SRGP_defRectangle( (ONE_PLATE - PIECE_SIDE) / 2, (ONE_PLATE - PIECE_SIDE) / 2, ((ONE_PLATE - PIECE_SIDE) / 2) + PIECE_SIDE - 1, ((ONE_PLATE - PIECE_SIDE) / 2) + PIECE_SIDE - 1 ); SRGP_setColor( A_COLOR ); SRGP_fillEllipse( PieceRect ); SRGP_setColor( HIGHLIGHT_COLOR ); SRGP_ellipse( PieceRect ); SRGP_useCanvas( Saved_Canvas ); return CanvasID; } // end CreateHighlightCanvas() //========================================================================= void Checkers::CreateMaskCanvas( point PointOrigin, int k) // k = # of sides { SRGP_setColor( Even_Color ); SRGP_fillRectangleCoord( Plate[SelPlateID/2].pos.x, Plate[SelPlateID/2].pos.y, Plate[SelPlateID/2].pos.x + ONE_PLATE - 1, Plate[SelPlateID/2].pos.y + ONE_PLATE - 1 ); int Saved_Canvas = SRGP_inquireActiveCanvas(); // save ID of current canvas MaskRect = SRGP_defRectangle( PointOrigin.x, PointOrigin.y, PointOrigin.x + (k * ONE_PLATE - 1, PointOrigin.y + (k * ONE_PLATE - 1 ); MaskCanvas = SRGP_createCanvas( k * ONE_PLATE, k * ONE_PLATE ); SRGP_useCanvas( MaskCanvas ); // copy region from screen to mask canvas SRGP_copyPixel( SCREEN_CANVAS, MaskRect, Origin ); // reset rectangle coordinates to the ones of the newly created canvas // so we can copy from canvas to screen (intermitently with the moving piece) MaskRect = SRGP_inquireCanvasExtent( MaskCanvas ); SRGP_useCanvas( Saved_Canvas ); // make previous canvas active again } // end CreateMaskCanvas() //========================================================================= void Checkers::DefineMoveDirection( int xDisp, int yDisp ) { int dx = 1; int dy = 1; int k = 2; // speed factor (must be divisor of TWO_PLATES) point Start = Plate[SelPlateID/2].pos; point End = Plate[MoveToID/2].pos; if ( xDisp > 0 && yDisp > 0 ) { dx = 1; dy = 1; MaskOrigin = Start; } else if ( xDisp > 0 && yDisp < 0 ) { dx = 1; dy = -1; MaskOrigin.x = Start.x; MaskOrigin.y = Start.y - ONE_PLATE; } else if ( xDisp < 0 && yDisp > 0 ) { dx = -1; dy = 1; MaskOrigin.x = Start.x - ONE_PLATE; MaskOrigin.y = Start.y; } else if ( xDisp < 0 && yDisp < 0 ) { dx = -1; dy = -1; MaskOrigin = End; } if ( abs(xDisp) == TWO_PLATES ) // if jump { if ( xDisp < 0 && yDisp > 0 ) { MaskOrigin.x = Start.x - TWO_PLATES; MaskOrigin.y = Start.y; } else if ( xDisp > 0 && yDisp < 0 ) { MaskOrigin.x = Start.x; MaskOrigin.y + Start.y - TWO_PLATES; } // end else CreateMaskCanvas( MaskOrigin, 3 ); dx = dx * k; // increase speed x-direction dy = dy * k; // increase speed y-direction PerformMove( Start, End, dx, dy, 2 ); UpdatePlatesAfterMove( TRUE ); // TRUE is for Jump } else // else if normal move ( no jump ) { CreateMaskCanvas( MaskOrigin, 2 ); PerformMove( Start, End, dx, dy, 1 ); UpdatePlatesAfterMove( FALSE ); // FALSE is for NO Jump } // end else SRGP_deleteCanvas( MaskCanvas ); } // end DefineMoveDirection() //========================================================================= void Checkers::PerformMove( point Start, point End, int dx, int dy ) { // choose highlaighted canvas to use (defense or attack) int CanvasID = High_Att_Canvas; if ( Plate[SelPlateID/2].color == Def_Color ) CanvasID = High_Def_Canvas; while ( Start.x != End.x || Start.y != End.y ) { SRGP_copyPixel( CanvasID, OneSquare, Start ); // paint above alternately with the mask canvas below SRGP_copyPixel( Mask_Canvas, MaskRect, MaskOrigin ); Start.x += dx; // update point along the path Start.y += dy; } // end while // make moved piece visible at its destination SRGP_copyPixel( CanvasID, OneSquare, End ); } // end PerformMove() //========================================================================= void Checkers::UpdatePlatesAfterMove( logical Jump ) { if ( Jump ) { SRGP_setColor( Even_Color ); SRGP_fillRectangleCoord( Plate[JumpedPlateID/2].pos.x, Plate[JumpedPlateID/2].pos.y, Plate[JumpedPlateID/2].pos.x + ONE_PLATE - 1, Plate[JumpedPlateID/2].pos.y + ONE_PLATE - 1 ); UpdatePlateColor( JumpedPlateID, NONE ); JumpedPlateID = NONE; } // end if UpdatePlateColor( MoveToID, Plate[SelPlateID/2].color ); UpdatePlateColor( SelPlateID, NONE ); SelPlateID = MoveToID; MoveToID = NONE; } // end UpdatePlatesAfterMove() //========================================================================= #endif