SDSU CS 596 OODP
Board Assignment

[To Lecture Notes Index]
San Diego State University -- This page last updated November 14, 1995
----------

Contents of Board Assignment Lecture

  1. Solutions and Issues on Board Class
  2. Solution 1 to Assignment
    1. Simple Board Class
  3. Solution 2 to Assignment
    1. Issues
      1. Storing and Accessing
        1. How to call SquareThing Constructors?
      2. What is a square thing?
        1. Inheritance vs. Composition
    2. ComplexBoard Class
  4. Which Solution is Better?
    1. SimpleBoard Responsibilities

Solutions and Issues on Board Class

The Traps

Don't imbed numbers in your program
Make each square 10 by 10


Programs do need some documentation
What is a label?


Avoid repeating code - look for common abstraction
Two functions required same knowledge
map pixel location to square on board

Solution 1 to Assignment


Board class does all the work

Simple Board Class

#ifndef	_SimpleBoard_HH
#define	_SimpleBoard_HH

enum Color { Dark = 0, Light = 1 };
const int Even = 0;

#include "Point.h"

class SimpleBoard {
	public:
		SimpleBoard (int SquaresPerSide = 8, 
					int PixelsPerSquare = 10, 
					Color FirstSquareColor = Light);

		Color ColorAt (const LPoint PixelLocation ) const;
		LPoint LabelAt (const LPoint PixelLocation ) const;
		
	private:
		int BoardSize;			// squares per row and column
		int SquareSize;			// Pixels per side of square

		Color	EvenSquareColor;	// Row Index + Column index 
		Color 	OddSquareColor;	// gives parity of square
		
		LPoint SquareIndexAt (const LPoint PixelLocation ) const;

};
#endif
#include "SimpleBoard.h"
#include <assert.h>

SimpleBoard :: SimpleBoard (int SquaresPerSide, 
						int PixelsPerSquare , 
						Color FirstSquareColor) {

	assert ( (SquaresPerSide % 2) == Even );
	 
	BoardSize = SquaresPerSide;
	SquareSize = PixelsPerSquare;
	EvenSquareColor = FirstSquareColor;
	
	if (FirstSquareColor == Dark)
		OddSquareColor = Light;
	else
		OddSquareColor = Dark;
}

LPoint SimpleBoard :: SquareIndexAt (const LPoint PixelLocation ) const{

	assert( (PixelLocation.x() >= 0) && (PixelLocation.y() >= 0) );
	assert( (PixelLocation.x() <=  BoardSize * SquareSize ) && 
			(PixelLocation.y() <=  BoardSize * SquareSize ) );
			
	return PixelLocation / SquareSize;
}
	

Color SimpleBoard :: ColorAt (const LPoint PixelLocation ) const {

	LPoint SquareIndex = SquareIndexAt( PixelLocation );
	
	int IndexSum = SquareIndex.x() + SquareIndex.y();
	int SquareParity = IndexSum % 2;
	
	if ( SquareParity == Even )
		return EvenSquareColor;
	else
		return OddSquareColor;
}

LPoint SimpleBoard :: LabelAt (const LPoint PixelLocation ) const {
	return SquareIndexAt( PixelLocation );
}
class LPoint {

	public:
		LPoint( int x = 0, int y = 0 );
		~LPoint() { cout << "destroy point\n";};

		friend ostream& operator << ( ostream& output, 
										LPoint print );
		void  print ( ostream& output ) const;

		int operator >= ( const LPoint&  a ) const;
		int operator <= ( const LPoint&  a ) const;

		float distance ( const LPoint& a ) const;
		LPoint operator + ( const LPoint& a ) const;
		LPoint operator - ( const LPoint& a ) const;
		LPoint operator / ( int scalar ) const;

		int x() const;
		int y() const;
		float radiusVector() const;

	private:
		int xCoordinate;
		int yCoordinate;
};

LPoint  LPoint ::  operator / ( int scalar ) const {
	return LPoint ( xCoordinate / scalar, yCoordinate / scalar);
}


Solution 2 to Assignment


Board class contains n*n square things, which do some work


Issues


How to store and access the n*n square things?

What is a square thing?


Storing and Accessing

Simple Array, Direct Access
class Board {
	public:
		Board( int Size ) 
			{ Squares = new SquareThing[Size* Size];
			  BoardSize = Size;};

	private:
		SquareThing* Squares;
		int BoardSize;
};



Access square in row R and column C by:


	(Squares[ R * BoardSize + C ]).color()


