Advanced Placement Computer Science AB:
Assignment 20:  Classes Continued -- Othello
   Sewickley Academy, 2000-2001

See Course Home Page.
 
Date Assigned: Mon Nov-6
Date Due: Tue Nov-7

(This was the extra credit assignment for Assignment 19, but it is not extra credit here.)  Write another subclass called Othello, which should play othello. The rules to this game are very simple, and are available in many locations online (such as here).

Upshot:  place 2 white and 2 black pieces in the middle of the board to start (place them such that the white pieces are diagonal to each other (and, hence, the black pieces are also diagonal to each other)).  On each turn, players place one piece (if they have a move at all) such that they sandwich the opponent's pieces between the placed piece and another piece of the player's color without any intervening gaps.  All the sandwiched pieces are flipped, so if white, they become black, and vice versa.  A single placed piece could sandwich opponents pieces in up to 8 directions -- all the sandwiched pieces in all directions become those of the player.  Play ends when neither player has a legal move.  This is a very simple game to describe and to write in code.
Note 1:  The point of this is to further verify that your classes are set up properly.  Do not spend too much effort on the rules of Othello (though they are exceedingly simple in any case).


Note 2:  In class we briefly discussed writing functions which take other functions as arguments.  We will not cover that in this assignment, and will cover it instead tomorrow in class.  Meanwhile, if you want some extra credit, this ability is demonstrated by the following code (see the function apply()):
#include <stdio.h>
#include <iostream.h>

int square(int x)
{
 return x*x;
}

int cube(int x)
{
 return x*x*x;
}

int apply(int(*fn)(int x),int x)
{
 return fn(x);
}

void main()
{
 cout << "apply(square,5) = " << apply(square,5) << endl;
 cout << "apply(cube,5) = "   << apply(cube,5) << endl;
 getchar();
}

For difficult extra credit, add a static function to the TwoPlayerBoardGame class, registerTwoPlayerBoardGame(), which takes a name of a game and a function which itself creates an instance of the game.  Each game you write -- Checkers and Othello, at least -- would call this function exactly once (figure out how to do this, too).  Then, write another static function in TwoPlayerBoardGame, called playGame(), which presents the user with the list of available games and lets the user decide which game to play, then creates an instance of that game and plays it.

For what it's worth, we will do exactly this in class tomorrow in any case.



Note 3:  Here is another extra credit problem for you to consider.  It does not involve classes at all.  In the IntroCS class, several students and I developed the following (largely uncommented and not quite completed, yet functional) code that creates a random word search from a list of words which are stored in a file in c:\temp\words.txt (the words must be separated by exactly one space or one newline, with no extra whitespace at all).  The code is attached here.  Your task is to understand this code such that you could, under quiz conditions, recreate all or part of it.  Also, you could make interesting modifications to it, such as to make it find smaller solutions.  Good luck!
// main.cpp in WordSearch
// by DK, JP, CZ, and EM
// 1-Nov-00 (uh oh, Y21C bug)

#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <io.h>

////////////////////////////////////////////////////////////////////
//------------------------Globals, Constants, and Other Neat Things
////////////////////////////////////////////////////////////////////

const char* FILENAME = "c:\\temp\\words.txt";

const int MAX_ITERATIONS = 100;

const int MAX_STRINGS = 1000;
const int MAX_STRING_LENGTH = 30;

const int MAX_ROWS = 100;
const int MAX_COLS = 100;

const int DIRS = 8;

int g_rows;
int g_cols;

char g_strings[MAX_STRINGS][MAX_STRING_LENGTH];
int g_stringCount;

char g_board[MAX_ROWS][MAX_COLS];

////////////////////////////////////////////////////////////////////
//------------------------Begin Random Code-----------------------//
////////////////////////////////////////////////////////////////////

// returns a random between 0 and 1
double random1()
{
        static bool seeded = false;
        if (seeded == false)
        {
                srand(time(NULL)); // seed the random # generator
                seeded = true;
        }
        return double(rand()) / RAND_MAX;
}

double random(double lo, double hi)
{
        if (hi < lo)
        {
                double temp = hi;
                hi = lo;
                lo = temp;
        }
        double range = hi - lo;
        return lo + range * random1();
}

int random(int lo, int hi)
{
        int result;
        do
        {
                result = int(random(double(lo),double(hi+1)));
        }
        while (result == (hi+1));
        return result;
}
 

////////////////////////////////////////////////////////////////////
//------------------------Begin Fairly UnRandom Code--------------//
////////////////////////////////////////////////////////////////////

// @TODO:  This is debug code, it will go away when we write our file stuff
const int TEMP_SIZE = 6;
char* g_strings_temp[TEMP_SIZE] =
{"Four","Score","And","Seven","Years","Ago"};

void loadStrings()
{
        g_stringCount = 0;
        FILE *fp;

        fp = fopen(FILENAME,"r");
        if (fp == NULL)
        {
                cout << "Could not open file '" << FILENAME << "'.  Sorry." << endl;
                getchar();
                exit(1);
        }

        while (!feof(fp))
        {
                int i = 0;
                char c;
                while (c=getc(fp),((c != '\n') && (c != ' ') && (c != EOF)))
                        g_strings[g_stringCount][i++] = toupper(c);
                // add null terminator
                g_strings[g_stringCount][i] = NULL;
                ++g_stringCount;
        }
}

