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 {