Simple Array, Indirect Access
class Board {
	public:
		Board( int Size ) 
			{ Squares = new SquareThing[Size* Size];
			   BoardSize = Size;};

	private:
		SquareThing* Squares;
		int BoardSize;

		SquareThing& squareAt( int Row, int Column ) {
			return Squares[  Row * BoardSize + Column  ];
			};

		SquareThing& operator [] ( int Row ) {
			return Squares + (Row * BoardSize);
			};
};

Access square in row R and column C by either:

	squareAt( R, C).color()


or
	(this->[ R ][ C ]).color();


Use Matrix
template <class Type>
class Matrix 
{	public :		
		Matrix(int NumOfRows, int NumOfColumns);
		~Matrix();
		Type* operator[](int whichRow);
		
		int NumberOfRows()	{ return ColumnSize; } const;
		int NumberOfColumns() { return RowSize; } const;
		
	private:
		Type* elements;
		int RowSize;
		int ColumnSize;
};

template <class Type>
Matrix<Type> :: Matrix(int NumOfRows, int NumOfColumns) {

	elements = new Type[ NumOfRows * NumOfColumns];
	RowSize = NumOfColumns;
	ColumnSize = NumOfRows;
};

template <class Type>
Type* Matrix<Type> :: operator[](int whichRow) {
	return elements + ( whichRow * RowSize);
};

template <class Type>
Matrix<Type>  :: ~Matrix() { 
	delete [] elements;
};

class Board {
	public:
		Board( int Size ) 

	private:
		Matrix<SquareThing> Squares;
};

Board :: Board( int Size ) : Squares( Size, Size ) {};



Access square in row R and column C by:


	Squares[ R ] [ C ]. color();

How to call SquareThing Constructors?

Answer - You cant

class Board {
	public:
		Board( int Size ) 

	private:
		Matrix< SquareThing > Squares;
};

Set values of data members
Board :: Board( int Size ) : Squares( Size, Size ) {

	for (int R = 0; R < Squares.NumberOfRows(); R++ )
		for (int C = 0; C < Squares.NumberOfColumns(); C++ )

			Squares[ R ][ C ].setDataMember( value );
};
Copy Objects
Board :: Board( int Size ) : Squares( Size, Size ) {

	for (int R = 0; R < Squares.NumberOfRows(); R++ )
		for (int C = 0; C < Squares.NumberOfColumns(); C++ )

			Squares[ R ][ C ] = SquareThing( values );
};

Use Pointers

class Board {
	public:
		Board( int Size ) 

	private:
		Matrix< SquareThing* > Squares;
};

Board :: Board( int Size ) : Squares( Size, Size ) {

	for (int R = 0; R < Squares.NumberOfRows(); R++ )
		for (int C = 0; C < Squares.NumberOfColumns(); C++ )

			Squares[ R ][ C ] = new SquareThing( values );
};

What is a square thing?


Square thing is square with color and may have a checker on it
class LRectangle {
	public:
		LRectangle ( const LPoint& UpperLeftCorner, 
				const LPoint& LowerLeftCorner ,
				Color interior);
		
		void print ( ostream& output ) const;
		void draw() const;
		
		LPoint getUpperLeftCorner() const;
		LPoint getUpperRightCorner() const;
		LPoint getLowerLeftCorner() const;
		LPoint getLowerRightCorner() const;
		LPoint* getCorners() const;
		
		int contains( const LPoint& a ) const;
		int intersects( const LRectangle a ) const;

		void setColor( Color NewColor);
		void setUpperLeftCorner( LPoint NewCorner );
		
	protected:
		LPoint	UpperLeft;
		int	width;
		int	height;
		Color	interiorColor;

		int containsCornerOf( const LRectangle a ) const;
};

Inheritance vs. Composition

Inheritance
class BoardSquare : public LRectangle {
public:
	BoardSquare( int Row, int Column,
				const LPoint& UpperLeftCorner, 
				const LPoint& LowerLeftCorner ,
				Color interior)
					: LRectangle(UpperLeftCorner, 
						LowerLeftCorner ,
						interior) {

		code to construct BoardSquare
	};

	void draw() const;

private:
	Checker* piece;

};
int BoardSquare :: draw( ) const {
	
	LRectangle :: draw();
	if ( piece != 0 )
		piece->draw();
}

Composition
class BoardSquare  {
public:
	BoardSquare( int Row, int Column,
				const LPoint& UpperLeftCorner, 
				const LPoint& LowerLeftCorner ,
				Color interior)
					: mySquare(UpperLeftCorner, 
						LowerLeftCorner ,
						interior) {

		code to construct BoardSquare
	};

