0

I am trying to design a recursive solution to Lewis Carroll's 'Word Ladder' game

The game attempts to link a starting word, for example 'WET' to an ending word, e.g. 'DRY' using a chain of words where between any 2 words just 1 letter is different. There is a limit on how many words can be used to make the chain.

For example for WET - DRY the solution is WET - bet - bat - bay - day - DRY

I am using the below recursive C function

  • exit condition: word is 1 step away from the 'target word' - i.e. if it sees DAY (= 1 step away from DRY), it returns true
  • It finds the solution, however, the problem is: it does not pass the solution back to original function. With each return call, the chain shaves off one word - i.e. the function's innermost call correctly solves 'bet - bat - bay - day' and stores it in the answer_chain - however once calls unwind - this array somehow gets passed as 'bet - bat - bay' (=loses 1 word), and so on, and as a result the original outermost function call gets no information about the solution.

I've tried:

  • Copying 'by value' by creating a c-style string first, assigning it back later
  • Copying the array item-by-item only when the first return is reached, and just assigning one array to the other otherwise (answer_chain = answer_chain_temp)

I've unfortunately not been able to figure out what the issue actually is.

Any help would be greatly appreciated.

bool find_chain(const char* start_word, const char* target_word, const char* answer_chain[], int max_steps){
    
    if (answer_chain[0] == NULL) {} // This is to zero out incoming answer_chain  
    else if (!valid_step(start_word,answer_chain[0])){ 
        delete[] answer_chain;
        for (unsigned int i = 0; i < sizeof(answer_chain)/sizeof(answer_chain[0]);++i){
            answer_chain[i] = NULL;
        }
    }
        
    int filled_words = find_sz_of_char_arrays(answer_chain); // Looks for null-terminator
    
    string previous_word, try_new_word;
    bool is_solved = false;
    
    if (valid_chain(answer_chain) && valid_step(answer_chain[filled_words-1],target_word) ) {
        is_solved = true;
    }
    
    if (is_solved) {return true;}
    if (max_steps == 0 && !is_solved) {return false;}
    
    if (filled_words > 0) { previous_word = answer_chain[filled_words-1];} else {previous_word = start_word;}
    
    for (unsigned int j = 0; j < strlen(start_word); ++j){
        try_new_word = previous_word;
        for (unsigned int i = 0; i < 26; ++i){  
            char new_char = i + 'A';            
            if (try_new_word.at(j) != new_char) { // has to be different from character already in place
                try_new_word.at(j) = new_char;              
                
                if (valid_step(previous_word.c_str(),try_new_word.c_str()) && !is_word_already_in_chain(try_new_word.c_str(),answer_chain) ) {
                        
                    const char** answer_chain_temp = new const char*[15];   // append 'try' word to answer array
    
                    for (int k = 0; k < filled_words;++k){
                        answer_chain_temp[k] = answer_chain[k];}
                    answer_chain_temp[filled_words] = try_new_word.c_str();
                    answer_chain_temp[filled_words+1] = NULL;
    
                    if (find_chain(start_word,target_word,answer_chain_temp,max_steps-1)){
                        delete[] answer_chain;
                        answer_chain = new const char*[15];
    
                        for (int kk = 0; kk < 15;++kk) {
                            if (answer_chain_temp[kk]!=NULL){
                                answer_chain[kk] = answer_chain_temp[kk];
                            }
                        }
    
                        delete[] answer_chain_temp;
                        
                        return true;
                    } // if successful - append the word
                } // if valid step
            } // if letter is differerent
        } // for i
    } // for j
    
    return false;
}

EDIT: I've now changed the middle part to copy the .s_str() by value, however the issue still seems to persist. I believe something is off with how I am copying and deleting after the solution has been found:

const char** answer_chain_temp = new const char*[15];   // append 'try' word to answer array

for (int k = 0; k < filled_words;++k){answer_chain_temp[k] = answer_chain[k];}

char * writable = new char[try_new_word.size() + 1];
std::copy(try_new_word.begin(), try_new_word.end(), writable);
writable[try_new_word.size()] = '\0';

answer_chain_temp[filled_words] = writable;                     
answer_chain_temp[filled_words+1] = NULL;

