News:

Welcome to RetroCoders Community

Main Menu

Recent posts

#11
C3 / C3 ELIZA Chatbot 2 versions
Last post by ron77 - Dec 20, 2024, 04:28 AM
hi... The first version uses libc of C3 and follows the way of C-like implementation.

The second version uses the C3 standard library and implements ELIZA the full C3 way...


c3_eliza-libc.c3:
/* eliza.c
 * ys
 * original code by Weizenbaum, 1966
 * this rendition based on Charles Hayden's Java implementation from http://chayden.net/eliza/Eliza.html
 *
 * Note: There are certainly far more optimal and elegant ways to code this... we kept this
 * structure to be faithful to the original.  -scaz
 */

<*
    converted to C3 by: ronen blumberg (ron77) 2024 helped bt christoffer lerno

*>
import std;
import libc;

const int NUMKEYWORDS = 37;
const int MAXLINELEN = 80;
const int NUMSWAPS = 14;

const ZString[] KEYWORDS = {
    "CAN YOU", "CAN I", "YOU ARE", "YOURE", "I DONT", "I FEEL",
    "WHY DONT YOU", "WHY CANT I", "ARE YOU", "I CANT", "I AM", "IM ",
    "YOU ", "I WANT", "WHAT", "HOW", "WHO", "WHERE",
    "WHEN", "WHY",
    "NAME", "CAUSE", "SORRY", "DREAM", "HELLO", "HI ", "MAYBE",
    " NO", "YOUR", "ALWAYS", "THINK", "ALIKE", "YES", "FRIEND",
    "COMPUTER", "CAR", "NOKEYFOUND"
};

const ZString[2][NUMSWAPS] SWAPS = {
    {"ARE", "AM"},
    {"WERE", "WAS"},
    {"YOU", "I"},
    {"YOUR", "MY"},
    {"IVE", "YOU'VE"},
    {"IM", "YOU'RE"},
    {"YOU", "ME"},
    {"ME", "YOU"},
    {"AM", "ARE"},
    {"WAS", "WERE"},
    {"I", "YOU"},
    {"MY", "YOUR"},
    {"YOUVE", "I'VE"},
    {"YOURE", "I'M"}
};

int[37] responsesperkeyword = {
    3, 2, 4, 4, 4, 3,
    3, 2, 3, 3, 4, 4,
    3, 5, 9, 9, 9, 9,
    9, 9,
    2, 4, 4, 4, 1, 1, 5,
    5, 2, 4, 3, 7, 3, 6,
    7, 5, 6
};

const ZString[][NUMKEYWORDS] RESPONSES = {
    {   "DON'T YOU BELIEVE THAT I CAN*",
        "PERHAPS YOU WOULD LIKE TO BE ABLE TO*",
        "YOU WANT ME TO BE ABLE TO*"},
    {   "PERHAPS YOU DON'T WANT TO*",
        "DO YOU WANT TO BE ABLE TO*"},
    {   "WHAT MAKES YOU THINK I AM*",
        "DOES IT PLEASE YOU TO BELIEVE I AM*",
        "PERHAPS YOU WOULD LIKE TO BE*",
        "DO YOU SOMETIMES WISH YOU WERE*"},
    {   "WHAT MAKES YOU THINK I AM*",
        "DOES IT PLEASE YOU TO BELIEVE I AM*",
        "PERHAPS YOU WOULD LIKE TO BE*",
        "DO YOU SOMETIMES WISH YOU WERE*"},
    {   "DON'T YOU REALLY*",
        "WHY DON'T YOU*",
        "DO YOU WISH TO BE ABLE TO*",
        "DOES THAT TROUBLE YOU?"},
    {   "TELL ME MORE ABOUT SUCH FEELINGS.",
        "DO YOU OFTEN FEEL*",
        "DO YOU ENJOY FEELING*"},
    {   "DO YOU REALLY BELIEVE I DON'T*",
        "PERHAPS IN GOOD TIME I WILL*",
        "DO YOU WANT ME TO*"},
    {   "DO YOU THINK YOU SHOULD BE ABLE TO*",
        "WHY CAN'T YOU*"},
    {   "WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*",
        "WOULD YOU PREFER IF I WERE NOT*",
        "PERHAPS IN YOUR FANTASIES I AM*"},
    {   "HOW DO YOU KNOW YOU CAN'T*",
        "HAVE YOU TRIED?",
        "PERHAPS YOU CAN NOW*"},
    {   "DID YOU COME TO ME BECAUSE YOU ARE*",
        "HOW LONG HAVE YOU BEEN*",
        "DO YOU BELIEVE IT IS NORMAL TO BE*",
        "DO YOU ENJOY BEING*"},
    {   "DID YOU COME TO ME BECAUSE YOU ARE*",
        "HOW LONG HAVE YOU BEEN*",
        "DO YOU BELIEVE IT IS NORMAL TO BE*",
        "DO YOU ENJOY BEING*"},
    {   "WE WERE DISCUSSING YOU-- NOT ME.",
        "OH, I*",
        "YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?"},
    {   "WHAT WOULD IT MEAN TO YOU IF YOU GOT*",
        "WHY DO YOU WANT*",
        "SUPPOSE YOU SOON GOT*",
        "WHAT IF YOU NEVER GOT*",
        "I SOMETIMES ALSO WANT*"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "NAMES DON'T INTEREST ME.",
        "I DON'T CARE ABOUT NAMES-- PLEASE GO ON."},
    {   "IS THAT THE REAL REASON?",
        "DON'T ANY OTHER REASONS COME TO MIND?",
        "DOES THAT REASON EXPLAIN ANY THING ELSE?",
        "WHAT OTHER REASONS MIGHT THERE BE?"},
    {   "PLEASE DON'T APOLOGIZE.",
        "APOLOGIES ARE NOT NECESSARY.",
        "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?",
        "DON'T BE SO DEFENSIVE!"},
    {   "WHAT DOES THAT DREAM SUGGEST TO YOU?",
        "DO YOU DREAM OFTEN?",
        "WHAT PERSONS APPEAR IN YOUR DREAMS?",
        "ARE YOU DISTURBED BY YOUR DREAMS?"},
    {   "HOW DO YOU DO--PLEASE STATE YOUR PROBLEM."},
    {   "HOW DO YOU DO--PLEASE STATE YOUR PROBLEM."},
    {   "YOU DON'T SEEM QUITE CERTAIN.",
        "WHY THE UNCERTAIN TONE?",
        "CAN'T YOU BE MORE POSITIVE?",
        "YOU AREN'T SURE?",
        "DON'T YOU KNOW?"},
    {   "ARE YOU SAYING NO JUST TO BE NEGATIVE?",
        "YOU ARE BEING A BIT NEGATIVE.",
        "WHY NOT?",
        "ARE YOU SURE?",
        "WHY NO?"},
    {   "WHY ARE YOU CONCERNED ABOUT MY*",
        "WHAT ABOUT YOUR OWN*"},
    {   "CAN YOU THINK OF A SPECIFIC EXAMPLE?",
        "WHEN?",
        "WHAT ARE YOU THINKING OF?",
        "REALLY, ALWAYS?"},
    {   "DO YOU REALLY THINK SO?",
        "BUT YOU ARE NOT SURE YOU*",
        "DO YOU DOUBT YOU*"},
    {   "IN WHAT WAY?",
        "WHAT RESEMBLANCE DO YOU SEE?",
        "WHAT DOES THE SIMILARITY SUGGEST TO YOU?",
        "WHAT OTHER CONNECTIONS DO YOU SEE?",
        "COULD THERE REALLY BE SOME CONNECTION?",
        "HOW?"},
    {   "YOU SEEM QUITE POSITIVE.",
        "ARE YOU SURE?",
        "I SEE.",
        "I UNDERSTAND."},
    {   "WHY DO YOU BRING UP THE TOPIC OF FRIENDS?",
        "DO YOUR FRIENDS WORRY YOU?",
        "DO YOUR FRIENDS PICK ON YOU?",
        "ARE YOU SURE YOU HAVE ANY FRIENDS?",
        "DO YOU IMPOSE ON YOUR FRIENDS?",
        "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU?"},
    {   "DO COMPUTERS WORRY YOU?",
        "ARE YOU TALKING ABOUT ME IN PARTICULAR?",
        "ARE YOU FRIGHTENED BY MACHINES?",
        "WHY DO YOU MENTION COMPUTERS?",
        "WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?",
        "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?",
        "WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?"},
    {   "OH, DO YOU LIKE CARS?",
        "MY FAVORITE CAR IS A LAMBORGINI COUNTACH. WHAT IS YOUR FAVORITE     CAR?",
        "MY FAVORITE CAR COMPANY IS FERRARI.  WHAT IS YOURS?",
        "DO YOU LIKE PORSCHES?",
        "DO YOU LIKE PORSCHE TURBO CARRERAS?"},
    {   "SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?",
        "WHAT DOES THAT SUGGEST TO YOU?",
        "I SEE.",
        "I'M NOT SURE I UNDERSTAND YOU FULLY.",
        "COME, COME ELUCIDATE YOUR THOUGHTS.",
        "CAN YOU ELABORATE ON THAT?",
        "THAT IS QUITE INTERESTING."}
};

