learningBot = {} -- Choose what dictionary that should be used at startup learningBot.startingDictionary = "default.lua" -- Some default settings in case loading the dictionary fails learningBot.options = {} learningBot.options.name = "Failed" learningBot.options.dictionary = "default.lua" learningBot.options.acceptNewWords = true learningBot.options.maxWords = 1000 learningBot.options.replaceWords = true learningBot.options.maxOccurancesForReplace = 10 learningBot.options.acceptInput = true learningBot.data = {} learningBot.data.numWords = 0 learningBot.dictionary = {} -- Handle text and update the dictionary learningBot.handleInput = function(sentence) if learningBot.options.acceptInput == false then return end -- Lowercase the sentence sentence = string.lower(sentence) -- Remove all punctuations sentence = string.gsub(sentence, "%p", "") -- Trim the string (remove spaces at the begining and the end) sentence = string.sub(sentence, string.find(sentence, "[^%s]"), -1) while string.char(string.byte(sentence, -1)) == " " do sentence = string.sub(sentence, 1, -2) end -- Remove all double, triple etc, spaces sentence = string.gsub(sentence, "%s%s+", " ") local words = learningBot.explode(" ", sentence) for k,v in pairs(words) do -- If it's a new word, create a base template if learningBot.dictionary[v] == nil then if learningBot.options.acceptNewWords == true and learningBot.data.numWords < learningBot.options.maxWords then -- Create space for the new word learningBot.dictionary[v] = {} learningBot.dictionary[v][1] = 0 learningBot.dictionary[v][2] = 0 learningBot.data.numWords = learningBot.data.numWords + 1 elseif learningBot.options.replaceWords == true then -- Replace the most rarely used word local tmpLowestOccurances = 9999 local tmpLowestKey = 1 local tmptmpOccurances = 0 for k2,v2 in pairs(learningBot.dictionary) do tmptmpOccurances = 0 for k3,v3 in pairs(v2) do tmptmpOccurances = tmptmpOccurances + v3 end if tmptmpOccurances < tmpLowestOccurances then tmpLowestOccurances = tmptmpOccurances tmpLowestKey = k2 end end -- Only replace if we have a well established dictionary if tmpLowestOccurances < learningBot.options.maxOccurancesForReplace then -- Remove all occurances of tmpLowestKey learningBot.dictionary[tmpLowestKey] = nil for k2,v2 in pairs(learningBot.dictionary) do if v2[tmpLowestKey] ~= nil then v2[tmpLowestKey] = nil end end -- Create space for the new word learningBot.dictionary[v] = {} learningBot.dictionary[v][1] = 0 learningBot.dictionary[v][2] = 0 end end end if learningBot.dictionary[v] ~= nil then -- Check if next word exists or not (sentence end) if k ~= table.getn(words) then if learningBot.dictionary[v][words[k+1]] == nil then learningBot.dictionary[v][words[k+1]] = 0 end -- Increase number of occurances of the next word following this one learningBot.dictionary[v][words[k+1]] = learningBot.dictionary[v][words[k+1]] + 1 else if learningBot.dictionary[v][2] == nil then learningBot.dictionary[v][2] = 0 end -- Increase number of occurances of "" following this one learningBot.dictionary[v][2] = learningBot.dictionary[v][2] + 1 end end end -- Increase number of sentence starter occurances for the first word if learningBot.dictionary[words[1]] ~= nil then if learningBot.dictionary[words[1]][1] == nil then learningBot.dictionary[words[1]][1] = 0 end learningBot.dictionary[words[1]][1] = learningBot.dictionary[words[1]][1] + 1 end end -- Return a sentence constructed from the dictionary. Start with 'word' if it is declared learningBot.createSentence = function(word) if word == nil then -- max_random is used to pick a word with higher probability to get a more commonly used one (as a sentence starter) local max_random = 0 for k,v in pairs(learningBot.dictionary) do max_random = max_random + learningBot.dictionary[k][1] end random = math.random(0, max_random) word = 1 local wordOccurance = 0 -- Get the first word for k,v in pairs(learningBot.dictionary) do wordOccurance = wordOccurance + learningBot.dictionary[k][1] -- We found the word if wordOccurance >= random then word = k break end end end -- Print the words until nil is returned local sentence = "" while word ~= nil do sentence = sentence..word.." " word = learningBot.pickNextWord(word) end sentence = string.upper(string.sub(sentence, 0, 1))..string.sub(sentence, 2, -2).."." return sentence end -- Return a word suited to follow 'word'. Returns nil if end of sentence is suitable learningBot.pickNextWord = function(word) if learningBot.dictionary[word] == nil then return end local max_random = 0 for k,v in pairs(learningBot.dictionary[word]) do if k ~= 1 then max_random = max_random + v end end local random = math.random(max_random) local wordOccurance = 0 for k,v in pairs(learningBot.dictionary[word]) do if k ~= 1 then wordOccurance = wordOccurance + v if wordOccurance >= random then if k == 2 then return nil end return k end end end end -- Saves the dictionary in a seperate file and select it for further input learningBot.saveDictionary = function(filename) if filename == nil then filename = learningBot.options.dictionary end if string.find(filename, "..") or string.find(filename, ":") then _Msg("Suspected attempt to overwrite system files. Aborting.\n") return end local fileString = "learningBot.options = {}\n\n" -- Save options learningBot.options.dictionary = filename for k,v in pairs(learningBot.options) do if type(v) ~= "string" then fileString = fileString.."learningBot.options."..k.." = "..tostring(v).."\n" else fileString = fileString.."learningBot.options."..k.." = \""..tostring(v).."\"\n" end end fileString = fileString.."\n\nlearningBot.dictionary = {}\n\n" -- Save the dictionary, word table declaration for k,v in pairs(learningBot.dictionary) do fileString = fileString.."learningBot.dictionary[\""..k.."\"] = {}\n" end fileString = fileString.."\n\n" -- Save dictionary, word table elements for k,v in pairs(learningBot.dictionary) do for k2,v2 in pairs(v) do if type(k2) == "string" then fileString = fileString.."learningBot.dictionary[\""..k.."\"][\""..k2.."\"] = "..v2.."\n" else fileString = fileString.."learningBot.dictionary[\""..k.."\"]["..k2.."] = "..v2.."\n" end end fileString = fileString.."\n" end --local file = io.open(filename, "w+") --file:write(fileString) --file:close() _file.Write("lua/lbdict/"..filename, fileString) _Msg("Learning-Bot Dictionary ("..filename..") has been saved!\n") end learningBot.loadDictionary = function(filename) if filename == nil then filename = learningBot.options.dictionary end --dofile(filename) _OpenScript("lua/lbdict/"..filename) -- Calculate the number of words in the dictionary learningBot.data.numWords = 0 for k,v in pairs(learningBot.dictionary) do learningBot.data.numWords = learningBot.data.numWords + 1 end _Msg("Learning-Bot Dictionary ("..filename..") has been loaded!\n") end learningBot.explode = function(d,p) local t,ll,l t={} ll=0 while true do l=string.find(p,d,ll+1,true) -- find the next d in the string if l~=nil then -- if "not not" found then.. table.insert(t, string.sub(p,ll,l-1)) -- Save it in our array. ll=l+1 -- save just after where we found it for searching next time. else table.insert(t, string.sub(p,ll)) -- Save what's left in our array. break -- Break at end, as it should be, according to the lua manual. end end return t end -- **********GMOD SPECIFIC CODE FOLLOWS HERE, keep above code as Lua universal as possible************* -- Load the default dictionary immediately learningBot.loadDictionary(learningBot.startingDictionary) learningBot.startingDictionary = nil -- Talks with chosen output method learningBot.talk = function(word) _PrintMessageAll(HUD_PRINTTALK, learningBot.options.name..": "..learningBot.createSentence(word)) end -- Talk "locally". Which means another output method learningBot.talkLocal = function(propid) _Msg("Debug: talkLocal\n") if _EntExists(propid) then _Msg("Debug: Entity accepted 2\n") _GModText_Start("Default") _GModText_SetEntity(propid) _GModText_SetTime(4, 1, 1) _GModText_SetText(learningBot.options.name..": "..learningBot.createSentence()) _GModText_SetEntityOffset(vector3(0, 0, 32)) _GModText_Send(0, propid + 31000) -- Paranoia end end -- Handles input from console command 'learnbot' learningBot.handleCommand = function(userid, arg) if arg == nil then _PrintMessage(userid, HUD_PRINTCONSOLE, learningBot.options.name..":\nDictionary size: "..learningBot.data.numWords.. "\nAccepts input: "..tostring(learningBot.options.acceptInput).. "\nAccepts new words: "..tostring(learningBot.options.acceptNewWords).. "\nMax number of words allowed in dictionary: "..tostring(learningBot.options.maxWords).. "\nReplaces words when dictionary is full: "..tostring(learningBot.options.replaceWords).. "\nMax frequency of word to allow it being replaced: "..tostring(learningBot.options.maxOccurancesForReplace).. "\n\n--Commands".. "\nlearnbot spawn".. "\nlearnbot save ".. "\nlearnbot load ") return end local args = learningBot.explode(" ", arg) if args[1] == "spawn" then _TraceLine(_PlayerGetShootPos(userid), _PlayerGetShootAng(userid), 9999, userid) learningBot.data.entity = _EntCreate("npc_dog") _EntSetPos(learningBot.data.entity, vecAdd(_TraceEndPos(), vector3(0,0,64))) _EntSetName(learningBot.data.entity, "learnbot") _EntSpawn(learningBot.data.entity) end if args[1] == "save" then learningBot.saveDictionary(args[2]) elseif args[1] == "load" then learningBot.loadDictionary(args[2]) end end CONCOMMAND("learnbot", learningBot.handleCommand) -- Speak every 4 minutes AddTimer(240, 0, learningBot.talk) -- Speak locally every 200 seconds AddTimer(200, 0, learningBot.talkLocal) -- Save the dictionary every 2 minutes AddTimer(120, 0, learningBot.saveDictionary) -- Talk when a player someone is killed HookEvent("eventPlayerKilled", function(userid, attacker, weapon) learningBot.talk() end) -- Talk when the bot's name is mentioned. Respond in some cases. Othervise, input the text into the dictionary HookEvent("eventPlayerSay", function(userid, strText, bTeam) -- Remove the "PlayerName: " in the string strText = string.sub(strText, string.len(_PlayerInfo(userid, "name")) + 3) -- Check if the bot's name is mentioned if string.find(string.lower(strText), string.lower(learningBot.options.name)) then words = learningBot.explode(" ", string.lower(strText)) -- Check if there's two words and the last one ends with "?" if table.getn(words) == 2 and string.char(string.byte(words[2], -1)) == "?" then -- Remove the "?" words[2] = string.sub(words[2], 1, -2) if string.find(words[1], string.lower(learningBot.options.name)) then learningBot.talk(words[2]) else learningBot.talk(words[1]) end else learningBot.talk() end else learningBot.handleInput(strText) end end) -- Check if the bot has been "used" and speak "locally" if that is the case. HookEvent("eventPlayerUseEntity", function(userid, propid) _Msg("Debug: eventPlayerUseEntity\n") if _EntGetName(propid) == "learnbot" then _Msg("Debug: Entity accepted\n") learningBot.talkLocal(propid) end end)