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;
}



Go: package main

import (
    "bufio"
    "fmt"
    "log"
    "math/rand"
    "os"
    "regexp"
    "strings"
    "time"
)

var dirs = [][]int{{1, 0}, {0, 1}, {1, 1}, {1, -1}, {-1, 0}, {0, -1}, {-1, -1}, {-1, 1}}

const (
    nRows    = 10
    nCols    = nRows
    gridSize = nRows * nCols
    minWords = 25
)

var (
    re1 = regexp.MustCompile(fmt.Sprintf("^[a-z]{3,%d}$", nRows))
    re2 = regexp.MustCompile("[^A-Z]")
)

type grid struct {
    numAttempts int
    cells       [nRows][nCols]byte
    solutions   []string
}

func check(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func readWords(fileName string) []string {
    file, err := os.Open(fileName)
    check(err)
    defer file.Close()
    var words []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        word := strings.ToLower(strings.TrimSpace(scanner.Text()))
        if re1.MatchString(word) {
            words = append(words, word)
        }
    }
    check(scanner.Err())
    return words
}

func createWordSearch(words []string) *grid {
    var gr *grid
outer:
    for i := 1; i < 100; i++ {
        gr = new(grid)
        messageLen := gr.placeMessage("Rosetta Code")
        target := gridSize - messageLen
        cellsFilled := 0
        rand.Shuffle(len(words), func(i, j int) {
            words[i], words[j] = words[j], words[i]
        })
        for _, word := range words {
            cellsFilled += gr.tryPlaceWord(word)
            if cellsFilled == target {
                if len(gr.solutions) >= minWords {
                    gr.numAttempts = i
                    break outer
                } else { 
                    break
                }
            }
        }
    }
    return gr
}

func (gr *grid) placeMessage(msg string) int {
    msg = strings.ToUpper(msg)
    msg = re2.ReplaceAllLiteralString(msg, "")
    messageLen := len(msg)
    if messageLen > 0 && messageLen < gridSize {
        gapSize := gridSize / messageLen
        for i := 0; i < messageLen; i++ {
            pos := i*gapSize + rand.Intn(gapSize)
            gr.cells[pos/nCols][pos%nCols] = msg[i]
        }
        return messageLen
    }
    return 0
}

func (gr *grid) tryPlaceWord(word string) int {
    randDir := rand.Intn(len(dirs))
    randPos := rand.Intn(gridSize)
    for dir := 0; dir < len(dirs); dir++ {
        dir = (dir + randDir) % len(dirs)
        for pos := 0; pos < gridSize; pos++ {
            pos = (pos + randPos) % gridSize
            lettersPlaced := gr.tryLocation(word, dir, pos)
            if lettersPlaced > 0 {
                return lettersPlaced
            }
        }
    }
    return 0
}

func (gr *grid) tryLocation(word string, dir, pos int) int {
    r := pos / nCols
    c := pos % nCols
    le := len(word)

    
    if (dirs[dir][0] == 1 && (le+c) > nCols) ||
        (dirs[dir][0] == -1 && (le-1) > c) ||
        (dirs[dir][1] == 1 && (le+r) > nRows) ||
        (dirs[dir][1] == -1 && (le-1) > r) {
        return 0
    }
    overlaps := 0

    
    rr := r
    cc := c
    for i := 0; i < le; i++ {
        if gr.cells[rr][cc] != 0 && gr.cells[rr][cc] != word[i] {
            return 0
        }
        cc += dirs[dir][0]
        rr += dirs[dir][1]
    }

    
    rr = r
    cc = c
    for i := 0; i < le; i++ {
        if gr.cells[rr][cc] == word[i] {
            overlaps++
        } else {
            gr.cells[rr][cc] = word[i]
        }
        if i < le-1 {
            cc += dirs[dir][0]
            rr += dirs[dir][1]
        }
    }

    lettersPlaced := le - overlaps
    if lettersPlaced > 0 {
        sol := fmt.Sprintf("%-10s (%d,%d)(%d,%d)", word, c, r, cc, rr)
        gr.solutions = append(gr.solutions, sol)
    }
    return lettersPlaced
}

func printResult(gr *grid) {
    if gr.numAttempts == 0 {
        fmt.Println("No grid to display")
        return
    }
    size := len(gr.solutions)
    fmt.Println("Attempts:", gr.numAttempts)
    fmt.Println("Number of words:", size)
    fmt.Println("\n     0  1  2  3  4  5  6  7  8  9")
    for r := 0; r < nRows; r++ {
        fmt.Printf("\n%d   ", r)
        for c := 0; c < nCols; c++ {
            fmt.Printf(" %c ", gr.cells[r][c])
        }
    }
    fmt.Println("\n")
    for i := 0; i < size-1; i += 2 {
        fmt.Printf("%s   %s\n", gr.solutions[i], gr.solutions[i+1])
    }
    if size%2 == 1 {
        fmt.Println(gr.solutions[size-1])
    }
}

func main() {
    rand.Seed(time.Now().UnixNano())
    unixDictPath := "/usr/share/dict/words"
    printResult(createWordSearch(readWords(unixDictPath)))
}