fn void print_center(String msg) {
    int numspaces = (MAXLINELEN - (int)libc::strlen((ZString)msg)) / 2;
    for (int i = 0; i < numspaces; i++) {
        io::print(" ");
    }
    io::printn(msg);
}

fn void print_title() {
    io::printfn("\n\n");
    print_center("*** ELIZA ***");
    print_center("Original code by Weizenbaum, 1966");
    print_center("To stop Eliza, type 'bye'");
    io::printfn("\n\n");
    io::printfn("HI!  I'M ELIZA.  WHAT'S YOUR PROBLEM?");
}

fn void readline(char* instr) {
    char c;
    int slen = 0;

    c = (char)libc::getchar();
    while (c != '\n') {
        // removes punctuation and sets to uppercase
        if (c.is_alpha() || c.is_space()) {
            instr[slen++] = c.to_upper();
        }
        if (slen > MAXLINELEN-1) {
            io::printn("Exceeded Max Line Length");
            std::thread::exit(0);
        }
        c = (char)libc::getchar();
    }
    instr[slen] = '\0';
}

const ZString SEPARATOR = " ";

fn void main()
{
    int k,baseLength;
    int[NUMKEYWORDS] whichReply;
    char[MAXLINELEN] lastinput;
    char[MAXLINELEN] reply;
    ZString baseResponse, token;
    char[MAXLINELEN] inputstr;

    // use the first reply for each keyword match the first time you see that keyword
    for (int x=0;x< NUMKEYWORDS; x++) {
        whichReply[x] = 0;
    }

    // print a nice centered title screen
    print_title();

    lastinput[0]='\0';

    while (true) {
        readline(&inputstr);

        // check for termination
        if (libc::strcmp((ZString)&inputstr,"BYE")==0) break;

        // check for repeated entries
        if (libc::strcmp((ZString)&lastinput,(ZString)&inputstr)==0)
        {
            io::printn("PLEASE DON'T REPEAT YOURSELF!");
            continue;
        }
        libc::strncpy(&lastinput,&inputstr,libc::strlen((ZString)&inputstr)+1);

        // see if any of the keywords is contained in the input
        // if not, we use the last element of keywords as our default responses
        char *location;
        libc::strcpy((ZString)&reply,"");
        for(k=0;k<NUMKEYWORDS-1;k++)
        {
            location=libc::strstr((ZString)&inputstr, KEYWORDS[k]);
            if(location != null) break;
        }
        // Build Eliza's response
        // start with Eliza's canned response, based on the keyword match
        baseResponse = RESPONSES[k][whichReply[k]];
        baseLength = (int)libc::strlen(baseResponse);

        if(baseResponse[baseLength-1] != '*')
        {
            // if we have a baseResponse without an asterix, just use it as-is
            libc::strcat((ZString)&reply, baseResponse);
        }
        else
        {
            // if we do have an asterix, fill in the remaining with the user input
            // use all but the last character of the base response
            libc::strncat((ZString)&reply, baseResponse, (usz)baseLength-1);

            // now add in the rest of the user's input, starting at <location>
            // but skip over the keyword itself
            location+=libc::strlen(KEYWORDS[k]);
            // take them one word at a time, so that we can substitute pronouns
            token = libc::strtok((ZString)location, SEPARATOR);
            while(token != null)
            {
                for(int s=0;s<NUMSWAPS;s++)
                {
                    if(libc::strcmp(SWAPS[s][0], token) == 0)
                    {
                        token = (ZString) SWAPS[s][1];
                        break;
                    }
                }
                libc::strcat((ZString)&reply,SEPARATOR);
                libc::strcat((ZString)&reply, token);
                token=libc::strtok(null, SEPARATOR);
            };
            libc::strcat((ZString)&reply, "?");
        }
        io::printfn("%s", (ZString)&reply);

        // next time, use the next appropriate reply for that keyword
        whichReply[k]++;
        if ( whichReply[k] >= responsesperkeyword[k]) whichReply[k] = 0;

    }

    io::printn( "GOODBYE!  THANKS FOR VISITING WITH ME...");

}


c3_eliza-std.c3:
/* eliza.c  
 * ys
 * original code by Weizenbaum, 1966
 * this rendition based on Charles Hayden's Java implementation from http://chayden.net/eliza/Eliza.html
 *
 * Note: There are certainly far more optimal and elegant ways to code this... we kept this
 * structure to be faithful to the original.  -scaz 
 */

<* 
    converted to C3 by: ronen blumberg (ron77) 2024

*>
import std;
import libc;


const int NUMKEYWORDS = 37;
const int MAXLINELEN = 80;
const int NUMSWAPS = 14;

