News:

Welcome to RetroCoders Community

Main Menu

C3 chatbot Danny

Started by ron77, Dec 24, 2024, 05:26 AM

Previous topic - Next topic

ron77

hi all, well i converted my chatbot from C to C3 here is the code:

module c3_danny_std;

import std;
import libc;
import std::math::random;

extern fn char* strdup(char* s);

const int MAX_STRING_LENGTH = 1000;
const int MAX_ARRAY_SIZE = 1000;
const int MAX_KEYWORDS = 50;
const int MAX_REPLIES = 50;
const int MAX_MATCHES = 10;
const int MAX_SWAP_WORDS = 100;
const int MAX_MEMORY = 15;

/*
s:are>am
s:am>are
s:were>was
s:was>were
s:you>i
s:i>you
s:your>my
s:my>your
s:i've>you've
s:you've>i've
s:i'm>you're
s:you're>i'm
s:me>you
s:i'll>you'll
s:you'll>i'll
*/




String[] word_in = {
    "are",
    "am",
    "were",
    "was",
    "you",
    "i",
    "your",
    "my",
    "i've",
    "you've",
    "i'm",
    "you're",
    "me",
    "i'll",
    "you'll",

};
String[] word_out = {
    "am",
    "are",
    "was",
    "were",
    "i",
    "you",
    "my",
    "your",
    "you've",
    "i've",
    "you're",
    "i'm",
    "you",
    "you'll",
    "i'll",

};



String[MAX_SWAP_WORDS] wordIn;
String[MAX_SWAP_WORDS] wordOut;
int wCnt = 0;

String[MAX_MEMORY] memory;
int memoryCount = 0;

struct ArraySet {
    String[MAX_KEYWORDS] keywords;
    String[MAX_REPLIES] replies;
    int keywordCount;
    int replyCount;
} 

ArraySet[MAX_ARRAY_SIZE] g_Key_Reply;
int g_Key_ReplyCount = 0;

String[MAX_ARRAY_SIZE] default_replies;
int default_reply_count = 0;

fn char* safe_strdup(char* s) {
    if (!s) return null;
    char* result = strdup(s);
    if (!result) {
        io::printfn("%s Memory allocation failed in safe_strdup\n", &std::io::stderr);
        return null;
    }
    return result;
}

fn char* isolatePunctuation(char* s) {
    char* b = malloc(libc::strlen((ZString)s) * 3 + 1);
    if (!b) return null;
    
    int j = 0;
    for (int i = 0; s[i]; i++) {
        if (libc::strchr("?!,.:;<>(){}[]", s[i])) {
            b[j++] = ' ';
            b[j++] = s[i];
            b[j++] = ' ';
        } else {
            b[j++] = s[i];
        }
    }
    b[j] = '\0';
    return b;
}

fn void loadArrays(String filename) {
    File! f = io::file::open(filename, "r");
    if (catch error = f) {
        io::printfn("Error opening file: %s: %s\n", filename, error);
        return;
    }

    defer (void)f.close();

    // String line; no needs for this
    int currentSet = -1;
    
    while (try line = io::treadline(&f)) { // line is created here automatically
        if (line.len < 3) {
            // Skip lines that aren't long enough
            continue;
        }
        if (f.eof()) break;

        if (line[0..2] == "d1:") { //(strncmp(line, "d1:", 3) == 0) {
            default_replies[default_reply_count++] = line[3..]; //strcpy(default_replies[default_reply_count++], line + 3);
        }
    
        if (line[0..1] == "k:") { //(line[0] == 'k' && line[1] == ':') {
            if (currentSet == -1 || g_Key_Reply[currentSet].replyCount > 0) {
                currentSet++;
            }
            if (currentSet >= MAX_ARRAY_SIZE) {
                io::printfn("Error: too many keyword-reply pairs\n");
                break;
            }
            if (g_Key_Reply[currentSet].keywordCount < 50) {
                String keyword = string::tformat(" %s ", line[2..]);
            g_Key_Reply[currentSet].keywords[g_Key_Reply[currentSet].keywordCount] = keyword; //line[2..];//strcpy(g_Key_Reply[currentSet].keywords[g_Key_Reply[currentSet].keywordCount], line + 2);
            g_Key_Reply[currentSet].keywordCount++;
            g_Key_ReplyCount = currentSet + 1;
            // g_Key_Reply[currentSet].replyCount = 0;
            } else {
                io::printfn("Error: too many keywords for keyword-reply pair %d\n", currentSet);
            }
        } else if (line[0..1] == "r:") { //(line[0] == 'r' && line[1] == ':') {
            if (g_Key_Reply[currentSet].replyCount < 50) {
            g_Key_Reply[currentSet].replies[g_Key_Reply[currentSet].replyCount] = line[2..];//strcpy(g_Key_Reply[currentSet].replies[g_Key_Reply[currentSet].replyCount], line + 2);
            g_Key_Reply[currentSet].replyCount++;
            // g_Key_Reply[currentSet].keywordCount = 0;
            } else {
                io::printfn("Error: too many replies for keyword %s\n", g_Key_Reply[currentSet].keywords[g_Key_Reply[currentSet].keywordCount - 1]);
            }
        }
        
    }

     //fclose(file);
    io::printfn("number of keywords-replies pair groups %d\n", currentSet);

}