void fillBoard()
{
        // fill empty spaces on board with random characters
        int row, col, count = 0;
        for (row=0; row<g_rows; row++)
        for (col=0; col<g_cols; col++)
                if (g_board[row][col] == NULL)
                {
                        ++count;
                        g_board[row][col] = 'A' + random(0,25);
                        // g_board[row][col] = '.';
                }
        cout << "fillBoard() filled " << count << " spaces " << endl;
}

void printBoard()
{
        int row,col;
        cout << endl << "Here is the " << g_rows << " x "
                 << g_cols << " board:" << endl;
        for (row=0;row<g_rows;row++)
        {
                for (col=0;col<g_cols;col++)
                        cout << g_board[row][col] << " ";
                cout << endl;
        }
}

void printStrings()
{
        cout << "Here are the " << g_stringCount << " strings:" << endl;
        int i;
        for (i=0; i<g_stringCount;i++)
                cout << "   '" << g_strings[i] << "'" << endl;
}

// This returns the number of chars used when placing the string,
// and -1 if it is not at all placeable here.
int placeString(char* string, int row, int col, int dir, bool reallyPlace)
{
        int numChars = 0;
        unsigned int i;
        int rowSign, colSign;
        int thisDir = 0;

        // 1. set rowSign,colSign to (1,1), (1,0), (1,-1), etc,
        //    based on value of dir
        for (rowSign=-1;rowSign<=1;rowSign++)
        for (colSign=-1;colSign<=1;colSign++)
                if ((rowSign || colSign) && (thisDir++ == dir))
                        goto foundRowCol;

foundRowCol:
        // 2. Place string along this vector from row,col
        for (i=0;i<strlen(string);i++)
        {
                int row2 = row + i * rowSign;
                int col2 = col + i * colSign;
                // verify we're on the board
                if ((row2 < 0) || (row2 >= g_rows) ||
                        (col2 < 0) || (col2 >= g_cols))
                        // we ran off the board, so fail by returning -1
                        return -1;
                // verify target board position is either
                // empty or matches this character in the string
                bool positionEmpty = (g_board[row2][col2] == NULL);
                bool positionMatches = (g_board[row2][col2] == string[i]);

                if ((positionEmpty == false) && (positionMatches == false))
                        // position is occupied and it doesn't match,
                        // so we must fail and go home.
                        return -1;

                // Increment the number of characters if we do not
                // have a match
                if (positionMatches == false)
                        numChars++;

                // Place the character
                if (reallyPlace)
                        g_board[row2][col2] = string[i];
        }
        return numChars;
}

bool loadString(int i)
{
        char* string = g_strings[i];

        int col1 = random(0,g_cols-1);
        int row1 = random(0,g_rows-1);
        int dir1 = random(0,DIRS-1);

        bool foundOne = false;
        int bestCol, bestRow, bestDir, bestNumChars;

        int row, col, dir, rowDelta, colDelta, dirDelta;

        // Iterate over every row,col,dir to find best choice
        for (rowDelta=0; rowDelta<g_rows; rowDelta++)
        for (colDelta=0; colDelta<g_cols; colDelta++)
        for (dirDelta=0; dirDelta<DIRS;   dirDelta++)
        {
                row = (row1 + rowDelta) % g_rows;
                col = (col1 + colDelta) % g_cols;
                dir = (dir1 + dirDelta) % DIRS;

                int numChars = placeString(string,row,col,dir,false);
                if ((numChars >= 0) &&
                        ((foundOne == false) ||
                         (bestNumChars > numChars)))
                {
                        // We just found a better one than the one
                        // we have so far, if any
                        bestRow = row;
                        bestCol = col;
                        bestDir = dir;
                        bestNumChars = numChars;
                        foundOne = true;
                }
        }

        // Place it in the best location, if any
        if (foundOne)
        {
                placeString(string,bestRow,bestCol,bestDir,true);
                return true;
        }
        else
                return false;
}

bool loadBoard()
{
        // Repeatedly try to loadBoard until maxIterations
        int iteration;
        for (iteration=0; iteration<MAX_ITERATIONS; iteration++)
        {
                bool loadStringFailed = false;

                // first, load board with NULL character
                int row, col;
                for (row=0; row<g_rows; row++)
                for (col=0; col<g_cols; col++)
                        g_board[row][col] = (char) NULL;

                // load the board with the strings in g_strings[];
                int i = random(0,g_stringCount-1);
                int iDelta;
                for (iDelta=0; iDelta<g_stringCount; iDelta++)
                {
                        if (loadString((i + iDelta) % g_stringCount) == false)
                        {
                                loadStringFailed = true;
                                break;
                        }
                }

                // Woohoo!  We've done it!
                if (loadStringFailed == false)
                        return true;
        }
        // Urk.  No such luck over MAX_ITERATIONS attempts
        return false;
}
 

////////////////////////////////////////////////////////////////////
//------------------------Main()
////////////////////////////////////////////////////////////////////

void main()
{
        loadStrings();
        printStrings();
        g_rows = g_cols = 4;
        while (!loadBoard())
        {
                g_rows++;
                g_cols++;
        }
        fillBoard();
        printBoard();
        getchar();
}
 




See Course Home Page.