1

I'm trying to make an algorithm to fill a list of given words in a square matrix where letters amount is equal to matrix size.

The trick here is that you should be able to connect each word (each letter with the letter after him in the same word) and at the end of connection the board should be cleared. (each word is independent - doesn't connect with other words).

You can connect a word: if its letters are neighbors - (neighbor can be: left, right, top, bottom, top-left, top-right, bottom-left, bottom-right). you should connect letters by order and can't skip letters. (like drawing a line on paper without removing the pencil) and you cant go above other letters from another words while connecting a word.

When a word is connected it's removed from the board - and every letter above its letters will fall down to take it's place (like: letters can't fly). each will fall above the other and not at the same position in the matrix.

Words doesn't have to be all connected by the first look at the board - so: they should be connectable while other words are connected - then its letters will be neighbors.

This is an example of how the board should look like:

Board Solving Example - GIF

The Board in the image was generated by the algorithm I wrote, it generates a 5X5 board within 1-10 seconds witch it's too much for a computer. the problem I have is that a 7X7 and larger boards will take "infinite" time to be generated, yet I couldn't generate any 7X7 board.

Another note: a board should not follow a pattern that repeats on all boards, and its preferred not to follow patterns.

I have searched for days on the internet and couldn't find articles solving such problem. My CPU usage when launching the code is about 1.1%. I don't know why it takes a lot of time.

I'm using a recursive algorithm, and this is the main function that controls the generations:

public void Build()
    {
        // Sort words from The longest to the shortest
        sortWords();

        //convert list of words to string
        string words = ListToString(this.words);

        // Get The Result of the generation function.
        bool result = Generate(words, null);
    }



    private Random random = new Random();
    private bool Generate(string words, Word word)
    {
        // If [words = null], Then The function failed to generate a board from no words.
        if (words == null)
            return false;

        // If got to the end of the words, Then succedd to fill all words in the board
        if (words.Length == 0)
            return true;

        // If Got [" "] blank letter, Then should switch to the next word. 
        if (words[0].ToString() == " ")
        {
            board.changedWord(); // Delegate: Update UI
            return Generate(words.Substring(1, words.Length - 1), null);
        }

        // If [word = null], Then we are creating a new word
        if (word == null)
            word = new Word(board);

        // Check If there is a group of letters that can't connect with other letters
        // and they cann't contain any word in them.
        if (board.isLocked(minInString(words)))
            return false;

        // Store all available legal neighbours to the previus added letter
        List<Location> neighbours = new List<Location>();


        if (word.Length == 0) { // This is a new word -> Any Cell would be legal
            neighbours = board.FreeLocations();
        }

        else { // This word is in progress. Neighbours rules should be applied
            neighbours = board.Neighbours(word.Letters.Last().Location);
        }

        if (neighbours.Count == 0) { // If found no neighbours, Then cant continue with the current word
            return false;
        }

        int index = 0; // Shoiuld hold letter index in word - has nothing to do right now. initail value [0].

        while (neighbours.Count > 0)
        { // Try each one of the suggested neighbours

            // Get a random neighbour from available legal neighbours
            int randLocationIndex = random.Next(neighbours.Count);
            Location location = neighbours[randLocationIndex];

            // If word wasn't added to the words, add it!
            if (!board.Words.Contains(word))
                board.Words.Add(word);

            // Add the current letter to the related word in board
            board.Add(new Letter(word, location, words[0].ToString(), index), word);

            if (!word.isValid()) {
                // Check if current word can be connected,
                //Its own letters doesn't make any problem to each other.
                board.Remove(word.Letter(word.Length - 1));
                neighbours.RemoveAt(randLocationIndex);
                continue;
            }

            if (!board.Solved()) {
                // Check if the board is solvable [Gravity cases]
                // -> Check each word that devides the connection between current word, that its connectable 
                board.Remove(word.Letter(word.Length - 1));
                neighbours.RemoveAt(randLocationIndex);
                continue;
            }

            // Try the letters after me.
            bool result = Generate(words.Substring(1, words.Length - 1), word);

            // If all words after this, succeded, then me and sons succeded, return true.
            if (result == true)
                return true;

            else { // Remove added point from word's path, Remove it from available neighbours
                board.Remove(word.Letter(word.Length - 1));
                neighbours.RemoveAt(randLocationIndex);
            }
        }

        // If Current Word has added no letters to board: then remove it from board
        // we dont need any empty useless word in the board.
        if (word.Length == 0) {
            //board.CancelWord(); Delegate: Update Board UI
            board.Words.Remove(word);
        }

        // Tryed all sugested neighbours for current letter, all of them has failed
        // retuen false - Thats a fail :(
        return false;
    }

I can read / write c#, C++, C, Java, Swift, and Objective-C.

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • You should post the code you're using now to generate the 5x5 board from your gif - it sounds like you're brute forcing your generation rather than doing much algorithmically. – Preston Guillot Jan 10 '17 at 19:18
  • It sound like you are generating the board with a recursive algorithm that is attempting every combination. That algorithm has to know when to stop trying adding words. For example if you have only 4 empty spaces you shouldn't be trying to add a 5 letter word. – jdweng Jan 10 '17 at 19:27
  • @jdweng Yes I'm using a recursive algorithm, and i do take such case in mind. thank you for notifying me about such things. I will search my code for such cases. many thanks :) – AbdulHadi Yasin Jan 10 '17 at 19:51
  • @PrestonGuillot I have added a snippet of my code as you stated, many thanks :). – AbdulHadi Yasin Jan 10 '17 at 19:58
  • You have to trade-off how well you pack (number of words on board) verses time to run algorithm. Getting the best packing will take a long time so if time is important than the packing criteria has to be reduced. So if you have a 5x5 board (25 spaces) you may want to stop when you filled 18 spaces. With a 7x7 you may want to stop at 35 filled. After you reach 35 you may want to fill with random characters. – jdweng Jan 10 '17 at 21:10
  • @jdweng What you are saying sounds interesting, even though filling a partial part from the board will not work for me. but how would that help achieving a full board. but you have inspired me to think of limiting neighbor spaces, if there was a way to do that! I think that the function that check the gravity case is limiting the packs (number of words on board). Actually my question was to find a way to check that. since disabling this condition will faster the process to a second for 7X7 board. I still cant find the relationship. would be thankful for any help. and many thanks. :) – AbdulHadi Yasin Jan 10 '17 at 21:27

0 Answers0