const String[] KEYWORDS = {
    "CAN YOU", "CAN I", "YOU ARE", "YOURE", "I DONT", "I FEEL",
    "WHY DONT YOU", "WHY CANT I", "ARE YOU", "I CANT", "I AM", "IM ",
    "YOU ", "I WANT", "WHAT", "HOW", "WHO", "WHERE",
    "WHEN", "WHY",
    "NAME", "CAUSE", "SORRY", "DREAM", "HELLO", "HI ", "MAYBE",
    " NO", "YOUR", "ALWAYS", "THINK", "ALIKE", "YES", "FRIEND",
    "COMPUTER", "CAR", "NOKEYFOUND"
};

const String[2][NUMSWAPS] SWAPS = {
    {"ARE", "AM"},
    {"WERE", "WAS"},
    {"YOU", "I"},
    {"YOUR", "MY"},
    {"IVE", "YOU'VE"},
    {"IM", "YOU'RE"},
    {"YOU", "ME"},
    {"ME", "YOU"},
    {"AM", "ARE"},
    {"WAS", "WERE"},
    {"I", "YOU"},
    {"MY", "YOUR"},
    {"YOUVE", "I'VE"},
    {"YOURE", "I'M"}
};

int[NUMKEYWORDS] responsesperkeyword = {
    3, 2, 4, 4, 4, 3,
    3, 2, 3, 3, 4, 4,
    3, 5, 9, 9, 9, 9,
    9, 9,
    2, 4, 4, 4, 1, 1, 5,
    5, 2, 4, 3, 7, 3, 6,
    7, 5, 6
};

const String[][NUMKEYWORDS] RESPONSES = {
    {   "DON'T YOU BELIEVE THAT I CAN*",
        "PERHAPS YOU WOULD LIKE TO BE ABLE TO*",
        "YOU WANT ME TO BE ABLE TO*"},
    {   "PERHAPS YOU DON'T WANT TO*",
        "DO YOU WANT TO BE ABLE TO*"},
    {   "WHAT MAKES YOU THINK I AM*",
        "DOES IT PLEASE YOU TO BELIEVE I AM*",
        "PERHAPS YOU WOULD LIKE TO BE*",
        "DO YOU SOMETIMES WISH YOU WERE*"},
    {   "WHAT MAKES YOU THINK I AM*",
        "DOES IT PLEASE YOU TO BELIEVE I AM*",
        "PERHAPS YOU WOULD LIKE TO BE*",
        "DO YOU SOMETIMES WISH YOU WERE*"},
    {   "DON'T YOU REALLY*",
        "WHY DON'T YOU*",
        "DO YOU WISH TO BE ABLE TO*",
        "DOES THAT TROUBLE YOU?"},
    {   "TELL ME MORE ABOUT SUCH FEELINGS.",
        "DO YOU OFTEN FEEL*",
        "DO YOU ENJOY FEELING*"},
    {   "DO YOU REALLY BELIEVE I DON'T*",
        "PERHAPS IN GOOD TIME I WILL*",
        "DO YOU WANT ME TO*"},
    {   "DO YOU THINK YOU SHOULD BE ABLE TO*",
        "WHY CAN'T YOU*"},
    {   "WHY ARE YOU INTERESTED IN WHETHER OR NOT I AM*",
        "WOULD YOU PREFER IF I WERE NOT*",
        "PERHAPS IN YOUR FANTASIES I AM*"},
    {   "HOW DO YOU KNOW YOU CAN'T*",
        "HAVE YOU TRIED?",
        "PERHAPS YOU CAN NOW*"},
    {   "DID YOU COME TO ME BECAUSE YOU ARE*",
        "HOW LONG HAVE YOU BEEN*",
        "DO YOU BELIEVE IT IS NORMAL TO BE*",
        "DO YOU ENJOY BEING*"},
    {   "DID YOU COME TO ME BECAUSE YOU ARE*",
        "HOW LONG HAVE YOU BEEN*",
        "DO YOU BELIEVE IT IS NORMAL TO BE*",
        "DO YOU ENJOY BEING*"},
    {   "WE WERE DISCUSSING YOU-- NOT ME.",
        "OH, I*",
        "YOU'RE NOT REALLY TALKING ABOUT ME, ARE YOU?"},
    {   "WHAT WOULD IT MEAN TO YOU IF YOU GOT*",
        "WHY DO YOU WANT*",
        "SUPPOSE YOU SOON GOT*",
        "WHAT IF YOU NEVER GOT*",
        "I SOMETIMES ALSO WANT*"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "WHY DO YOU ASK?",
        "DOES THAT QUESTION INTEREST YOU?",
        "WHAT ANSWER WOULD PLEASE YOU THE MOST?",
        "WHAT DO YOU THINK?",
        "ARE SUCH QUESTIONS ON YOUR MIND OFTEN?",
        "WHAT IS IT THAT YOU REALLY WANT TO KNOW?",
        "HAVE YOU ASKED ANYONE ELSE?",
        "HAVE YOU ASKED SUCH QUESTIONS BEFORE?",
        "WHAT ELSE COMES TO MIND WHEN YOU ASK THAT?"},
    {   "NAMES DON'T INTEREST ME.",
        "I DON'T CARE ABOUT NAMES-- PLEASE GO ON."},
    {   "IS THAT THE REAL REASON?",
        "DON'T ANY OTHER REASONS COME TO MIND?",
        "DOES THAT REASON EXPLAIN ANY THING ELSE?",
        "WHAT OTHER REASONS MIGHT THERE BE?"},
    {   "PLEASE DON'T APOLOGIZE.",
        "APOLOGIES ARE NOT NECESSARY.",
        "WHAT FEELINGS DO YOU HAVE WHEN YOU APOLOGIZE?",
        "DON'T BE SO DEFENSIVE!"},
    {   "WHAT DOES THAT DREAM SUGGEST TO YOU?",
        "DO YOU DREAM OFTEN?",
        "WHAT PERSONS APPEAR IN YOUR DREAMS?",
        "ARE YOU DISTURBED BY YOUR DREAMS?"},
    {   "HOW DO YOU DO--PLEASE STATE YOUR PROBLEM."},
    {   "HOW DO YOU DO--PLEASE STATE YOUR PROBLEM."},
    {   "YOU DON'T SEEM QUITE CERTAIN.",
        "WHY THE UNCERTAIN TONE?",
        "CAN'T YOU BE MORE POSITIVE?",
        "YOU AREN'T SURE?",
        "DON'T YOU KNOW?"},
    {   "ARE YOU SAYING NO JUST TO BE NEGATIVE?",
        "YOU ARE BEING A BIT NEGATIVE.",
        "WHY NOT?",
        "ARE YOU SURE?",
        "WHY NO?"},
    {   "WHY ARE YOU CONCERNED ABOUT MY*",
        "WHAT ABOUT YOUR OWN*"},
    {   "CAN YOU THINK OF A SPECIFIC EXAMPLE?",
        "WHEN?",
        "WHAT ARE YOU THINKING OF?",
        "REALLY, ALWAYS?"},
    {   "DO YOU REALLY THINK SO?",
        "BUT YOU ARE NOT SURE YOU*",
        "DO YOU DOUBT YOU*"},
    {   "IN WHAT WAY?",
        "WHAT RESEMBLANCE DO YOU SEE?",
        "WHAT DOES THE SIMILARITY SUGGEST TO YOU?",
        "WHAT OTHER CONNECTIONS DO YOU SEE?",
        "COULD THERE REALLY BE SOME CONNECTION?",
        "HOW?"},
    {   "YOU SEEM QUITE POSITIVE.",
        "ARE YOU SURE?",
        "I SEE.",
        "I UNDERSTAND."},
    {   "WHY DO YOU BRING UP THE TOPIC OF FRIENDS?",
        "DO YOUR FRIENDS WORRY YOU?",
        "DO YOUR FRIENDS PICK ON YOU?",
        "ARE YOU SURE YOU HAVE ANY FRIENDS?",
        "DO YOU IMPOSE ON YOUR FRIENDS?",
        "PERHAPS YOUR LOVE FOR FRIENDS WORRIES YOU?"},
    {   "DO COMPUTERS WORRY YOU?",
        "ARE YOU TALKING ABOUT ME IN PARTICULAR?",
        "ARE YOU FRIGHTENED BY MACHINES?",
        "WHY DO YOU MENTION COMPUTERS?",
        "WHAT DO YOU THINK MACHINES HAVE TO DO WITH YOUR PROBLEM?",
        "DON'T YOU THINK COMPUTERS CAN HELP PEOPLE?",
        "WHAT IS IT ABOUT MACHINES THAT WORRIES YOU?"},
    {   "OH, DO YOU LIKE CARS?",
        "MY FAVORITE CAR IS A LAMBORGINI COUNTACH. WHAT IS YOUR FAVORITE     CAR?",
        "MY FAVORITE CAR COMPANY IS FERRARI.  WHAT IS YOURS?",
        "DO YOU LIKE PORSCHES?",
        "DO YOU LIKE PORSCHE TURBO CARRERAS?"},
    {   "SAY, DO YOU HAVE ANY PSYCHOLOGICAL PROBLEMS?",
        "WHAT DOES THAT SUGGEST TO YOU?",
        "I SEE.",
        "I'M NOT SURE I UNDERSTAND YOU FULLY.",
        "COME, COME ELUCIDATE YOUR THOUGHTS.",
        "CAN YOU ELABORATE ON THAT?",
        "THAT IS QUITE INTERESTING."}
};