	int contains( const LPoint& a ) const;
	void draw() const;

private:
	Checker* piece;
	LRectangle mySquare;
};

int BoardSquare :: contains( const LPoint& a ) const {
	return mySquare.contains( a );
};

int BoardSquare :: draw( ) const {
	
	mySquare.draw();
	if ( piece != 0 )
		piece->draw();
}

Inheritance vs. Composition
Inheritance

Less work - get operations for free

May inherit unneeded operations
void setColor( Color NewColor);
BoardSquare has no control over who sets color

Need to understand base and derive class

Composition

More work - must implement all require operations
int BoardSquare :: contains( const LPoint& a ) const {
return mySquare.contains( a );
};


Don't inherit unneeded operations

Base class has more code, but is easier to understand

More flexibility in changing implementation
What about board games with round squares?

Round Squares with Composition
class BoardSquare  {			// so the name is not very good
public:
	BoardSquare( int Row, int Column,
				const LPoint& UpperLeftCorner, 
				const LPoint& LowerLeftCorner ,
				Color interior)   {
		code to construct BoardSquare
	};

	int contains( const LPoint& a ) const;
	void draw() const;

private:
	Checker* piece;
	Shape* myShape;
};

class Shape {
	// stuff not shown
};

class Rectangle : public Shape{
	// stuff not shown
};

class Circle : public Shape{
	// stuff not shown
};


Round Squares with Inheritance
class BoardThing  {
public:
	BoardSquare( int Row, int Column,
				const LPoint& UpperLeftCorner, 
				const LPoint& LowerLeftCorner ,
				Color interior)   {
		code to construct BoardSquare
	};

private:
	Checker* piece;
};
class Shape {
	// stuff not shown
};

class Rectangle : public Shape{
	// stuff not shown
};

class Circle : public Shape{
	// stuff not shown
};

class BoardSquare : public BoardThing, public Rectangle {}

class BoardCircle : public BoardThing, public Circle {}

ComplexBoard Class


#ifndef	_ComplexBoard_HH
#define	_ComplexBoard_HH

#include "Point.h"
#include "BoardSquare.h"
#include "Matrix.h"
#include "BoardTypes.h"

class ComplexBoard {
	public:

		ComplexBoard (LPoint UpperLeftCorner,
					int SquaresPerSide = 8, 
					int PixelsPerSide = 80, 
					Color FirstSquareColor = Light);
		
		LPoint SquareIndexAt (const LPoint PixelLocation ) ;
		Color ColorOf (const LPoint RowColumn );
	
	private:
		Matrix< BoardSquare* >	TheBoard;
		
		LPoint	Offset;		// in pixels, is actual coordinate of 
							//	upperleft corner
};

#endif
#include "ComplexBoard.h"
#include <assert.h>

ComplexBoard :: ComplexBoard (LPoint UpperLeftCorner,
							int SquaresPerSide, 
							int PixelsPerSide , 
							Color FirstSquareColor) : 
			TheBoard( SquaresPerSide, SquaresPerSide){

	assert ( (SquaresPerSide % 2) == Even );
	
	Offset = UpperLeftCorner;
	
	int PixelsPerSquare = PixelsPerSide / SquaresPerSide;
	
	for ( int Row = 0; Row < SquaresPerSide; Row++ )
		for ( int Column = 0; Column < SquaresPerSide; Column++ )
			TheBoard[ Row ][ Column ] = 
				new BoardSquare( Row, Column, 
								PixelsPerSquare, 
								FirstSquareColor);
}

LPoint ComplexBoard :: SquareIndexAt (const LPoint PixelLocation ) {
	LPoint LocalPixelLocation = PixelLocation - Offset;
	
	for ( int Row = 0; Row < TheBoard.NumberOfRows(); Row++ )
		for ( int C = 0; C < TheBoard.NumberOfColumns(); C++ )
			if ( TheBoard[ Row ][ C ] -> 
							contains( LocalPixelLocation ) )
				return LPoint( Column, Row );
				
	assert( 1 == 0);
	return LPoint(0 , 0 );
}
Color ComplexBoard :: ColorOf (const LPoint PixelLocation )  {

	LPoint SquareIndex = SquareIndexAt( PixelLocation );
	return TheBoard[ SquareIndex.y()][ SquareIndex.x() ] -> color();
}


Which Solution is Better?




SimpleBoard Responsibilities


Translate between the physical (screen) and logical representation of the board
What about pieces - they need this service

Draw the board

const int Even = 0;
enum Color { Dark = 0, Light = 1 };

