views:

255

answers:

4

Hello! I had one of my friends send me this programming assignment so I could brush up on some of my C++ skills. Below is the program description and my proposed algorithm. Could someone please provide some feedback/alternative solutions:

Problem:

This program creates word search puzzles - Words are printed at random locations in a rectangular grid. Words may be horizontal or vertical and may be forward (left-to-right or top-to-bottom) or reversed (right-to-left or bottom-to-top). Unused grid squares are filled with random letters. The program should take a set of word lists as input and produce two files as output. The first lists, for each puzzle, the list of words in the puzzle, followed by the puzzle itself. The second should show where in each puzzle the words are located, without the random filler letters

Our input file contains the following: A number n > 0 (representing the # of words in the puzzle) followed by that many words. For example:

3
FRODO
GIMLI
ARAGORN

N will not be larger than 10

We need to create the puzzle using a multidimensional array of size 12 x 12

Requirements:
1. Two output files - One containing the puzzle words and he puzzles, one with only the solutions and no filler characters
2.There needs to be as many horizontal words as there are vertical words
3. A 1/3 of the words needs to be reversed
4. There needs to be at least two intersections in the puzzle


Proposed Algorithm:
1. Create two multidimensional arrays - one for the puzzle and one for the solutions
2. Create a one-dimensional array that contains the various letters of the alphabet
3. Fill the puzzles array with random letters of the alphabet (using a pseudo-random # generator and the array from step # 2)
4. Begin reading the input file
5. Read in n
6. While counter is less than n, read in words, also have a counter for vertical words and horizontal words
7. For each word, find the length of the string
8. Find a random array location to insert the word.
9. If random location index + string length <= 12 or if random location index - string length is >= 0 (to ensure that the word will fit forward or reverse) insert the word
10. Also insert the word into the solutions array
12. Reuse arrays to insert all the words in the input file (in a similar manner)

I am still unsure as to how I can ensure that at least two intersections exist.

I am also concerned that my proposed algorithm is unnecessarily convoluted.

Your feedback is greatly appreciated!


OK, Here's as far as I got into the coding process, before I decided to go back and revisit the algorithm:

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <ctime>
using namespace std;

//Error Codes
const int INPUT_FAIL = 1;
const int PUZZLES_OUTPUT_FAIL = 2;
const int SOLUTIONS_OUTPUT_FAIL = 3;

//Function Declarations/Prototypes
void OpenFiles(ifstream& input, ofstream& puzzles, ofstream& solutions);
//PRE:  The filestream objects exist and their address locations are passed in
//POST: The filestreams are opened. If they cannot be opened, an error message is printed to screen
//      and the program is terminated.

void FillArray(char puzzle[][12], char alphabet[]);
//PRE:  The address of the array is passed in
//POST: The array is filled with a random set of 

void CreatePuzzle(char puzzle[][12], ifstream& input, ofstream& puzzles, ofstream& solutions);
//PRE:  The address of the puzzle array,the address of the ifstream object and the addresses of the
//      ofstream objects are passed in.
//POST: The data in the input file is read and the words are input into the puzzle AND the puzzle
//      and solutions are printed to file.

void PrintPuzzle(char puzzle[][12], ofstream& output);
//PRE:  The address of the puzzle array and the ofstream object is passed in
//POST: The puzzle is output to the file

int main()
{
    //Seed the pseudo random generator
    srand(time(NULL));



    //Declare the filestream objects
    ifstream input;
    ofstream puzzles, solutions;

    //Declare the 2D array
    char puzzle[12][12];
    char solution[12][12];

    //Declare an alphabet array
    char alphabet[27] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
    /*char alphabet[27] = {'A','B','C','D','E','F','G','H','I','J','K','L',
    'M','N','O','P','Q','R','S','T','U','V','W',
    'X','Y','Z'};*/

    //Attempt to open files
    OpenFiles(input, puzzles, solutions);

    //Fill puzzle array with random letters of the alphabet
    FillArray(puzzle, alphabet);

    //Print puzzle
    PrintPuzzle(puzzle, puzzles);

    //Read in data to create puzzle
    input >> numwords;

    return 0;
}



//Function definitions
void OpenFiles(ifstream& input, ofstream& puzzles, ofstream& solutions)
{

    //Attempt to open files
    input.open("input.txt");
    puzzles.open("puzzles2.txt");
    solutions.open("solutions2.txt");

    //Ensure they opened correctly
    if (input.fail())
    {
        cout << "Input file failed to open!" << endl;
        exit(INPUT_FAIL);
    }

    if (puzzles.fail())
    {
        cout << "Output file - puzzles.txt failed to open!" << endl;
        exit(PUZZLES_OUTPUT_FAIL);
    }

    if (solutions.fail())
    {
        cout << "Output file - solutions.txt failed to open" << endl;
        exit(SOLUTIONS_OUTPUT_FAIL);
    }

}


void FillArray(char puzzle[][12], char alphabet[])
{
    int tmp;
    for(int i = 0; i < 12; i++)
    {
        for(int j = 0; j < 12; j++)
        {
            tmp = rand()%26;
            puzzle[i][j] = alphabet[tmp];
        }
    }
}


void PrintPuzzle(char puzzle[][12], ofstream& output)
{
    for(int i = 0; i < 12; i++)
    {
        for(int j = 0; j < 12; j++)
        {
            output <<   puzzle[i][j] << " ";
        }
        output << endl;
    }
}



void CreatePuzzle(char puzzle[][12], ifstream& input, ofstream& puzzles, ofstream& solutions)
{
    string pword; //#the puzzle word being read
    int numwords; //# of words in a given puzzle
    char tmparray[13];
    int wordlength = 0;
    int startloc;

    //Read the number of words to be used in the puzzle
    input >> numwords;

    int vwords = numwords/2; //#of vertical words
    int rwords = numwords/3; //# of reversed words
    int hwords = (numwords - (numwords/2)); //# of horizontal words

    for(int i = 0; i < numwords; i++)
    {
        //Read the word into our tmparray
        input >> pword;
        tmparray[] = pword;
        wordlength = pword.length();

        //Find a random array location to begin inserting the words
        startloc = rand()%12;
    int tmpcount = 0; //a temporary counter to ensure that 
        for(tmpcount; tmpcount <= 1; tmpcount ++)startloc + wordlength < 12)
        {
            for(int j = 0; j <= wordlength; j++)
            {
                puzzle[startloc][startloc]
+3  A: 

Try it on paper first
Then make it work (in code)
Then make it fast /efficent / elegant

edit - Sorry I wasn't being sarcastic, this was before the OP posted code, and it wasn't clear they had attempted the problem.

Martin Beckett
Martin, I actually already went down that road but didnt feel like I was going about it the right way. Hence the above request.
noobzilla
I'd be happy to post the code I wrote but didnt think anyone would want to wade through it.
noobzilla
Post the code so we can see what you have so far, after all it's always good practice to read someone's else code :).
Erethon
@noobzilla Upvoted your comment "didn't think anyone would want to wade through it" not because no one would want to do that, but because some of the beginner questions on here are "Here's a huge quantity of code; tell me if it's good or not" instead of a concise, well-asked actual question. So I appreciate your trying not to be like that. However, since Erethon kind of offered to take a look, I suppose you might as well post it.
MatrixFrog
There's a delicate balance between not wanting to wade through a huge block of code and wanting to know that the asker is actually making an effort toward the solution :)
Jimmy
Thanks guys. I appreciate the feedback and patience with us noobs. I had hoped that it wouldn't seem like I hadn't even tried (+ that's why I only asked for feedback on my algorithm). I know better know and will always share code :)
noobzilla
+1  A: 

A few thoughts/suggestions:

  1. I think you could do this with one single 2D array instead of two. It seems simpler in my mind, though of course you may find I'm wrong when you sit down to actually implement this.
  2. At step 9, instead of finding a spot where the word could fit either forward or reversed, first decide which way it's going to go in (perhaps using a random number generator). Then pick a spot and check the first condition, and just insert the word, either forwards or backwards. Remember this is a 2D array though, so you'll need to look at both the x and y coordinates of the point you chose. You'll also have to look at the words you've already placed in the grid, to make sure you don't overwrite something that's already there.
  3. For finding intersections, think about how you would do it by hand (like Martin said). You've already placed a few words in the grid and now you want to add a new one. Suppose there aren't many intersections yet so you'd like to have the current word intersect one of the ones already in teh grid, if possible. How do you know if an intersection is possible, and how do you know where to place the word so that it creates an intersection?
MatrixFrog
point 1 seems reasonable to me. even if you have two arrays, you wouldn't need to fill out the trash characters until the very end.
Jimmy
Maybe I am missing something really obvious here, but it would seem if I try to insert the trash characters at the end, I will need to keep track of a whole bunch of array locations so I don't overwrite data.
noobzilla
You have to do that anyway, don't you? Otherwise when you're adding a new word to the grid, you might overwrite a word that's already there. You could mark a space as blank with just a space or a question mark or something, and then at the end replace all the spaces/question marks with random letters.
MatrixFrog
+2  A: 

My first suggestion would be don't bother pre-filling the array with anything - just stick th words in, and fill in the gaps randomly once you're finished.

Anon.
I think being devious is better fill it in with partial words that match parts of the real words...
Tim
A: 

My first thoughts are:

  1. Place the words first and then randomly fill the gaps. I think it will be much easier for you to visualise it that way and also be easier to check if the word placement is done correctly.
  2. After placing the first word I would save the word into an array. After checking if the second word is small enough to fit the puzzle I would have the program find the common letters of word 1 and 2. If they have a common letter, place the second word so the two words intersect(of course you must first check if the way you try to place word 2 is legal aka if it fits the puzzle the way you are trying to place it). Word 3 is the same way except look for possible intersections between words 1 and 2.If there is no possible intersection try the next word. If you can't have 2 or more intersections after placing all words, empty the array and replace the first word in a different random position. After having placed words in such a way that at least two intersections exist, you can continue and place the rest of the words.
Erethon
Erethon, Jimmy had a similar idea for #1 - See my response above. I thought that approach would lead to more complicated code (I keep thinking I need to have a simpler solution - Maybe I am wrong :)On #2 - That's an interesting approach. I might try that.
noobzilla