fn void loadSwapWords(String filename) {
    File! f = io::file::open(filename, "r");
    if (catch error = f) {
        io::printfn("Error opening file: %s: %s\n", filename, error);
        return;
    }

    defer (void)f.close();
    String line;
    while ((line = io::treadline(&f)!!) && wCnt < MAX_SWAP_WORDS) {
        if (line.len < 3) {
            // Skip lines that aren't long enough
                continue;
        }

        if (f.eof()) break;

        //line[strcspn(line, "\n")] = 0;  // Remove newline
        if (line[0..1] == "s:") { //(line[0] == 's' && line[1] == ':') {
            int indexOfdelimiter = (int)line.index_of(">")!!;
            // if (indexOfdelimiter != null) {
                wordIn[wCnt] = line[2..indexOfdelimiter];//strcpy(wordIn[wCnt], line + 2);
                wordOut[wCnt] = line[indexOfdelimiter + 1..line.len-1];//strcpy(wordOut[wCnt], delimiter + 1);
                wCnt++;
            // }
            // char* delimiter = strchr(line + 2, '>');
            // if (delimiter) {
            //     *delimiter = '\0';
            //     strcpy(wordIn[wCnt], line + 2);
            //     strcpy(wordOut[wCnt], delimiter + 1);
            //     wCnt++;
            // }
        }
    }

     //fclose(file);
}


fn String swapWords(String input) {
    if (input == "") return "";


    // DString output;
    String output;
    // output.temp_init();
    String[] words = input.tsplit(" ");
    String temp = "";
 

    foreach (j, word: words) {
        String result = word;
        bool swapped = false;

        foreach (i, swap_word: word_in) {
            if (word == word_in[i]) {
                result = word_out[i];
                break;
            }
        }

        output = String.tconcat(output, result);
        // if (j < words.len - 1) output.append(" ");
        if (j < words.len - 1) output = String.tconcat(output, " ");
    }


    // return output.str_view();
    return output;
}



fn String processReply(String reply, String userInput) {
    if (reply == "" || userInput == "") return reply;
    
    
    if (reply[reply.len - 1] == '*') {
        // If the reply doesn't end with an asterisk, don't swap words
        // return reply;
    // } else {
        reply = reply[0:reply.len-1];  // Remove the asterisk
        // reply = reply[0..reply.len-2];
    }  
    String keyword = "";
    for (int i = 0; i < g_Key_ReplyCount; i++) {
        for (int j = 0; j < g_Key_Reply[i].keywordCount; j++) {
            if (userInput.contains(g_Key_Reply[i].keywords[j])) {
                keyword = g_Key_Reply[i].keywords[j];
                break;
            }
        }
        if (keyword != "") break;
    }

    if (keyword != "") {
        int keywordIndex = (int)userInput.index_of(keyword)!!;
        String tail = userInput[keywordIndex + keyword.len..];
        String swappedTail = swapWords(tail);

        if (swappedTail != "") {
            reply = String.tconcat(reply, " ");
            reply = String.tconcat(reply, swappedTail);
        }
    }
    // }

    return reply;
}


// fn String userQuestion(String txt) {
//     String[MAX_MATCHES] matchedReplies;
//     int matchCount = 0;
//     bool isMatch = false;   
//     String finalReply = "";