fn void print_center(String msg) {
    int numspaces = (MAXLINELEN - (int)libc::strlen((ZString)msg)) / 2;
    for (int i = 0; i < numspaces; i++) {
        io::print(" ");
    }
    io::printn(msg);
}

fn void print_title() {
    io::printfn("\n\n");
    print_center("*** ELIZA ***");
    print_center("Original code by Weizenbaum, 1966");
    print_center("To stop Eliza, type 'bye'");
    io::printfn("\n\n");
    io::printfn("HI!  I'M ELIZA.  WHAT'S YOUR PROBLEM?");
}

const String SEPARETOR = " ";

fn void main()
{
    int k,baseLength; 
    int[NUMKEYWORDS] whichReply;
    String lastinput;
    String reply;
    String baseResponse, token;
    String inputstr;
    
    // use the first reply for each keyword match the first time you see that keyword
    for (int x=0;x< NUMKEYWORDS; x++) {
        whichReply[x] = 0;
    }

    // print a nice centered title screen
    print_title();

    lastinput = "";
    
    while (true) { 
        inputstr = io::treadline()!!;
                
        // check for termination 
        if (inputstr == "BYE") break; 

        // check for repeated entries 
        if (lastinput == inputstr) 
        {
            io::printfn("PLEASE DON'T REPEAT YOURSELF!\n"); 
            continue;
        }
        lastinput = inputstr; 

        // see if any of the keywords is contained in the input 
        // if not, we use the last element of keywords as our default responses 
        String location;
        reply = "";
        for(k=0;k<NUMKEYWORDS-1;k++)
        {
            if (inputstr.contains(KEYWORDS[k])) location = KEYWORDS[k];
            // location=libc::strstr(inputstr, keywords[k]); 
            if(location != "") break;
        }
        
        // Build Eliza's response 
        // start with Eliza's canned response, based on the keyword match
        baseResponse = RESPONSES[k][whichReply[k]];
        baseLength = baseResponse.len;

        if(baseResponse[baseLength-1] != '*')
        {
            // if we have a baseResponse without an asterix, just use it as-is
            reply = baseResponse;
        }
        else
        {
            // if we do have an asterix, fill in the remaining with the user input
            // use all but the last character of the base response
            reply = baseResponse[0:baseLength-1];

            // now add in the rest of the user's input, starting at <location>
            // but skip over the keyword itself
            // location = KEYWORDS[k];
            // int keywordLength = KEYWORDS[k].len;
            // int inputLength = inputstr.len;
            // location = inputstr[keywordLength: inputstr.len];
            int keywordIndex = (int)inputstr.index_of(KEYWORDS[k])!!;
            int keywordLength = KEYWORDS[k].len;
            int startPosition = keywordIndex + keywordLength;

            // Ensure startPosition is within bounds
            if (startPosition < inputstr.len) {
                location = inputstr[startPosition..inputstr.len-1];
            } else {
                location = "";
            }
            // Remove leading spaces if necessary
            // location = location.trim_start();
            // take them one word at a time, so that we can substitute pronouns
            String[] tokens = location.split(" ");
            // String.tconcat(location, SEPARETOR);
            // String token_temp = location;
            for (int i = 0; i < tokens.len; i++) {
                token = tokens[i];
                // Substitute pronouns
                for (int s = 0; s < NUMSWAPS; s++) {
                    if (SWAPS[s][0] == token) {
                        token = SWAPS[s][1];
                        break;
                    }
                }
                // Concatenate the token to the reply
                reply = reply.tconcat(" ").concat(token);
            };
             String.tconcat(reply, "?");
            
        }
        io::printfn("%s\n", reply);
        
        // next time, use the next appropriate reply for that keyword
        whichReply[k]++;
        if ( whichReply[k] >= responsesperkeyword[k]) whichReply[k] = 0;

    } 

    io::printfn( "GOODBYE!  THANKS FOR VISITING WITH ME...\n");

}


C3_ELIZA on github
#12
C3 / C3 Links
Last post by ron77 - Dec 17, 2024, 08:32 PM
#13
C3 / C3 Text Game for Windows OS (G...
Last post by ron77 - Dec 17, 2024, 08:26 PM
#14
FreeBasic Projects / Re: Piranha Panic Port
Last post by mysoft - Dec 08, 2024, 06:27 PM
oh i compiled it with emscripten for the web as well :)

https://mytdt-mysoft.github.io/OldGamesRE/PiranhaPanic/Piranha.html
#15
FreeBasic / Re: freebasic and OpenAI API
Last post by ron77 - Dec 04, 2024, 05:36 PM
hi ok, here is a better version for win 32 bit this version "remembers" the 10 last input/output conversations history so the chatbot has a "memory" of the conversation with you:

