Hello to all lovers and researchers of artificial intelligence! In this article, I would like to tell about an interesting project: the modular system of universal artificial intelligence (ISUAI) "Amiga Virtual" (AV, "Virtual Girlfriend"). I will talk about the basic principles of its work and describe some details of the implementation, and the most curious will be able to explore all the source codes. Development is conducted on Delphi, but the modules can theoretically be written in any PL. This system will be of interest both to end users of chat bots and related systems, and to developers of AI - after all, almost any type of AI can be developed on its basis.

procedure LoadModules; var i: Integer; begin for i := 0 to Length(Module) - 1 do begin if Module[i].Handle > 0 then FreeModule(Module[i]); if LoadModule(Module[i]) then DetermineModuleType(Module[i]); end; end; function LoadModule(var M: TModule): boolean; var B: String; C: Integer; begin with M do begin Handle := LoadLibrary(PWideChar(FileName)); if Handle > 0 then begin @SetLanguage := GetProcAddress(Handle, 'SetLanguage'); IncAndUpdateProgress; // - @SendLanguageData := GetProcAddress(Handle, 'SendLanguageData'); IncAndUpdateProgress; @GetName := GetProcAddress(Handle, 'GetName'); IncAndUpdateProgress; // , // if @GetName <> nil then begin B := GetName; C := Pos(ControlCodeMarker, B); if C > 0 then begin Name := Copy(B, C + 1, Length(B)); ControlCode := Copy(B, 1, C - 1); ControlCodeFormated := FormatControlCode(ControlCode); end else begin Name := B; ControlCode := MainForm.LanguageData[133]; end; end else begin Name := MainForm.LanguageData[114]; ControlCode := MainForm.LanguageData[133]; end; if (@OpenWindow <> nil) and (@CloseWindow <> nil) then WindowState := closed else WindowState := window_does_not_exist; if (@Sleep <> nil) and (@WakeUp <> nil) then ModuleState := working else ModuleState := module_cant_sleep; LoadStatistics; // Result := true; end else Result := false; end; end; function DetermineModuleType(var M: TModule): TModuleType; begin with M do begin MType := undetermined; if (@SetLanguage = nil) and ... (* nil *) then MType := erroneous_or_empty else begin if (@SetSource <> nil) or (@NextData <> nil) or (@Progress <> nil) or (@RestartParsing <> nil) then MType := parser else begin if (@GetData <> nil) and (@SendData <> nil) then MType := input_and_output else if (@GetData <> nil) and (@SendData = nil) then MType := only_input else if (@GetData = nil) and (@SendData <> nil) then MType := only_output else if (@GetData = nil) and (@SendData = nil) then MType := no_input_and_no_output; end; end; Result := MType; end; end; TIOThread = class(TThread) public const SleepTime = 1000; var Module: TModule; SelfID: Integer; constructor Create(M: TModule; ID: Integer); protected procedure Execute; override; end; procedure TIOThread.Execute; procedure GetData; var M: String; begin M := String(Module.GetData); if M <> SCM_No_Message then Synchronize( procedure begin Pool.AddRecord(M, SelfID); end); end; procedure SendData; var i: Integer; begin with Pool do if not Empty then begin for i := 0 to Length(Records) - 1 do with Records[i] do if not ModuleGot[SelfID] and (AuthorID <> SelfID) then begin Module.SendData(PChar(Text)); ModuleGot[SelfID] := true; end; Synchronize( procedure begin CheckAndDeleteOddRecords; end); end; end; begin inherited; while not Terminated do begin case Module.MType of only_input: GetData; only_output: SendData; input_and_output: begin SendData; GetData; end; end; Sleep(SleepTime); end; end; TPoolRecord = record Text: String; AuthorID: Integer; ModuleGot: array of Boolean; end; TPool = class Records: array of TPoolRecord; Empty: Boolean; procedure AddRecord(RecordText: String; RecordAuthor: Integer); procedure CheckAndDeleteOddRecords; constructor Create; procedure Show; end; procedure TPool.AddRecord(RecordText: String; RecordAuthor: Integer); var i, RL: Integer; begin RL := Length(Records); SetLength(Records, RL + 1); with Records[RL] do begin Text := RecordText; AuthorID := RecordAuthor; SetLength(ModuleGot, OutputModulesCount); for i := 0 to OutputModulesCount - 1 do if i = AuthorID then ModuleGot[i] := true else ModuleGot[i] := false; end; with MainForm, MainForm.ChatBox.Lines do case RecordAuthor of - 1: Add(User.Name + ': ' + RecordText); else if RecordText = SCM_Dont_Know_Answer then begin if DontKnowCheckBtn.Checked then Add(LanguageData[156]); end else Add(AVirtual.Name + ': ' + RecordText); end; Empty := false; end; procedure TPool.CheckAndDeleteOddRecords; function ItsOdd(ID: Integer): Boolean; var i: Integer; begin ItsOdd := true; with Records[ID] do for i := 0 to Length(ModuleGot) - 1 do if not ModuleGot[i] then begin ItsOdd := false; exit; end; end; procedure DeleteRecord(ID: Integer); var i: Integer; begin for i := ID to Length(Records) - 2 do Records[i] := Records[i + 1]; SetLength(Records, Length(Records) - 1); end; var i: Integer; begin if not Empty then begin for i := Length(Records) - 1 downto 0 do if ItsOdd(i) then DeleteRecord(i); if Length(Records) = 0 then Empty := true; end; if MainForm.PoolShowBtn.Checked then Show; end; library ; uses System.SysUtils, System.Classes, SystemControlMessagesUnit in '..\..\AmigaVirtual\SystemControlMessagesUnit.pas', MainFormUnit in 'MainFormUnit.pas', const ControlCode = ++; Name = ControlCode + '> '; Help = '' + #13 + ''; var FormState: (closed, opened); Buffer, VirtualName: String; NewMessageGot: Boolean; function GetName: PChar; stdcall; begin Result := PChar(Name); end; function GetHelp: PChar; stdcall; begin Result := PChar(Help); end; procedure OpenWindow; stdcall; begin if MainForm = nil then MainForm := TMainForm.Create(nil); MainForm.Show; FormState := opened; end; procedure CloseWindow; stdcall; begin if FormState = opened then begin MainForm.Close; FormState := closed; MainForm.Release; MainForm := nil; end; end; procedure SendData(Data: PChar); stdcall; begin Buffer := Data; end; function GetData: PChar; stdcall; begin if NewMessageGot then begin Result := PChar(Buffer); NewMessageGot := false; end else Result := PChar(SCM_No_Message); end; procedure Start; stdcall; begin NewMessageGot := false; if MainForm = nil then MainForm := TMainForm.Create(nil); end; procedure SetVirtual(Name: PChar); stdcall; begin VirtualName := Name; end; procedure LoadData; stdcall; begin // end; procedure SaveData; stdcall; begin // end; exports GetName, GetHelp, OpenWindow, CloseWindow, SendData, GetData, Start, SetVirtual, LoadData, SaveData; begin end. SetLanguage: function(Language: PChar): boolean; stdcall; SendLanguageData: function(Data: array of PChar): boolean; stdcall; GetName: function: PChar; stdcall; GetHelp: function: PChar; stdcall; Start: procedure; stdcall; Sleep: function: boolean; stdcall; WakeUp: procedure; stdcall; OpenWindow: procedure; stdcall; CloseWindow: procedure; stdcall; SetVirtual: procedure(Name: PChar); stdcall; SaveData: procedure; stdcall; LoadData: procedure; stdcall; Reset: procedure; stdcall; SetNewMainWindow: procedure(Position, Size: TPoint); stdcall; GetTimerInterval: function: Integer; stdcall; SendData: procedure(Data: PChar); stdcall; GetData: function: PChar; stdcall; SetSource: procedure(SourcePath: PChar); stdcall; NextData: function: PChar; stdcall; Progress: function: Real; stdcall; RestartParsing: procedure; stdcall; 
type CResult = (no_collision, collision); function CheckModulesCollision: CResult; var i, j: Integer; CC: String; begin Result := no_collision; with ModulesList do for i := 0 to Items.Count - 1 do if Checked[i] then begin CC := FindModuleByFileName(ExtractDLLName(Items[i])).ControlCode; for j := 0 to Items.Count - 1 do if Checked[j] and (i <> j) and (CC = FindModuleByFileName(ExtractDLLName(Items[j])).ControlCode) then Result := collision; end; end; 
procedure TMainForm.DownloadFilesButtonClick(Sender: TObject); var Dir, FileName: String; begin SetCurrentDir(ProgramPath); Dir := Category[ContentCategoryBox.ItemIndex]; Dir := UpCase(Dir[1]) + Copy(Dir, 2, Length(Dir)); if not DirectoryExists(Dir) then CreateDir(Dir); SetCurrentDir(Dir); FileName := ContentList.Items[ContentList.ItemIndex]; if FileExists(FileName) then MessageDlg(LanguageData[172], mtInformation, [mbOk], 0) else begin DownloadFile(SiteProtocol + OfficialWebsite + ExchangeCenterPage + '?c=' + Category[ContentCategoryBox.ItemIndex] + '&f=' + FileName + '&l=' + LanguageData[0], FileName); // LanguageData[0] if Copy(FileName, Length(FileName) - 2, 3) = 'zip' then UnzipFiles(FileName, GetCurrentDir); case ContentCategoryBox.ItemIndex of 0: UpdateModulesList; 1: UpdateVirtualsList; end; SetStatusMessage(LanguageData[173] + ' ' + ProgramPath + Dir + '\'); end; end; procedure TMainForm.DownloadFile(From, SaveTo: String); var LoadStream: TMemoryStream; begin Downloading := true; LoadStream := TMemoryStream.Create; IdHTTP.Get(TIdURI.URLEncode(From), LoadStream); LoadStream.SaveToFile(SaveTo); LoadStream.Free; Downloading := false; SplashScreen.Close; end; 