//     // Find matching replies
//     for (int i = 0; i < g_Key_ReplyCount && matchCount < MAX_MATCHES; i++) {
//         bool foundInGroup = false;
//         for (int j = 0; j < g_Key_Reply[i].keywordCount; j++) {
//             if (txt.contains(g_Key_Reply[i].keywords[j])) {
//                 if (!foundInGroup) {
//                     int replyIndex = random::rand(g_Key_Reply[i].replyCount);
//                     matchedReplies[matchCount++] = g_Key_Reply[i].replies[replyIndex];
//                     foundInGroup = true;
//                     isMatch = true;
//                 }
//             }
//         }
//     }

//     // Process replies
//     if (matchCount > 0 && isMatch && matchCount <= MAX_MATCHES) {
//         foreach (reply : matchedReplies[0..matchCount]) {
//             String processed = processReply(reply, txt);
//             if (processed != "") {
//                 if (finalReply != "") {
//                     finalReply = String.tconcat(finalReply, " ");
//                 }
//                 finalReply = String.tconcat(finalReply, processed);
//             }
//         }
//         return finalReply.trim();
//     }

//     // Default reply
//     return default_replies[random::rand(default_reply_count)];
// }


fn String userQuestion(String txt) {
    String response = "I don't understand. Can you rephrase that?";
    String[MAX_MATCHES] matchedReplies;
    int matchCount = 0;
    bool isMatch = false;   
    String combinedReply = "";

    for (int i = 0; i < g_Key_ReplyCount && matchCount < MAX_MATCHES; i++) {
        for (int j = 0; j < g_Key_Reply[i].keywordCount; j++) {
            if (txt.contains(g_Key_Reply[i].keywords[j])) {
                int replyIndex = random::rand(g_Key_Reply[i].replyCount);
                matchedReplies[matchCount++] = g_Key_Reply[i].replies[replyIndex];
                isMatch = true;
                break;
            }
        }
    }

    if (matchCount > 0 && isMatch && matchCount <= MAX_MATCHES) {
        
        for (int i = 0 ; i < matchCount ; i++) {
            String reply = matchedReplies[i];
             if (reply[reply.len-1] == '*') {
            combinedReply = String.tconcat(combinedReply, processReply(reply, txt));
            } else {
                combinedReply = String.tconcat(combinedReply, reply);
            }
            combinedReply = String.tconcat(combinedReply, " ");
            
        }
    response = combinedReply;
    } else if (!isMatch) {
        int defaultIndex = random::rand(default_reply_count);
        response = default_replies[defaultIndex];
    }

    return response;
}





fn String commands(String txt) {
    // String isolated = isolatePunctuation(txt);
    // for (int i = 0; isolated[i]; i++) {
    //     isolated[i] = tolower(isolated[i]);
    // }
    String text = string::tformat(" %s ", txt);
    String response = userQuestion(text);
    
    // free(isolated);
    return response;
}

fn void speak(String text) {
    String command;
    command = "voice -r -1 -n \"Microsoft David Desktop\" ";
    String text2 = string::tformat("\"%s\"",text);
    // command = string::tformat("%s \"%s\"", command, text2);
    command = String.tconcat(command, text2);
    // command = String.tconcat(text2, "\"");
    // String.tconcat(command, text2);
    libc::system((ZString)command);
}

fn int main() {
    libc::srand((uint)libc::time(null));
    
    loadArrays("./database.txt");
    // loadSwapWords("./swapwords.txt");

    // for (int i = 0; i < wCnt; i++) {
    //     io::printfn("wordIn[%d] = %s, wordOut[%d] = %s\n", i, wordIn[i], i, wordOut[i]);
    // }
    
    io::printfn("CHATBOT DANNY IN C3 PROGRAMMING LANGUAGE VERSION 1.1.6.5 (C) RON77\n");
    
    String input;
    String response;
    
    while (true) {
        io::printf("> ");
        //if (fgets(input, sizeof(input), stdin) == NULL) {
        input = io::treadline()!!; 
            
        
        // input[strcspn(input, "\n")] = 0;  // Remove newline
        
        if (input == "quit") break;
        
        response = commands(input);
        
        if (response != "") {
            // Split the response into separate lines and speak each one
            String line = response;
            while (line != "") {
                io::printn(line);
                io::printn("\n");
                speak(line);
                line = "";
            }
            
            // free(response);
        } else {
            io::printfn("An error occurred while processing your input.\n");
        }
    }
    
    return 0;
}

The database.txt can be found here, as well as the 'voice.exe' TTS cli engine for Windows...
https://retrocoders.phatcode.net/index.php?topic=840.0