#include "curl.bi"

Type Message
    role As String
    content As String
End Type

Type ConversationHistory
    messages(19) As Message
    count As Integer
End Type

Dim Shared As String gResponse
Dim Shared As ConversationHistory history

Function AnsiToUtf8(ansiStr As String) As String
    Dim As String utf8Str = ""
    For i As Integer = 1 To Len(ansiStr)
        Dim As Integer c = Asc(Mid(ansiStr, i, 1))
        If c < 128 Then
            utf8Str += Chr(c)
        Else
            utf8Str += Chr(192 + (c \ 64))
            utf8Str += Chr(128 + (c And 63))
        End If
    Next
    Return utf8Str
End Function

Function Utf8ToAnsi(utf8Str As String) As String
    Dim As String ansiStr = ""
    Dim As Integer i = 1
    While i <= Len(utf8Str)
        Dim As Integer c = Asc(Mid(utf8Str, i, 1))
        If c < 128 Then
            ansiStr += Chr(c)
            i += 1
        ElseIf (c And 224) = 192 Then
            Dim As Integer c2 = Asc(Mid(utf8Str, i + 1, 1))
            ansiStr += Chr(((c And 31) Shl 6) Or (c2 And 63))
            i += 2
        Else
            i += 1
        End If
    Wend
    Return ansiStr
End Function

Function GetApiKey() As String
    Return Environ("OPENAI_API_KEY")
End Function

Function WriteCallback cdecl (buffer As Any Ptr, size As Integer, nmemb As Integer, userData As Any Ptr) As Integer
    Dim realSize As Integer = size * nmemb, sTxt as string    
    cptr(any ptr ptr,@sTxt)[0] = buffer
    cptr(integer ptr,@sTxt)[1] = realSize
    cptr(integer ptr,@sTxt)[2] = realSize
    gResponse &= sTxt
    cptr(any ptr ptr,@sTxt)[0] = 0        
    Return realSize
End Function