procedure TMainForm.DeployDefaultLanguages; procedure DeployLanguage(LanguageName: String); var ResHandle, MemHandle: THandle; MemStream: TMemoryStream; ResPtr: PByte; ResSize: Longint; ResName: String; i: Integer; begin ResName := ''; for i := 1 to Length(LanguageName) do ResName := ResName + UpCase(LanguageName[i]); ResName := ResName + '_LP'; ResHandle := FindResource(HInstance, PWideChar(ResName), RT_RCDATA); if ResHandle = 0 then begin ShowMessage('Default language "' + LanguageName + '" not found. (' + ResName + ')'); exit; end; MemHandle := LoadResource(HInstance, ResHandle); ResPtr := LockResource(MemHandle); MemStream := TMemoryStream.Create; ResSize := SizeOfResource(HInstance, ResHandle); MemStream.SetSize(ResSize); MemStream.Write(ResPtr^, ResSize); MemStream.Seek(0, 0); MemStream.SaveToFile(LangFilesDir + '/' + LanguageName + LanguageFileExtension); FreeResource(MemHandle); MemStream.Destroy; end; begin if not DirectoryExists(LangFilesDir) then CreateDir(LangFilesDir); DeployLanguage('Russian'); DeployLanguage('English'); end; procedure TMainForm.ChangeLanguageTo; function LanguageDataLoaded: Boolean; var T: TextFile; B: RawByteString; begin SetCurrentDir(ProgramPath + LangFilesDir); if FileExists(Language + LanguageFileExtension) then begin AssignFile(T, Language + LanguageFileExtension); Reset(T); SetLength(LanguageData, 1); LanguageData[0] := Language; while not eof(T) do begin SetLength(LanguageData, Length(LanguageData) + 1); ReadLn(T, B); LanguageData[Length(LanguageData) - 1] := UTF8ToWideString(B); end; CloseFile(T); if Length(LanguageData) - 1 >= LangFileMinSize then Result := true else Result := false; end else Result := false; end; procedure SetCaptions; begin with HelpForm do begin LoadHelpTexts; if CurrentTopic < HelpLast then OpenTopic(CurrentTopic) else OpenTopic(0); end; AddModulesHelpToMainProgramHelp; ChangeModulesLanguageToProgramLanguage; AuthorizationTab.Caption := LanguageData[18]; // - ... CloudSaveNow.Caption := LanguageData[181]; CloudLoadNow.Caption := LanguageData[182]; end; procedure CheckMenuItem; var Item: TMenuItem; begin for Item in LanguageMenu.Items do if Item.Name = LanguageData[0] + 'Lang' then Item.Checked := true; end; begin if LanguageDataLoaded then begin if not Silent then SetStatusMessage(LanguageData[2] + ' ' + LanguageData[0]) else FormCaption.Caption := LanguageData[1]; SetCaptions; end else begin if Language <> SavedLanguage then ChangeLanguageTo(SavedLanguage) else ChangeLanguageTo(DefaultLanguage); MessageDlg(LanguageData[3] + #13 + LanguageData[2] + ' ' + LanguageData[0] + '.', mtError, [mbOk], 0); end; CheckMenuItem; end; 

procedure TMainForm.UpdateProgram; procedure DeployBAT; var bat: TextFile; begin if not FileExists(ProgramPath + 'update.bat') then begin AssignFile(bat, ProgramPath + 'update.bat'); Rewrite(bat); WriteLn(bat, 'taskkill /im av.exe'); WriteLn(bat, 'sleep 1'); // Windows XP WriteLn(bat, 'timeout /t 1 /nobreak'); // Windows 7+ WriteLn(bat, 'del av.exe'); WriteLn(bat, 'move ' + ZipsDir + '\av.exe %1'); WriteLn(bat, 'del /S /Q ' + ZipsDir); WriteLn(bat, 'start av.exe'); // WriteLn(bat, 'pause'); CloseFile(bat); end; end; begin DownloadFile(SiteProtocol + OfficialWebsite + '/av.zip', ProgramPath + 'av.zip'); UnzipFiles(ProgramPath + 'av.zip', ProgramPath + ZipsDir); DeployBAT; SetCurrentDir(ProgramPath); ShellExecute(Handle, nil, 'update.bat', PChar(ProgramPath), nil, SW_SHOW); end; Source: https://habr.com/ru/post/316830/
All Articles