Task: Word search


C++: #include <iomanip>
#include <ctime>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <fstream>

const int WID = 10, HEI = 10, MIN_WORD_LEN = 3, MIN_WORD_CNT = 25;

class Cell {
public:
    Cell() : val( 0 ), cntOverlap( 0 ) {}
    char val; int cntOverlap;
};
class Word {
public:
    Word( std::string s, int cs, int rs, int ce, int re, int dc, int dr ) : 
      word( s ), cols( cs ), rows( rs ), cole( ce ), rowe( re ), dx( dc ), dy( dr ) {}
    bool operator ==( const std::string& s ) { return 0 == word.compare( s ); }
    std::string word;
    int cols, rows, cole, rowe, dx, dy;
};
class words {
public:
    void create( std::string& file ) {
        std::ifstream f( file.c_str(), std::ios_base::in );
        std::string word;
        while( f >> word ) {
            if( word.length() < MIN_WORD_LEN || word.length() > WID || word.length() > HEI ) continue;
            if( word.find_first_not_of( "abcdefghijklmnopqrstuvwxyz" ) != word.npos ) continue;
            dictionary.push_back( word );
        }
        f.close();
        std::random_shuffle( dictionary.begin(), dictionary.end() );
        buildPuzzle();
    }

    void printOut() {
        std::cout << "\t";
        for( int x = 0; x < WID; x++ ) std::cout << x << "  ";
        std::cout << "\n\n";
        for( int y = 0; y < HEI; y++ ) {
            std::cout << y << "\t";
            for( int x = 0; x < WID; x++ )
                std::cout << puzzle[x][y].val << "  ";
            std::cout << "\n";
        }
        size_t wid1 = 0, wid2 = 0;
        for( size_t x = 0; x < used.size(); x++ ) {
            if( x & 1 ) {
                if( used[x].word.length() > wid1 ) wid1 = used[x].word.length();
            } else {
                if( used[x].word.length() > wid2 ) wid2 = used[x].word.length();
            }
        }
        std::cout << "\n";
        std::vector<Word>::iterator w = used.begin();
        while( w != used.end() ) {
            std::cout << std::right << std::setw( wid1 ) << ( *w ).word << " (" << ( *w ).cols << ", " << ( *w ).rows << ") (" 
                      << ( *w ).cole << ", " << ( *w ).rowe << ")\t";
            w++;
            if( w == used.end() ) break;
            std::cout << std::setw( wid2 ) << ( *w ).word << " (" << ( *w ).cols << ", " << ( *w ).rows << ") (" 
                      << ( *w ).cole << ", " << ( *w ).rowe << ")\n";
            w++;
        }
        std::cout << "\n\n";
    }
private:
    void addMsg() {
        std::string msg = "ROSETTACODE";
        int stp = 9, p = rand() % stp;
        for( size_t x = 0; x < msg.length(); x++ ) {
            puzzle[p % WID][p / HEI].val = msg.at( x );
            p += rand() % stp + 4;
        }
    }
    int getEmptySpaces() {
        int es = 0;
        for( int y = 0; y < HEI; y++ ) {
            for( int x = 0; x < WID; x++ ) {
                if( !puzzle[x][y].val ) es++;
            }
        }
        return es;
    }
    bool check( std::string word, int c, int r, int dc, int dr ) {
        for( size_t a = 0; a < word.length(); a++ ) {
            if( c < 0 || r < 0 || c >= WID || r >= HEI ) return false;
            if( puzzle[c][r].val && puzzle[c][r].val != word.at( a ) ) return false;
            c += dc; r += dr;
        }
        return true;
    }
    bool setWord( std::string word, int c, int r, int dc, int dr ) {
        if( !check( word, c, r, dc, dr ) ) return false;
        int sx = c, sy = r;
        for( size_t a = 0; a < word.length(); a++ ) {
            if( !puzzle[c][r].val ) puzzle[c][r].val = word.at( a );
            else puzzle[c][r].cntOverlap++;
            c += dc; r += dr;
        }
        used.push_back( Word( word, sx, sy, c - dc, r - dr, dc, dr ) );
        return true;
    }
    bool add2Puzzle( std::string word ) {
        int x = rand() % WID, y = rand() % HEI,
            z = rand() % 8;
        for( int d = z; d < z + 8; d++ ) {
            switch( d % 8 ) {
                case 0: if( setWord( word, x, y,  1,  0 ) ) return true; break;
                case 1: if( setWord( word, x, y, -1, -1 ) ) return true; break;
                case 2: if( setWord( word, x, y,  0,  1 ) ) return true; break;
                case 3: if( setWord( word, x, y,  1, -1 ) ) return true; break;
                case 4: if( setWord( word, x, y, -1,  0 ) ) return true; break;
                case 5: if( setWord( word, x, y, -1,  1 ) ) return true; break;
                case 6: if( setWord( word, x, y,  0, -1 ) ) return true; break;
                case 7: if( setWord( word, x, y,  1,  1 ) ) return true; break;
            }
        }
        return false;
    }
    void clearWord() {
        if( used.size() ) {
            Word lastW = used.back();
            used.pop_back();

            for( size_t a = 0; a < lastW.word.length(); a++ ) {
                if( puzzle[lastW.cols][lastW.rows].cntOverlap == 0 ) {
                    puzzle[lastW.cols][lastW.rows].val = 0;
                }
                if( puzzle[lastW.cols][lastW.rows].cntOverlap > 0 ) {
                    puzzle[lastW.cols][lastW.rows].cntOverlap--;
                }
                lastW.cols += lastW.dx; lastW.rows += lastW.dy;
            }
        }
    }
    void buildPuzzle() {
        addMsg();
        int es = 0, cnt = 0;
        size_t idx = 0;
        do {
            for( std::vector<std::string>::iterator w = dictionary.begin(); w != dictionary.end(); w++ ) {
                if( std::find( used.begin(), used.end(), *w ) != used.end() ) continue;
            
                if( add2Puzzle( *w ) ) {
                    es = getEmptySpaces();
                    if( !es && used.size() >= MIN_WORD_CNT ) 
                        return;
                }
            }
            clearWord();
            std::random_shuffle( dictionary.begin(), dictionary.end() );

        } while( ++cnt < 100 );
    }
    std::vector<Word> used;
    std::vector<std::string> dictionary;
    Cell puzzle[WID][HEI];
};
int main( int argc, char* argv[] ) {
    unsigned s = unsigned( time( 0 ) );
    srand( s );
    words w; w.create( std::string( "unixdict.txt" ) );
    w.printOut();
    return 0;
}


