Task: Word search




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




Python: import re
from random import shuffle, randint

dirs = [[1, 0], [0, 1], [1, 1], [1, -1], [-1, 0], [0, -1], [-1, -1], [-1, 1]]
n_rows = 10
n_cols = 10
grid_size = n_rows * n_cols
min_words = 25


class Grid:
    def __init__(self):
        self.num_attempts = 0
        self.cells = [['' for _ in range(n_cols)] for _ in range(n_rows)]
        self.solutions = []


def read_words(filename):
    max_len = max(n_rows, n_cols)

    words = []
    with open(filename, "r") as file:
        for line in file:
            s = line.strip().lower()
            if re.match(r'^[a-z]{3,' + re.escape(str(max_len)) + r'}$', s) is not None:
                words.append(s)

    return words


def place_message(grid, msg):
    msg = re.sub(r'[^A-Z]', "", msg.upper())

    message_len = len(msg)
    if 0 < message_len < grid_size:
        gap_size = grid_size // message_len

        for i in range(0, message_len):
            pos = i * gap_size + randint(0, gap_size)
            grid.cells[pos // n_cols][pos % n_cols] = msg[i]

        return message_len

    return 0


def try_location(grid, word, direction, pos):
    r = pos // n_cols
    c = pos % n_cols
    length = len(word)

    
    if (dirs[direction][0] == 1 and (length + c) > n_cols) or \
       (dirs[direction][0] == -1 and (length - 1) > c) or \
       (dirs[direction][1] == 1 and (length + r) > n_rows) or \
       (dirs[direction][1] == -1 and (length - 1) > r):
        return 0

    rr = r
    cc = c
    i = 0
    overlaps = 0

    
    while i < length:
        if grid.cells[rr][cc] != '' and grid.cells[rr][cc] != word[i]:
            return 0
        cc += dirs[direction][0]
        rr += dirs[direction][1]
        i += 1

    rr = r
    cc = c
    i = 0
    
    while i < length:
        if grid.cells[rr][cc] == word[i]:
            overlaps += 1
        else:
            grid.cells[rr][cc] = word[i]

        if i < length - 1:
            cc += dirs[direction][0]
            rr += dirs[direction][1]

        i += 1

    letters_placed = length - overlaps
    if letters_placed > 0:
        grid.solutions.append("{0:<10} ({1},{2})({3},{4})".format(word, c, r, cc, rr))

    return letters_placed


def try_place_word(grid, word):
    rand_dir = randint(0, len(dirs))
    rand_pos = randint(0, grid_size)

    for direction in range(0, len(dirs)):
        direction = (direction + rand_dir) % len(dirs)

        for pos in range(0, grid_size):
            pos = (pos + rand_pos) % grid_size

            letters_placed = try_location(grid, word, direction, pos)
            if letters_placed > 0:
                return letters_placed

    return 0


def create_word_search(words):
    grid = None
    num_attempts = 0

    while num_attempts < 100:
        num_attempts += 1
        shuffle(words)

        grid = Grid()
        message_len = place_message(grid, "Rosetta Code")
        target = grid_size - message_len

        cells_filled = 0
        for word in words:
            cells_filled += try_place_word(grid, word)
            if cells_filled == target:
                if len(grid.solutions) >= min_words:
                    grid.num_attempts = num_attempts
                    return grid
                else:
                    break 

    return grid


def print_result(grid):
    if grid is None or grid.num_attempts == 0:
        print("No grid to display")
        return

    size = len(grid.solutions)

    print("Attempts: {0}".format(grid.num_attempts))
    print("Number of words: {0}".format(size))

    print("\n     0  1  2  3  4  5  6  7  8  9\n")
    for r in range(0, n_rows):
        print("{0}   ".format(r), end='')
        for c in range(0, n_cols):
            print(" %c " % grid.cells[r][c], end='')
        print()
    print()

    for i in range(0, size - 1, 2):
        print("{0}   {1}".format(grid.solutions[i], grid.solutions[i+1]))

    if size % 2 == 1:
        print(grid.solutions[size - 1])


if __name__ == "__main__":
    print_result(create_word_search(read_words("unixdict.txt")))