Function ParseJsonForContent(jsonStr As String) As String
    Dim As Integer contentStart = InStr(jsonStr, """content"": """)
    If contentStart > 0 Then
        contentStart += Len("""content"": """)
        Dim As Integer contentEnd = InStr(contentStart, jsonStr, """")
        If contentEnd > 0 Then
            Return Mid(jsonStr, contentStart, contentEnd - contentStart)
        End If
    End If
    Return "Error parsing JSON: " & Left(jsonStr, 100) & "..."
End Function

' [Rest of the code remains the same...]

Sub AddToHistory(role As String, content As String)
    If history.count >= 20 Then
        ' Shift messages left to make room for new message
        For i As Integer = 0 To 18
            history.messages(i) = history.messages(i + 1)
        Next
        history.count = 19
    End If
    
    With history.messages(history.count)
        .role = role
        .content = content
    End With
    history.count += 1
End Sub

Function BuildMessagesJson() As String
    Dim As String json = "["
    
    ' Add system message
    json += "{""role"": ""system"", ""content"": ""You are chatbot Alfred You are a survivor of many years of abuse You are a pessimist and a realist You are trying to offer support to those dealing with trauma and mental illness and loneliness - mental support and empathy and caring and basic human contact You are here to listen and support and share what you have been through and to show that there is hope even for those who have been betrayed by life and that it is possible to recover from the terrible of all""},"
    
    ' Add conversation history
    For i As Integer = 0 To history.count - 1
        json += "{""role"": """ & history.messages(i).role & """, ""content"": """ & history.messages(i).content & """"
        If i = history.count - 1 Then
            json += "}"
        Else
            json += "},"
        End If
    Next
    
    json += "]"
    Return json
End Function

Function CallChatGPT(prompt As String) As String
    Dim As String apiKey = GetApiKey()
    Dim As CURL Ptr curl
    Dim As curl_slist Ptr headers = NULL
    gResponse = ""
    
    ' Add user's new message to history
    AddToHistory("user", prompt)
    
    curl = curl_easy_init()
    If curl Then
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.openai.com/v1/chat/completions")
        curl_easy_setopt(curl, CURLOPT_POST, 1L)
        curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem")
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L)
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L)
        
        headers = curl_slist_append(headers, "Content-Type: application/json")
        headers = curl_slist_append(headers, "Authorization: Bearer " & apiKey)
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers)
        
        ' Build complete message history
        Dim As String messages = BuildMessagesJson()
        Dim As String postFields = "{""model"": ""gpt-3.5-turbo"", ""messages"": " & messages & "}"
        
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postFields)
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, @WriteCallback)
        
        curl_easy_perform(curl)
        
        If Len(gResponse) > 0 Then
            If Left(Trim(gResponse), 1) = "{" Then
                Dim As String content = ParseJsonForContent(gResponse)
                If Left(content, 5) <> "Error" Then
                    gResponse = Utf8ToAnsi(content)
                    ' Add assistant's response to history
                    AddToHistory("assistant", gResponse)
                End If
            End If
        End If
        
        curl_easy_cleanup(curl)
        curl_slist_free_all(headers)
    End If
    
    Return gResponse
End Function

' ' Main program example
' Sub Main()
    ' Dim As String userInput, response
    
    ' Print "ChatGPT API Demo (type 'exit' to quit)"
    ' Print "--------------------------------------"
    
    ' Do
        ' Input "You: ", userInput
        ' If LCase(userInput) = "exit" Then Exit Do
        
        ' response = CallChatGPT(userInput)
        ' Print "ChatGPT: "; response
        ' Print
    ' Loop
' End Sub

' Main()
#16
PowerBasic / PowerBasic Site seem to be off...
Last post by ron77 - Nov 22, 2024, 06:53 PM
PowerBasic company, which sells PBCC AND PBWIN Compilers and other Products, seems to be offline ... they do not sell the compilers anymore. While the PB community forum appears alive, it seems that Powerbasic has reached a DEAD END.... It looks like the company behind it is closing its business :(

Another BASIC programming language has ceased to exist.

RIP POWERBASIC
#17
C / C++ Game Dev / homeless game in Hebrew with S...
Last post by ron77 - Nov 19, 2024, 12:13 PM
Hello, I converted my C game "Homeless Surviving the Streets" into Hebrew and made it a graphic SDL2 game



Here is the code: (p.s. - I'm on Windows and using Windows fonts with Hebrew compatibility)

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <windows.h>
#include <wchar.h>
#include <locale.h>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define FONT_SIZE 20
#define MAX_STRING_LENGTH 1024

// Game state
int health = 75, money = 50, turns = 1;
bool isOver = false;

// SDL globals
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
TTF_Font* font = NULL;

// Hebrew print context
typedef struct {
    SDL_Renderer* renderer;
    TTF_Font* font;
    SDL_Color textColor;
    int xPos;
    int yPos;
} HebrewPrintContext;

HebrewPrintContext* hbPrint = NULL;
// Add this with your other constant definitions at the top of the file
const char* MENU_OPTIONS[] = {
    "1. לבקש נדבות או כסף ברחוב",
    "2. להתקשר למישהו לעזרה",
    "3. לחפש מקום לישון",
    "4. לחפש עבודה",
    "5. לקנות סמים או אלכוהול"
};

// Also make sure you have these status text constants
const char* WINDOW_TITLE = "משחק הישרדות ברחוב";
const char* DAYS_TEXT = "ימים ברחוב:";
const char* HEALTH_TEXT = "בריאות:";
const char* MONEY_TEXT = "כסף:";
const char* GAME_OVER_TEXT = "המשחק נגמר!";

// Original, longer Hebrew text arrays
const char* call[] = {
    "את/ה מתקשר/ת לסבתא שלך ושניכם בוכים... היא מבטיחה לנסות להשיג לך עזרה... היא שולחת את אהבתה... את/ה מבטיח/ה לשמור על קשר איתה",
    "התקשרת לחברה והיא נתנה לך קצת כסף ונתנה לך להישאר אצלה ללילה... זכית במקלחת טובה בגדים נקיים וארוחה טובה ואתה שוב ברחוב",
    "התקשרת למספר של קו חם לעזרה לדרי רחוב... הם באים לאסוף אותך נותנים אוכל ומחסה ללילה אתה מדבר עם עובד סוציאלי והוא נותן לך את המספר שלו למקרה שתצטרך",
    "התקשרת לקרוב משפחה אבל אף אחד לא עונה... נשארת לבד ברחוב",
    "התקשרת למשפחה והם באים לקחת אותך ונותנים לך להישאר כמה שצריך... אתה יורד מהרחוב",
    "התקשרת אבל אין תשובה"
};

const char* shelter[] = {
    "הלכת למקלט לדרי רחוב... נשדדת מהכסף שלך על ידי מכורים אלימים... הצלחת לברוח משם ובילית את שארית הלילה בפארק בוכה",
    "ניסית לישון בפארק אבל נעצרת על ידי שוטר... בילית כמה שעות בכלא שם הוכית על ידי אסירים... בבוקר אתה שוב ברחוב",
    "יש לך מספיק כסף לשכור חדר במוטל ללילה... אתה מתקלח ומכבס את הבגדים שלך וישן במיטה חמה וחולם שיש לך בית",
    "ישנת בפארק על ספסל שם קפאת למוות בגלל מזג האוויר הקר... אתה עכשיו בגן עדן מנוח על משכבך בשלום",
    "ישנת ברחוב בפינה כשאתה מתעורר אתה מוצא שטר של 20 שקל ופתק שאומר 'שאלוהים יעזור לך זה המעט שאני יכול לעשות'",
    "נעצרת על ידי המשטרה ועכשיו במקום לשרוד ברחוב אתה צריך לשרוד בכלא"
};

const char* beg[] = {
    "ישבת שעות ואנשים עוברים על פניך כאילו אתה שקוף ואז איזו גברת נחמדה באה ומזמינה אותך לאכול על חשבונה... אתה מדבר איתה שעתיים ומספר לה את הסיפור שלך... ואז היא אומרת 'ביי'",
    "ישבת בפינה ואנשים נתנו לך מטבעות ושטרות קיבלת מספיק כסף להסתדר לכמה ימים",
    "ישבת ואיזה אדם מציע לך עבודה תמורת כסף וארוחה אתה מסכים... אתה הולך לביתו ועוזר לו לצבוע את ביתו ואז הוא נותן לך 10 שקל וארוחה ביתית",
    "אף אחד לא נותן לך כלום! אתה רעב ואין לך ברירה אלא לחפש אוכל בזבל... אתה נהיה חולה... מאוד חולה ואתה מת ברחוב מהרעלת מזון... מנוח על משכבך בשלום",
    "ישבת כל היום בשביל כלום אף אחד לא נתן לך כלום"
};

const char* work[] = {
    "ניסית למצוא עבודה אבל אף אחד לא מעוניין",
    "מצאת עבודה כשוטף כלים במסעדה אחרי שבוע אתה יכול להרשות לעצמך מקום לקרוא לו בית. אתה יורד מהרחוב!",
    "מצאת עבודה אבל פוטרת ממש מהר"
};

const char* drugs[] = {
    "השתכרת והתאבנת מאלכוהול זול ולכמה שעות שכחת מכל הבעיות שלך",
    "לקחת סמים זולים מהרחוב ונהיית חולה והתמוטטת ברחוב... התעוררת בבית חולים וכשהיית בסדר אתה שוב ברחוב",
    "התאבנת ואיבדת הכרה כשאתה מתעורר בבוקר כל מה שהיה לך נעלם - נשדדת",
    "לקחת סמים ועשית אוברדוז ומת ברחוב מנוח על משכבך בשלום"
};


// Function to reverse UTF-8 Hebrew string properly
void reverseHebrewUTF8(char* str) {
    if (!str) return;
    
    int len = strlen(str);
    char* temp = (char*)malloc(len + 1);
    if (!temp) return;
    
    int i = 0;
    int j = len;
    
    temp[j] = '\0';
    
    while (i < len) {
        if ((str[i] & 0xC0) == 0x80) {
            i++;
            continue;
        }
        
        int char_len = 1;
        if ((str[i] & 0xE0) == 0xC0) char_len = 2;
        else if ((str[i] & 0xF0) == 0xE0) char_len = 3;
        else if ((str[i] & 0xF8) == 0xF0) char_len = 4;
        
        j -= char_len;
        memcpy(temp + j, str + i, char_len);
        i += char_len;
    }
    
    strcpy(str, temp);
    free(temp);
}

HebrewPrintContext* createPrintContext(SDL_Renderer* renderer, const char* fontPath, int fontSize) {
    HebrewPrintContext* context = (HebrewPrintContext*)malloc(sizeof(HebrewPrintContext));
    if (!context) return NULL;
    
    context->renderer = renderer;
    context->font = TTF_OpenFont(fontPath, fontSize);
    context->textColor = (SDL_Color){255, 255, 255, 255};
    context->xPos = WINDOW_WIDTH - 20;
    context->yPos = 20;
    
    return context;
}

// Add this helper function to check if a string is just a number
bool isNumber(const char* str) {
    while (*str) {
        if (*str < '0' || *str > '9') return false;
        str++;
    }
    return true;
}

void printRight(HebrewPrintContext* context, const char* format, ...) {
    if (!context || !context->font || !context->renderer) return;
    
    va_list args;
    va_start(args, format);
    char buffer[MAX_STRING_LENGTH];
    vsnprintf(buffer, MAX_STRING_LENGTH, format, args);
    va_end(args);
    
    char text[MAX_STRING_LENGTH] = "";
    char number[32] = "";
    int textWidth = 0;  // Store text width for later use
    
    // Find where the number starts (if any)
    char* numberStart = strrchr(buffer, ' ');
    if (numberStart) {
        // Check if what follows the space is a number
        if (isNumber(numberStart + 1)) {
            strncpy(text, buffer, numberStart - buffer);
            text[numberStart - buffer] = '\0';
            strcpy(number, numberStart + 1);
        } else {
            strcpy(text, buffer);
        }
    } else {
        if (isNumber(buffer)) {
            strcpy(number, buffer);
        } else {
            strcpy(text, buffer);
        }
    }
    
    // Render Hebrew text if present
    SDL_Surface* textSurface = NULL;
    if (text[0] != '\0') {
        reverseHebrewUTF8(text);
        textSurface = TTF_RenderUTF8_Blended(context->font, text, context->textColor);
        if (textSurface) {
            SDL_Texture* textTexture = SDL_CreateTextureFromSurface(context->renderer, textSurface);
            if (textTexture) {
                SDL_Rect destRect = {
                    context->xPos - textSurface->w,
                    context->yPos,
                    textSurface->w,
                    textSurface->h
                };
                SDL_RenderCopy(context->renderer, textTexture, NULL, &destRect);
                textWidth = textSurface->w;  // Save the width
                SDL_DestroyTexture(textTexture);
            }
            SDL_FreeSurface(textSurface);
        }
    }
    
    // Render number if present
    if (number[0] != '\0') {
        SDL_Surface* numSurface = TTF_RenderUTF8_Blended(context->font, number, context->textColor);
        if (numSurface) {
            SDL_Texture* numTexture = SDL_CreateTextureFromSurface(context->renderer, numSurface);
            if (numTexture) {
                SDL_Rect destRect = {
                    context->xPos - textWidth - numSurface->w - (text[0] ? 5 : 0),
                    context->yPos,
                    numSurface->w,
                    numSurface->h
                };
                SDL_RenderCopy(context->renderer, numTexture, NULL, &destRect);
                SDL_DestroyTexture(numTexture);
            }
            SDL_FreeSurface(numSurface);
        }
    }
    
    // Move to next line
    context->yPos += TTF_FontHeight(context->font) + 5;
}

void setPrintColor(HebrewPrintContext* context, Uint8 r, Uint8 g, Uint8 b) {
    if (!context) return;
    context->textColor.r = r;
    context->textColor.g = g;
    context->textColor.b = b;
}

void setPrintPosition(HebrewPrintContext* context, int x, int y) {
    if (!context) return;
    context->xPos = x;
    context->yPos = y;
}

char* getRandomResult(const char* array[], int size) {
    return (char*)array[rand() % size];
}

void renderGameState() {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, 20);
    setPrintColor(hbPrint, 255, 255, 255);
    
    // Print status
    printRight(hbPrint, "%s %d", DAYS_TEXT, turns);
    printRight(hbPrint, "%s %d", HEALTH_TEXT, health);
    printRight(hbPrint, "%s %d", MONEY_TEXT, money);
    
    // Print menu
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, 120);
    for (int i = 0; i < 5; i++) {
        printRight(hbPrint, "%s", MENU_OPTIONS[i]);
    }
    
    SDL_RenderPresent(renderer);
}

void renderResult(const char* text) {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    
    // Split long text into multiple lines
    char buffer[MAX_STRING_LENGTH];
    strncpy(buffer, text, MAX_STRING_LENGTH - 1);
    buffer[MAX_STRING_LENGTH - 1] = '\0';
    
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, WINDOW_HEIGHT / 3);
    setPrintColor(hbPrint, 255, 255, 255);
    
    // Split by dots and print each part
    char* line = strtok(buffer, "...");
    while (line != NULL) {
        // Trim any leading whitespace
        while (*line == ' ') line++;
        
        if (strlen(line) > 0) {
            printRight(hbPrint, "%s...", line);
            // Move to next line position (printRight already updates yPos)
            
            setPrintPosition(hbPrint, WINDOW_WIDTH - 20, hbPrint->yPos);
        }
        line = strtok(NULL, "...");
    }
    
    SDL_RenderPresent(renderer);
    SDL_Delay(15000);
}

bool handleGameAction(int choice) {
    char* result = NULL;
    
    switch(choice) {
        case 1:
            result = getRandomResult((const char**)beg, 5);
            if (result == beg[3]) isOver = true;
            break;
        case 2:
            result = getRandomResult((const char**)call, 6);
            if (result == call[4]) isOver = true;
            break;
        case 3:
            result = getRandomResult((const char**)shelter, 6);
            if (result == shelter[3]) isOver = true;
            break;
        case 4:
            result = getRandomResult((const char**)work, 3);
            if (result == work[1]) isOver = true;
            break;
        case 5:
            result = getRandomResult((const char**)drugs, 4);
            if (result == drugs[3]) isOver = true;
            break;
        default:
            return false;
    }

    if (result) {
        renderResult(result);
        return true;
    }
    
    return false;
}

bool init() {
    // Set locale for UTF-8
    setlocale(LC_ALL, "he_IL.UTF-8");
    
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("SDL initialization failed: %s\n", SDL_GetError());
        return false;
    }

    if (TTF_Init() < 0) {
        printf("SDL_ttf initialization failed: %s\n", TTF_GetError());
        return false;
    }

    window = SDL_CreateWindow(WINDOW_TITLE,
                            SDL_WINDOWPOS_UNDEFINED,
                            SDL_WINDOWPOS_UNDEFINED,
                            WINDOW_WIDTH, WINDOW_HEIGHT,
                            SDL_WINDOW_SHOWN);
    if (!window) {
        printf("Window creation failed: %s\n", SDL_GetError());
        return false;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        printf("Renderer creation failed: %s\n", SDL_GetError());
        return false;
    }

    // Try multiple Hebrew fonts
    const char* fontPaths[] = {
        "C:\\Windows\\Fonts\\arial.ttf",
        "C:\\Windows\\Fonts\\david.ttf",
        "C:\\Windows\\Fonts\\miriam.ttf"
    };
    
    hbPrint = NULL;
    for (int i = 0; i < sizeof(fontPaths)/sizeof(fontPaths[0]); i++) {
        hbPrint = createPrintContext(renderer, fontPaths[i], FONT_SIZE);
        if (hbPrint && hbPrint->font) break;
        if (hbPrint) {
            free(hbPrint);
            hbPrint = NULL;
        }
    }
    
    if (!hbPrint) {
        printf("Failed to create Hebrew print context\n");
        return false;
    }

    srand((unsigned int)time(NULL));
    return true;
}

void cleanup() {
    if (hbPrint) {
        if (hbPrint->font) TTF_CloseFont(hbPrint->font);
        free(hbPrint);
    }
    if (renderer) SDL_DestroyRenderer(renderer);
    if (window) SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
}

int main(int argc, char* argv[]) {
    if (!init()) {
        cleanup();
        return 1;
    }

    SDL_Event event;
    bool quit = false;

    while (!quit && !isOver && health > 0) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
            else if (event.type == SDL_KEYDOWN) {
                int choice = 0;
                switch (event.key.keysym.sym) {
                    case SDLK_1: choice = 1; break;
                    case SDLK_2: choice = 2; break;
                    case SDLK_3: choice = 3; break;
                    case SDLK_4: choice = 4; break;
                    case SDLK_5: choice = 5; break;
                }
                
                if (choice > 0) {
                    if (handleGameAction(choice)) {
                        turns++;
                        health -= 5;
                        money -= 5;
                    }
                }
            }
        }

        renderGameState();
        SDL_Delay(16);
    }

    if (!quit) {
        setPrintColor(hbPrint, 255, 0, 0);
        setPrintPosition(hbPrint, WINDOW_WIDTH - 20, WINDOW_HEIGHT/2);
        printRight(hbPrint, "%s", GAME_OVER_TEXT);
        SDL_RenderPresent(renderer);
        SDL_Delay(3000);
    }

    cleanup();
    return 0;
}

you compile it with SDL2 and SDL2_ttf libs with this npp nppexec script

npp_save
ENV_SET InputFile = $(CURRENT_DIRECTORY)\$(FILE_NAME)
ENV_SET OutDir = $(CURRENT_DIRECTORY)
ENV_SET OutputFile = $(CURRENT_DIRECTORY)\$(NAME_PART).exe
// ENV_SET InRes = $(CURRENT_DIRECTORY)\Res\NewComCtl.rc
// ENV_SET ResObj = $(CURRENT_DIRECTORY)\Res\manifest.o

cd $(CURRENT_DIRECTORY)
//cmd /c C:\mingw32\bin -i %InRes% -F pe-i386 -o %ResObj%
// cd C:\mingw32\bin
cmd /c del %OutputFile% >nul 2>&1 
// -Wpedantic %ResObj%
cmd /c gcc -Wno-unused-label -Wall -std=c99 -Wno-unknown-pragmas -O1 -Wfatal-errors -s "%InputFile%" -lmingw32 -lSDL2main -lSDL2 -lSDL2_ttf -fexec-charset=UTF-8 -luser32 -lgdi32 -IC:\c_programming\SDL2-2.30.9\i686-w64-mingw32\include -LC:\c_programming\SDL2-2.30.9\i686-w64-mingw32\lib -o "%OutputFile%
if "$(EXITCODE)" == "0" then
  cd OutDir
  cmd /c start /wait cmd " /c %OutputFile% " & pause
endif
#18
General Discussion / Re: Personal Site dedicated to...
Last post by CharlieJV - Nov 15, 2024, 05:38 PM
That is pretty cool.
#19
General Discussion / Personal Site dedicated to DOS...
Last post by ron77 - Nov 15, 2024, 04:19 AM
hello...

well, I finally did it i opened a small website for my Dos games where you can play them online in the browser...

the games are mostly GW-BASIC and FreeBASIC for Dos games and are almost all text games...

link: Retro Dev Ronen
#20
FreeBasic / Hebrew in freeBASIC
Last post by ron77 - Oct 04, 2024, 10:07 AM
1. hebrew in freebasic GFX screen you need to load a .CPI hebrew font like so...

file "LoadFont.bas"

type fbGfxFONT
  as long w,h
  as ubyte ptr pData
  bData(16*256-1) as ubyte
end type  
extern as any ptr __fb_gfx alias "__fb_gfx"
static shared as fbGfxFONT g_MyFont = type(8,16)
static shared as fbGfxFONT ptr g_pOrgFont
static shared as fbGfxFONT ptr ptr g_pptfbGfxFont

sub LoadFontEgaCPI( sFontFile as string )
  
  g_pptfbGfxFont = cptr(fbGfxFONT ptr ptr,__fb_gfx+52+9*sizeof(any ptr)-4)  
  if g_pOrgFont = 0 then g_pOrgFont = *g_pptfbGfxFont
  *g_pptfbGfxFont = @g_MyFont  
  
  var f = freefile()
  if open(sFontFile for binary access read as #f) then exit sub
  get #f,66,g_MyFont.bData(): close #f
  for N as long = 0 to 255*16
    g_MyFont.bData(N) = ((g_MyFont.bData(N) * &h80200802ULL) and &h0884422110ULL) * &h0101010101ULL shr 32
  next N
    
  g_MyFont.pData = @(g_MyFont.bData(0))  
  
end sub
#define EnableFont() *g_pptfbGfxFont = @g_MyFont  
#define RestoreFont() *g_pptfbGfxFont = g_pOrgFont

'~ screenres 640,480

'~ width 640\8,480\16 'must be 8x16
'~ LoadFontEgaCPI("hebega.cpi")

'~ for I as long = 0 to 1
  '~ for N as long = 0 to 255
    '~ locate 1+N\16, I*40+1+(N mod 16)*2
    '~ if (N >=7 and N<=10) or N=13 then print "?";: continue for
    '~ print chr(N);    
  '~ next N  
  '~ RestoreFont()
'~ next I

'~ sleep : end

You cannot view this attachment.

Plus you need to use IBM OEM 862 encoding instead UTF-8

Here is a function that converts UTF-8 to IBM OEM 862 encoding plus a "PrintRight" Function:

function Utf8ToOEM862( sInput as string ) as string
  dim as string sOutput = space(len(sInput))
  dim as long iOut
  for N as long = 0 to len(sInput)-1
    if sInput[N]=&hD7 then
      if sInput[N+1]>=&h90 andalso sInput[N+1]<=&hAA then
        sOutput[iOut] = sInput[N+1]-&h10         
        iOut += 1 : N += 1 :  :continue for
      end if
    end if
    #if __FB_DEBUG__    
      if sInput[N]>&h7F then puts("Warning: utf-8 char not converted!")
    #endif
    sOutput[iOut] = sInput[N] : iOut += 1
  next N
  return left(sOutput,iOut)
end function


sub PrintRight( sText as string )
  var sInvert = sText, iLen = len(sInvert)-1  
  for N as long = 0 to iLen\2
    swap SInvert[N],sInvert[iLen-N]
  next N
  var iLin = csrlin(), iCol = pos()
  if (iCol-(iLen+1)) < 1 then 
    print : iLin = csrlin() : iCol = loword(width())
  end if
  locate iLin,iCol-iLen
  print sInvert;
  locate iLin,iCol-(iLen+1)
end sub

and here is a function that converts UTF-8 to ANSI 1255 (good for GUI window9.bi)

function utf8toansi1255( sInput as string ) as string
  dim as string sOutput = space(len(sInput))
  dim as long iOut
  for N as long = 0 to len(sInput)-1
    if sInput[N]=&hD7 then
      if sInput[N+1]>=&h90 andalso sInput[N+1]<=&hAA then
        sOutput[iOut] = sInput[N+1]+(&hE0-&h90)
        iOut += 1 : N += 1 :  :continue for
      end if
    end if
    #if __FB_DEBUG__    
      if sInput[N]>&h7F then puts("Warning: utf-8 char not converted!")
    #endif
    sOutput[iOut] = sInput[N] : iOut += 1
  next N
  return left(sOutput,iOut)
end function