C#: using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Wordseach
{
    static class Program
    {
        readonly static int[,] dirs = {{1, 0}, {0, 1}, {1, 1}, {1, -1}, {-1, 0},
            {0, -1}, {-1, -1}, {-1, 1}};

        class Grid
        {
            public char[,] Cells = new char[nRows, nCols];
            public List<string> Solutions = new List<string>();
            public int NumAttempts;
        }

        readonly static int nRows = 10;
        readonly static int nCols = 10;
        readonly static int gridSize = nRows * nCols;
        readonly static int minWords = 25;

        readonly static Random rand = new Random();

        static void Main(string[] args)
        {
            PrintResult(CreateWordSearch(ReadWords("unixdict.txt")));
        }

        private static List<string> ReadWords(string filename)
        {
            int maxLen = Math.Max(nRows, nCols);

            return System.IO.File.ReadAllLines(filename)
                .Select(s => s.Trim().ToLower())
                .Where(s => Regex.IsMatch(s, "^[a-z]{3," + maxLen + "}$"))
                .ToList();
        }

        private static Grid CreateWordSearch(List<string> words)
        {
            int numAttempts = 0;

            while (++numAttempts < 100)
            {
                words.Shuffle();

                var grid = new Grid();
                int messageLen = PlaceMessage(grid, "Rosetta Code");
                int target = gridSize - messageLen;

                int cellsFilled = 0;
                foreach (var word in words)
                {
                    cellsFilled += TryPlaceWord(grid, word);
                    if (cellsFilled == target)
                    {
                        if (grid.Solutions.Count >= minWords)
                        {
                            grid.NumAttempts = numAttempts;
                            return grid;
                        }
                        else break; 
                    }
                }
            }
            return null;
        }

        private static int TryPlaceWord(Grid grid, string word)
        {
            int randDir = rand.Next(dirs.GetLength(0));
            int randPos = rand.Next(gridSize);

            for (int dir = 0; dir < dirs.GetLength(0); dir++)
            {
                dir = (dir + randDir) % dirs.GetLength(0);

                for (int pos = 0; pos < gridSize; pos++)
                {
                    pos = (pos + randPos) % gridSize;

                    int lettersPlaced = TryLocation(grid, word, dir, pos);
                    if (lettersPlaced > 0)
                        return lettersPlaced;
                }
            }
            return 0;
        }

        private static int TryLocation(Grid grid, string word, int dir, int pos)
        {
            int r = pos / nCols;
            int c = pos % nCols;
            int len = word.Length;

            
            if ((dirs[dir, 0] == 1 && (len + c) > nCols)
                    || (dirs[dir, 0] == -1 && (len - 1) > c)
                    || (dirs[dir, 1] == 1 && (len + r) > nRows)
                    || (dirs[dir, 1] == -1 && (len - 1) > r))
                return 0;

            int rr, cc, i, overlaps = 0;

            
            for (i = 0, rr = r, cc = c; i < len; i++)
            {
                if (grid.Cells[rr, cc] != 0 && grid.Cells[rr, cc] != word[i])
                {
                    return 0;
                }

                cc += dirs[dir, 0];
                rr += dirs[dir, 1];
            }

            
            for (i = 0, rr = r, cc = c; i < len; i++)
            {
                if (grid.Cells[rr, cc] == word[i])
                    overlaps++;
                else
                    grid.Cells[rr, cc] = word[i];

                if (i < len - 1)
                {
                    cc += dirs[dir, 0];
                    rr += dirs[dir, 1];
                }
            }

            int lettersPlaced = len - overlaps;
            if (lettersPlaced > 0)
            {
                grid.Solutions.Add($"{word,-10} ({c},{r})({cc},{rr})");
            }

            return lettersPlaced;
        }

        private static int PlaceMessage(Grid grid, string msg)
        {
            msg = Regex.Replace(msg.ToUpper(), "[^A-Z]", "");

            int messageLen = msg.Length;
            if (messageLen > 0 && messageLen < gridSize)
            {
                int gapSize = gridSize / messageLen;

                for (int i = 0; i < messageLen; i++)
                {
                    int pos = i * gapSize + rand.Next(gapSize);
                    grid.Cells[pos / nCols, pos % nCols] = msg[i];
                }
                return messageLen;
            }
            return 0;
        }

        public static void Shuffle<T>(this IList<T> list)
        {
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rand.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }

        private static void PrintResult(Grid grid)
        {
            if (grid == null || grid.NumAttempts == 0)
            {
                Console.WriteLine("No grid to display");
                return;
            }
            int size = grid.Solutions.Count;

            Console.WriteLine("Attempts: " + grid.NumAttempts);
            Console.WriteLine("Number of words: " + size);

            Console.WriteLine("\n     0  1  2  3  4  5  6  7  8  9");
            for (int r = 0; r < nRows; r++)
            {
                Console.Write("\n{0}   ", r);
                for (int c = 0; c < nCols; c++)
                    Console.Write(" {0} ", grid.Cells[r, c]);
            }

            Console.WriteLine("\n");

            for (int i = 0; i < size - 1; i += 2)
            {
                Console.WriteLine("{0}   {1}", grid.Solutions[i],
                        grid.Solutions[i + 1]);
            }
            if (size % 2 == 1)
                Console.WriteLine(grid.Solutions[size - 1]);

            Console.ReadLine();
        }        
    }
}