if (find_chain(start_word,target_word,answer_chain_temp,max_steps-1)){
delete[] answer_chain;
answer_chain = new const char*[15];

for (int kk = 0; kk < 15;++kk) {
    if (answer_chain_temp[kk] != NULL){
        answer_chain[kk] = answer_chain_temp[kk]; // <<<<<< I believe the problem is here
            }
        }
                    
display_chain(answer_chain,cout); cout << endl;
delete[] answer_chain_temp;
return true;```
  • `delete[]` is C++, not C. – Barmar Dec 23 '20 at 23:50
  • 2
    One obvious thing I see is that you're not allocating storage for each word in the answer, you're just storing a pointer to whatever `std::string::c_str()` returns. Read up on the lifetime of that pointer: https://stackoverflow.com/questions/6456359/what-is-stdstringc-str-lifetime What you'll find is that you should be making a copy of the data, not just storing the pointer. This might solve your issue. Specifically I am referring to this line: `answer_chain_temp[filled_words] = try_new_word.c_str();` and then this one: `answer_chain[kk] = answer_chain_temp[kk];` – Tumbleweed53 Dec 23 '20 at 23:54
  • 1
    BTW, It's bad mojo to mix C++ style libraries and C arrays. Use a `std::vector` here and these types of problems go away. – Tumbleweed53 Dec 23 '20 at 23:56
  • Thank you, agreed. I can't change the char's to string unfortunately, I have to work with the prototype as it is - per terms of the problem I've been given. – erixliechtenstein Dec 24 '20 at 00:08
  • You have a lot of stuff going on, making it harder to see this specific problem. I advise trimming your code down to a [mre]. Make a copy of your code, and don't worry about solving your problem. Keep the recursion structure, but drop mostly everything else. Forget input and just assume the word is "bet" each step. So recurse 4 times, trying to get "bet - bet - bet - bet", which your bug will change to "bet - bet - bet", then to "bet - bet" etc. Analyze this simpler setup to get a better idea of what is going on, and to make it easier on people volunteering to help you. – JaMiT Dec 24 '20 at 00:15
  • @Tumbleweed53 Thank you very much again for your answer. I've replaced `try_new_word.c_str()` with `char * writable = new char[try_new_word.size() + 1]; std::copy(try_new_word.begin(), try_new_word.end(), writable); writable[try_new_word.size()] = '\0'; answer_chain_temp[filled_words] = writable;` Would you be able to point me in the right direction with how to assign correctly back? `answer_chain[kk] = answer_chain_temp[kk];` I do understand what's causing the issue now from the link, but not sure how to fix it – erixliechtenstein Dec 24 '20 at 10:38
  • Right so it looks like you've made a copy of the data held by the string, so that's good. As far as the assignment back from `answer_chain_temp` to `answer_chain`, the data is now yours and you fully control its lifetime. So you can freely assign it, as long as you keep track of who's supposed to delete it. From my *super quick* read of your comment, it sounds like things should be working now - do they not? – Tumbleweed53 Dec 25 '20 at 04:21
  • thank you @Tumbleweed53. I am now working with the below. Same problem persists - I lose one data item on each unwind: `delete[] answer_chain; answer_chain = new const char*[15]; for (int kk = 0; kk < 15;++kk) { if (answer_chain_temp[kk] != NULL){ answer_chain[kk] = answer_chain_temp[kk]; // < I believe problem is here } } display_chain(answer_chain,cout); cout << endl; delete[] answer_chain_temp;` – erixliechtenstein Dec 25 '20 at 11:46
  • @Tumbleweed53 I've added an edit in the original post to better illustrate the current iteration. Apologies for the comment above, it's a bit hard to read, but hopefully the edit in the post makes sense. – erixliechtenstein Dec 25 '20 at 12:25
  • Stop using `new[]` and `delete[]` and `char*` strings, like, 20 years ago. – n. m. could be an AI Dec 25 '20 at 12:42
  • @n.'pronouns'm. I would love to, and I'd have none of these issues in using the normal `string` methods, unfortunately I have to use the function prototype with `const char*` per how the problem is set – erixliechtenstein Dec 25 '20 at 13:19
  • If you need to write or use an API with `const char*`, no problem, you can do that. Just don't create any `char*` strings internally and don't use any `new` or `delete`. – n. m. could be an AI Dec 27 '20 at 10:24

0 Answers0