Welcome to RetroCoders Community
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
int main() {
float A = 0, B = 0;
float i, j;
int k;
float z[1760];
char b[1760];
printf("\x1b[2J");
for(;;) {
memset(b,32,1760);
memset(z,0,7040);
for(j=0; j < 6.28; j += 0.07) {
for(i=0; i < 6.28; i += 0.02) {
float c = sin(i);
float d = cos(j);
float e = sin(A);
float f = sin(j);
float g = cos(A);
float h = d + 2;
float D = 1 / (c * h * e + f * g + 5);
float l = cos(i);
float m = cos(B);
float n = sin(B);
float t = c * h * g - f * e;
int x = 40 + 30 * D * (l * h * m - t * n);
int y= 12 + 15 * D * (l * h * n + t * m);
int o = x + 80 * y;
int N = 8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n);
if(22 > y && y > 0 && x > 0 && 80 > x && D > z[o]) {
z[o] = D;
b[o] = ".,-~:;=!*#$@"[N > 0 ? N : 0];
}
}
}
printf("\x1b[H");
for(k = 0; k < 1761; k++) {
putchar(k % 80 ? b[k] : 10);
}
A += 0.04;
B += 0.02;
usleep(30000);
}
return 0;
}
use std::fs;
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use std::ffi::CString;
use std::env;
use rand::Rng;
use anyhow::{Result, Context};
use bass_sys::*;
// File paths
const DESC1: &str = "data/des1.txt";
const OPENING_BANNER: &str = "banners/opening1.txt";
const OPENING_POINT: &str = "data/opening1.txt";
const MENU: &str = "data/mainmenu.txt";
const MONEY_MENU: &str = "data/moneyMenu.txt";
const FRIENDS_MENU: &str = "data/friendsmenu.txt";
const OVER: &str = "data/suicide.txt";
const GAME_END: &str = "data/theend.txt";
const FAMILY1: &str = "data/family1.txt";
const SCHOOL1: &str = "data/school1.txt";
// Arrays of file paths
const MONTHS: [&str; 12] = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
const SONGS_RAND: [&str; 12] = [
"sound/walrus1.ogg", "sound/walrus2.ogg", "sound/walrus3.ogg",
"sound/walrus4.ogg", "sound/walrus5.ogg", "sound/walrus6.ogg",
"sound/walrus7.ogg", "sound/walrus8.ogg", "sound/walrus9.ogg",
"sound/walrus10.ogg", "sound/walrus11.ogg", "sound/walrus12.ogg"
];
const MARY: [&str; 5] = [
"data/mary1.txt", "data/mary2rehab.txt", "data/mary3pregnant.txt",
"data/mary4jail.txt", "data/mary5dead.txt"
];
const JOE: [&str; 5] = [
"data/joe1.txt", "data/joe2rehab.txt", "data/joe3jail.txt",
"data/joe4.txt", "data/joe5prison.txt"
];
const FELIX: [&str; 5] = [
"data/felix1.txt", "data/felix2.txt", "data/felix3jail.txt",
"data/felix4rehab.txt", "data/felix5crazy.txt"
];
struct GameState {
health: i32,
money: i32,
turns: usize,
year: i32,
is_over: bool,
is_mary: bool,
is_joe: bool,
is_felix: bool,
is_suicide: bool,
stream: u32,
}
impl GameState {
fn new() -> Self {
Self {
health: 100,
money: 5000,
turns: 0,
year: 1991,
is_over: false,
is_mary: true,
is_joe: true,
is_felix: true,
is_suicide: false,
stream: 0,
}
}
fn play_music(&mut self, path: &str) -> Result<()> {
let mut full_path = env::current_dir()?;
full_path.push(path);
let path_str = full_path.to_str().unwrap();
println!("Attempting to play: {}", path_str);
if self.stream != 0 {
BASS_StreamFree(self.stream);
self.stream = 0;
}
let path_c = CString::new(path_str)?;
self.stream = BASS_StreamCreateFile(
0,
path_c.as_ptr() as *const _,
0,
0,
BASS_SAMPLE_LOOP
);
if self.stream == 0 {
println!("BASS Error Code: {}", BASS_ErrorGetCode());
return Ok(());
}
if BASS_ChannelPlay(self.stream, 0) == 0 {
println!("Failed to play: Error code {}", BASS_ErrorGetCode());
return Ok(());
}
Ok(())
}
fn stop_music(&mut self) {
if self.stream != 0 {
BASS_ChannelStop(self.stream);
BASS_StreamFree(self.stream);
self.stream = 0;
}
}
fn read_file(&self, path: &str) -> Result<String> {
let mut full_path = env::current_dir()?;
full_path.push(path);
fs::read_to_string(&full_path)
.with_context(|| format!("Failed to read file: {}", full_path.display()))
}
fn clear_screen(&self) {
print!("\x1B[2J\x1B[1;1H");
io::stdout().flush().unwrap();
}
fn get_input(&self) -> Result<i32> {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
Ok(input.trim().parse()?)
}
fn wait_for_enter(&self) -> Result<()> {
let mut input = String::new();
io::stdin().read_line(&mut input)?;
Ok(())
}
fn friends(&mut self) -> Result<()> {
self.clear_screen();
println!(
"date: {}, {} health: {} money: {}\n\n{}",
MONTHS[self.turns], self.year, self.health, self.money,
self.read_file(FRIENDS_MENU)?
);
match self.get_input()? {
1 => self.visit_mary()?,
2 => self.visit_joe()?,
3 => self.visit_felix()?,
4 => self.visit_school()?,
5 => self.visit_family()?,
_ => {
println!("INVALID INPUT!");
thread::sleep(Duration::from_secs(5));
}
}
Ok(())
}
fn visit_mary(&mut self) -> Result<()> {
self.clear_screen();
if !self.is_mary {
println!("{}", self.read_file(MARY[4])?);
} else {
let mut rng = rand::thread_rng();
let r = rng.gen_range(0..5);
if r == 4 {
self.is_mary = false;
}
println!("{}", self.read_file(MARY[r])?);
}
self.wait_for_enter()?;
Ok(())
}
fn visit_joe(&mut self) -> Result<()> {
self.clear_screen();
if !self.is_joe {
println!("{}", self.read_file(JOE[4])?);
} else {
let mut rng = rand::thread_rng();
let r = rng.gen_range(0..5);
if r == 4 {
self.is_joe = false;
}
println!("{}", self.read_file(JOE[r])?);
}
self.wait_for_enter()?;
Ok(())
}
fn visit_felix(&mut self) -> Result<()> {
self.clear_screen();
if !self.is_felix {
println!("{}", self.read_file(FELIX[4])?);
} else {
let mut rng = rand::thread_rng();
let r = rng.gen_range(0..5);
if r == 4 {
self.is_felix = false;
}
println!("{}", self.read_file(FELIX[r])?);
}
self.wait_for_enter()?;
Ok(())
}
fn visit_school(&mut self) -> Result<()> {
self.clear_screen();
println!("{}", self.read_file(SCHOOL1)?);
self.wait_for_enter()?;
Ok(())
}
fn visit_family(&mut self) -> Result<()> {
self.clear_screen();
println!("{}", self.read_file(FAMILY1)?);
self.wait_for_enter()?;
Ok(())
}
fn drugs(&mut self) -> Result<()> {
self.clear_screen();
let mut rng = rand::thread_rng();
let index = rng.gen_range(0..5);
println!("{}", self.read_file(&format!("data/drug{}.txt", index + 1))?);
match index {
0 => self.money -= 170,
3 => {
self.health -= 5;
self.money -= 350;
}
4 => {
self.health -= 5;
self.money -= 250;
}
_ => {}
}
self.wait_for_enter()?;
Ok(())
}
fn shoplist(&mut self) -> Result<()> {
self.clear_screen();
let mut rng = rand::thread_rng();
let index = rng.gen_range(0..5);
println!("{}", self.read_file(&format!("data/shop{}.txt", index + 1))?);
match index {
0 => self.money += 200,
1 => self.money += 150,
2 => self.health -= 3,
3 => self.money += 200,
4 => self.health -= 3,
_ => {}
}
self.wait_for_enter()?;
Ok(())
}
fn car(&mut self) -> Result<()> {
self.clear_screen();
let mut rng = rand::thread_rng();
let index = rng.gen_range(0..5);
println!("{}", self.read_file(&format!("data/car{}.txt", index + 1))?);
match index {
0 => self.money += 1000,
1 => self.money += 2000,
2 => self.health -= 5,
3 => self.money += 1500,
4 => self.health -= 5,
_ => {}
}
self.wait_for_enter()?;
Ok(())
}
fn burglary(&mut self) -> Result<()> {
self.clear_screen();
let mut rng = rand::thread_rng();
let index = rng.gen_range(0..5);
println!("{}", self.read_file(&format!("data/burglur{}.txt", index + 1))?);
match index {
0 => self.money += 2500,
1 => self.health -= 7,
2 => self.health -= 7,
3 => self.money += 2500,
4 => self.health -= 7,
_ => {}
}
self.wait_for_enter()?;
Ok(())
}
fn get_money(&mut self) -> Result<()> {
self.clear_screen();
println!(
"date: {}, {} health: {} money: {}\n\n{}",
MONTHS[self.turns], self.year, self.health, self.money,
self.read_file(MONEY_MENU)?
);
match self.get_input()? {
1 => self.shoplist()?,
2 => self.car()?,
3 => self.burglary()?,
_ => {}
}
Ok(())
}
fn rehab(&mut self) -> Result<()> {
if self.money < 2800 {
self.clear_screen();
println!(
"\nYOU DON'T HAVE ENOUGH MONEY FOR REHAB...\nIT COSTS 2800 AND YOU ONLY HAVE {}\n",
self.money
);
self.wait_for_enter()?;
return Ok(());
}
self.money -= 2800;
for i in 0..6 {
self.clear_screen();
println!("{}", self.read_file(&format!("data/rehab{}.txt", i + 1))?);
self.wait_for_enter()?;
}
self.health += 65;
if self.health > 100 {
self.health = 100;
}
Ok(())
}
fn mirror(&mut self) -> Result<()> {
self.clear_screen();
let mirror_index = match self.health {
h if h >= 90 => 0,
h if h >= 80 => 1,
h if h >= 70 => 2,
h if h >= 60 => 3,
h if h >= 50 => 4,
h if h >= 40 => 5,
h if h >= 30 => 6,
h if h >= 20 => 7,
h if h >= 10 => 8,
_ => 9,
};
println!("{}", self.read_file(&format!("data/mirror{}.txt", mirror_index + 1))?);
if self.health < 35 {
self.is_suicide = true;
}
self.wait_for_enter()?;
Ok(())
}
fn suicide(&mut self) -> Result<()> {
self.clear_screen();
println!("{}", self.read_file(OVER)?);
self.wait_for_enter()?;
self.is_over = true;
Ok(())
}
}
fn main() -> Result<()> {
// Initialize BASS
if BASS_Init(-1, 44100, 0, std::ptr::null_mut(), std::ptr::null_mut()) == 0 {
let error_code = BASS_ErrorGetCode();
eprintln!("Error initializing BASS library: {}", error_code);
return Ok(());
}
let mut game = GameState::new();
let mut rng = rand::thread_rng();
// Show intro
println!("{}", game.read_file(DESC1)?);
game.wait_for_enter()?;
game.clear_screen();
// Start background music
let song_index = rng.gen_range(0..12);
if let Err(e) = game.play_music(SONGS_RAND[song_index]) {
eprintln!("Failed to play music: {}", e);
}
println!("{}", game.read_file(OPENING_BANNER)?);
game.wait_for_enter()?;
game.clear_screen();
println!("{}", game.read_file(OPENING_POINT)?);
game.wait_for_enter()?;
game.clear_screen();
// Main game loop
while game.health > 0 && !game.is_over {
game.clear_screen();
if game.turns > 11 {
game.turns = 0;
game.year += 1;
}
if game.money < 0 {
game.money = 0;
}
println!(
"date: {}, {} health: {} money: {}\n\n{}",
MONTHS[game.turns], game.year, game.health, game.money,
game.read_file(MENU)?
);
if game.is_suicide {
println!("PRESS 8 TO TRY AND SUICIDE");
}
match game.get_input()? {
1 => {
game.stop_music();
let song_index = rng.gen_range(0..12);
if let Err(e) = game.play_music(SONGS_RAND[song_index]) {
eprintln!("Failed to change music: {}", e);
}
},
2 => game.friends()?,
3 => game.drugs()?,
4 => game.get_money()?,
5 => game.rehab()?,
6 => game.mirror()?,
7 => game.is_over = true,
8 if game.is_suicide => game.suicide()?,
_ => {}
}
game.turns += 1;
}
// Show ending
game.clear_screen();
println!("{}", game.read_file(GAME_END)?);
game.wait_for_enter()?;
// Cleanup
game.stop_music();
BASS_Free();
Ok(())
}
[package]
name = "rust_trainspotting_II_game"
version = "0.1.0"
edition = "2021"
[dependencies]
rand = "0.8"
bass-sys = "2.1"
anyhow = "1.0"
use std::fs::File;
use std::io::{self, BufRead, BufReader, Write};
use std::process::Command;
use std::collections::HashMap;
use rand::prelude::*;
use anyhow::{Result, Context as _};
// const MAX_STRING_LENGTH: usize = 1000;
// const MAX_KEYWORDS: usize = 20;
// const MAX_REPLIES: usize = 50;
const MAX_MEMORY: usize = 15;
#[derive(Default)]
struct ArraySet {
keywords: Vec<String>,
replies: Vec<String>,
}
#[derive(Default)]
struct ChatBot {
key_reply: Vec<ArraySet>,
default_replies: Vec<String>,
memory: Vec<String>,
word_swaps: HashMap<String, String>,
rng: ThreadRng,
single_line_mode: bool,
}
impl ChatBot {
fn new() -> Self {
Self {
key_reply: Vec::new(),
default_replies: Vec::new(),
memory: Vec::with_capacity(MAX_MEMORY),
word_swaps: HashMap::new(),
rng: thread_rng(),
single_line_mode: true,
}
}
fn load_arrays(&mut self, filename: &str) -> Result<()> {
let file = File::open(filename)
.with_context(|| format!("Failed to open file: {}", filename))?;
let reader = BufReader::new(file);
let mut current_set: Option<ArraySet> = None;
for line in reader.lines() {
let line = line?;
if line.starts_with("d1:") {
self.default_replies.push(line[3..].to_string());
} else if line.starts_with("k:") {
if current_set.is_some() && !current_set.as_ref().unwrap().replies.is_empty() {
if let Some(set) = current_set.take() {
self.key_reply.push(set);
}
}
if current_set.is_none() {
current_set = Some(ArraySet::default());
}
if let Some(set) = &mut current_set {
set.keywords.push(line[2..].to_string());
}
} else if line.starts_with("r:") {
if let Some(set) = &mut current_set {
set.replies.push(line[2..].to_string());
}
}
}
if let Some(set) = current_set {
self.key_reply.push(set);
}
println!("Number of keywords-replies pair groups: {}", self.key_reply.len());
Ok(())
}
fn load_swap_words(&mut self, filename: &str) -> Result<()> {
let file = File::open(filename)
.with_context(|| format!("Failed to open swap words file: {}", filename))?;
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line?;
if line.starts_with("s:") {
if let Some(pos) = line[2..].find('>') {
let (word_in, word_out) = line[2..].split_at(pos);
self.word_swaps.insert(
word_in.to_string(),
word_out[1..].to_string()
);
}
}
}
Ok(())
}
fn swap_words(&self, input: &str) -> String {
input
.split_whitespace()
.map(|word| {
self.word_swaps
.get(word)
.map_or(word.to_string(), |w| w.clone())
})
.collect::<Vec<_>>()
.join(" ")
}
fn isolate_punctuation(s: &str) -> String {
s.chars()
.map(|c| {
if "?!,.:;<>(){}[]".contains(c) {
format!(" {} ", c)
} else {
c.to_string()
}
})
.collect()
}
fn process_reply(&mut self, reply: &str, user_input: &str) -> String {
if !reply.ends_with('*') {
return reply.to_string();
}
let reply = &reply[..reply.len() - 1];
if self.memory.len() < MAX_MEMORY {
self.memory.push(user_input.to_string());
}
for set in &self.key_reply {
for keyword in &set.keywords {
if user_input.contains(keyword.as_str()) {
if let Some(rest) = user_input.split(keyword.as_str()).nth(1) {
let swapped = self.swap_words(rest);
return format!("{} {}", reply, swapped);
}
}
}
}
reply.to_string()
}
fn user_question(&mut self, txt: &str) -> String {
let mut matched_replies = Vec::new();
let mut found = false;
for set in &self.key_reply {
for keyword in &set.keywords {
if txt.contains(keyword.as_str()) {
if let Some(reply) = set.replies.choose(&mut self.rng).cloned() {
matched_replies.push(reply);
found = true;
break;
}
}
}
}
let processed_replies: Vec<String> = matched_replies.into_iter()
.map(|reply| self.process_reply(&reply, txt))
.collect();
if !processed_replies.is_empty() {
if self.single_line_mode {
processed_replies.join(" | ")
} else {
processed_replies.join("\n")
}
} else if !found && !self.default_replies.is_empty() {
self.default_replies
.choose(&mut self.rng)
.cloned()
.unwrap_or_else(|| "I don't understand. Can you rephrase that?".to_string())
} else {
"I don't understand. Can you rephrase that?".to_string()
}
}
fn commands(&mut self, txt: &str) -> String {
if txt.starts_with("/") {
match txt {
"/single" => {
self.single_line_mode = true;
return "Switched to single-line mode.".to_string();
}
"/multi" => {
self.single_line_mode = false;
return "Switched to multi-line mode.".to_string();
}
_ => {}
}
}
let isolated = Self::isolate_punctuation(txt);
let lowercase = isolated.to_lowercase();
self.user_question(&lowercase)
}
fn speak(&self, text: &str) -> Result<()> {
Command::new("voice")
.args(["-r", "-1", "-n", "Microsoft David Desktop", text])
.spawn()
.with_context(|| "Failed to start TTS process")?
.wait()
.with_context(|| "Failed to wait for TTS process")?;
Ok(())
}
}
fn main() -> Result<()> {
println!("CHATBOT DANNY IN RUST VERSION 1.0.0");
println!("Commands:");
println!(" /single - Switch to single-line mode");
println!(" /multi - Switch to multi-line mode");
println!(" quit - Exit the program");
let mut chatbot = ChatBot::new();
chatbot.load_arrays("database.txt")?;
chatbot.load_swap_words("swapwords.txt")?;
let stdin = io::stdin();
let mut stdout = io::stdout();
loop {
print!("> ");
stdout.flush()?;
let mut input = String::new();
stdin.lock().read_line(&mut input)?;
let input = input.trim();
if input == "quit" {
break;
}
let response = chatbot.commands(input);
if chatbot.single_line_mode {
println!("{}", response);
chatbot.speak(&response)?;
} else {
for line in response.lines() {
println!("{}", line);
chatbot.speak(line)?;
}
}
}
Ok(())
}
use std::{thread, time::Duration};
fn main() {
let mut a = 0.0f32;
let mut b = 0.0f32;
let mut z = vec![0.0f32; 1760];
let mut buffer = vec![' '; 1760];
// Clear screen
print!("\x1b[2J");
loop {
// Reset buffers
buffer.fill(' ');
z.fill(0.0);
let mut j = 0.0f32;
while j < 6.28 {
let mut i = 0.0f32;
while i < 6.28 {
let c = i.sin();
let d = j.cos();
let e = a.sin();
let f = j.sin();
let g = a.cos();
let h = d + 2.0;
let d = 1.0 / (c * h * e + f * g + 5.0);
let l = i.cos();
let m = b.cos();
let n = b.sin();
let t = c * h * g - f * e;
let x = (40.0 + 30.0 * d * (l * h * m - t * n)) as i32;
let y = (12.0 + 15.0 * d * (l * h * n + t * m)) as i32;
let o = (x + 80 * y) as usize;
let luminance = (8.0 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n)) as i32;
if y > 0 && y < 22 && x > 0 && x < 80 && d > z[o] {
z[o] = d;
buffer[o] = ".,-~:;=!*#$@".chars().nth((luminance.max(0) as usize).min(11)).unwrap();
}
i += 0.02;
}
j += 0.07;
}
// Move cursor to home position
print!("\x1b[H");
// Print the frame
for k in 0..1760 {
if k % 80 == 0 {
println!();
} else {
print!("{}", buffer[k]);
}
}
a += 0.04;
b += 0.02;
thread::sleep(Duration::from_micros(30000));
}
}
module example;
import std::math;
import std::io;
import std::thread;
import libc;
fn void main()
{
float a1 = 0;
float b1 = 0;
float i;
float j;
int k;
float[1760] z;
char[1760] b;
io::printf("\x1b[2J");
while (true)
{
// Initialize arrays
for (k = 0; k < 1760; k++)
{
b[k] = ' ';
z[k] = 0;
}
for (j = 0; j < 6.28; j += 0.07)
{
for (i = 0; i < 6.28; i += 0.02)
{
float c = math::sin(i);
float d = math::cos(j);
float e = math::sin(a1);
float f = math::sin(j);
float g = math::cos(a1);
float h = d + 2;
float d2 = 1 / (c * h * e + f * g + 5);
float l = math::cos(i);
float m = math::cos(b1);
float n = math::sin(b1);
float t = c * h * g - f * e;
int x = 40 + (int)(30 * d2 * (l * h * m - t * n));
int y = 12 + (int)(15 * d2 * (l * h * n + t * m));
int o = x + 80 * y;
int n2 = (int)(8 * ((f * e - c * d * g) * m - c * d * e - f * g - l * d * n));
if (y > 0 && y < 22 && x > 0 && x < 80 && d2 > z[o])
{
z[o] = d2;
b[o] = n2 > 0 ? ".,-~:;=!*#$@"[n2] : ".,-~:;=!*#$@"[0];
}
}
}
io::printf("\x1b[H");
for (k = 0; k < 1761; k++)
{
libc::putchar(k % 80 ? b[k] : 10);
}
a1 += 0.04;
b1 += 0.02;
thread::sleep_ms(30); // Replace usleep with thread::sleep_ms
}
}
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;
}
/* 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...");
}
/* 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");
}