#include "Point.h"

class SimpleBoard {
	public:

		SimpleBoard (LPoint UpperLeftCorner,
					int SquaresPerSide = 8, 
					int PixelsPerSide = 80, 
					Color FirstSquareColor = Light);

		LPoint SquareIndexAt (const LPoint PixelLocation ) const;
		LPoint CenterOfSquare (const LPoint RowColumn ) const;
		int SizeOfSquare() { return SquareSize; } const;
		void draw() const;
		
	private:
		int BoardSize;			// squares per row and column
		int SquareSize;			// Pixels per side of square
		
		Color	EvenSquareColor;	
		Color 	OddSquareColor;

		LPoint	Offset;		
		
		void drawSquare( const LPoint RowColumn ) const;
		Color ColorOf (const LPoint RowColumn ) const;
		LPoint SquareOriginOf (const LPoint RowColumn ) const;
};

#include "SimpleBoard.h"
#include <assert.h>

SimpleBoard :: SimpleBoard (LPoint UpperLeftCorner,
							int SquaresPerSide, 
							int PixelsPerSide , 
							Color FirstSquareColor) {

	assert ( (SquaresPerSide % 2) == Even );
	 
	BoardSize = SquaresPerSide;
	SquareSize = PixelsPerSide / SquaresPerSide;
	EvenSquareColor = FirstSquareColor;
	Offset = UpperLeftCorner;
	
	if (FirstSquareColor == Dark)
		OddSquareColor = Light;
	else
		OddSquareColor = Dark;
}

LPoint SimpleBoard :: SquareIndexAt (const LPoint PixelLocation ) const{

	LPoint LocalPixelLocation = PixelLocation - Offset;
	
	assert( (LocalPixelLocation.x() >= 0) && 
			(LocalPixelLocation.y() >= 0) );
	assert( (LocalPixelLocation.x() <=  BoardSize * SquareSize ) 
		&& (LocalPixelLocation.y() <=  BoardSize * SquareSize ) 		);
			
	return LocalPixelLocation / SquareSize;
}


Color SimpleBoard :: ColorOf (const LPoint RowColumn ) const {

	LPoint SquareIndex = RowColumn;
	
	int IndexSum = SquareIndex.x() + SquareIndex.y();
	int SquareParity = IndexSum % 2;
	
	if ( SquareParity == Even )
		return EvenSquareColor;
	else
		return OddSquareColor;
}

LPoint SimpleBoard :: 
SquareOriginOf (const LPoint RowColumn ) const {
	int LocalX = RowColumn.x() * SquareSize;
	int LocalY = RowColumn.y() * SquareSize;

	LPoint SquareOriginLocalCoordinates( LocalX, LocalY );
	return SquareOriginLocalCoordinates + Offset;
}

LPoint SimpleBoard :: 
CenterOfSquare (const LPoint RowColumn ) const {

	return SquareOriginOf( RowColumn ) +
			 LPoint( SquareSize/2 , SquareSize/2);
}

void  SimpleBoard :: drawSquare( const LPoint RowColumn ) const {

	LPoint SquareOriginGlobal = SquareOriginOf( RowColumn );

	fl_rectf( SquareOriginGlobal.x(), SquareOriginGlobal.y(), 
			SquareSize , SquareSize, ColorOf(RowColumn)  );
}

void SimpleBoard :: draw() const{
	
	for (int Row = 0; Row < BoardSize; Row++)
		for (int Column = 0; Column < BoardSize; Column++)
				drawSquare( LPoint(  Column, Row ));
}

How Do Pieces Know When SimpleBoard Changes Size?
They Don't
class CheckerPiece {
	public:
		void draw();

		static Board* MyBoard;

	private:

		int RowLocation;
		int ColumnLocation;
		Color MyColor;
}


void CheckerPiece :: draw() {
	
	int Center = MyBoard->CenterOfSquare( 
					LPoint (RowLocation, ColumnLocation) );

	int Radius = MyBoard->SizeOfSquare() - 8;

	fl_circ( Center.x(), Center.y(), Radius , MyColor );
}


What Happens when there are 2 games?

This can be fixed.

How Do Peices Know When ComplexBoard Changes Size?

void ComplexBoard :: ChangeSize (int NewSize ) {
	
	for ( int R = 0; R < TheBoard.NumberOfRows(); Row++ )
		for ( int C = 0; C < TheBoard.NumberOfColumns(); C++ )
			if ( TheBoard[ R ][ C ] -> 
					ChangeSize ( NewSize ) )
}

----------