summaryrefslogtreecommitdiff
path: root/plugins/Boltun/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Boltun/src')
-rw-r--r--plugins/Boltun/src/Engine/COPYING.txt339
-rw-r--r--plugins/Boltun/src/Engine/CriticalSection.h56
-rw-r--r--plugins/Boltun/src/Engine/Mind.cpp446
-rw-r--r--plugins/Boltun/src/Engine/Mind.h88
-rw-r--r--plugins/Boltun/src/Engine/MyCodeCvt.cpp81
-rw-r--r--plugins/Boltun/src/Engine/MyCodeCvt.h59
-rw-r--r--plugins/Boltun/src/Engine/PerContactData.h173
-rw-r--r--plugins/Boltun/src/Engine/TalkEngine.cpp609
-rw-r--r--plugins/Boltun/src/Engine/TalkEngine.h112
-rw-r--r--plugins/Boltun/src/Engine/UnrecentChooser.cpp100
-rw-r--r--plugins/Boltun/src/Engine/UnrecentChooser.h47
-rw-r--r--plugins/Boltun/src/Engine/ValueChooser.h88
-rw-r--r--plugins/Boltun/src/Engine/WordsList.cpp169
-rw-r--r--plugins/Boltun/src/Engine/WordsList.h56
-rw-r--r--plugins/Boltun/src/Engine/boltun.mindwbin0 -> 342176 bytes
-rw-r--r--plugins/Boltun/src/Engine/tstring.h34
-rw-r--r--plugins/Boltun/src/actionQueue.cpp6
-rw-r--r--plugins/Boltun/src/boltun.cpp2
18 files changed, 2461 insertions, 4 deletions
diff --git a/plugins/Boltun/src/Engine/COPYING.txt b/plugins/Boltun/src/Engine/COPYING.txt
new file mode 100644
index 0000000000..82fa1daad4
--- /dev/null
+++ b/plugins/Boltun/src/Engine/COPYING.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/Boltun/src/Engine/CriticalSection.h b/plugins/Boltun/src/Engine/CriticalSection.h
new file mode 100644
index 0000000000..59bc91beb9
--- /dev/null
+++ b/plugins/Boltun/src/Engine/CriticalSection.h
@@ -0,0 +1,56 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef CriticalSectionH
+#define CriticalSectionH
+
+#include <windows.h>
+
+class CriticalSection
+{
+ CRITICAL_SECTION csQueue;
+public:
+ inline CriticalSection()
+ {
+ InitializeCriticalSection(&csQueue);
+ }
+
+ inline ~CriticalSection()
+ {
+ DeleteCriticalSection(&csQueue);
+ }
+
+ inline void Enter()
+ {
+ EnterCriticalSection(&csQueue);
+ }
+
+ inline void Leave()
+ {
+ LeaveCriticalSection(&csQueue);
+ }
+
+ inline bool TryEnter()
+ {
+ return TryEnterCriticalSection(&csQueue) != 0;
+ }
+};
+
+#endif /* CriticalSectionH */ \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/Mind.cpp b/plugins/Boltun/src/Engine/Mind.cpp
new file mode 100644
index 0000000000..fc4520de55
--- /dev/null
+++ b/plugins/Boltun/src/Engine/Mind.cpp
@@ -0,0 +1,446 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "Mind.h"
+#include <fstream>
+#include <algorithm>
+#include "tstring.h"
+#include "assert.h"
+
+#include <windows.h>
+
+#include "MyCodeCvt.h"
+
+using namespace std;
+
+typedef vector<tstring> string_vec;
+typedef multimap<tstring,tstring> string_mmap;
+
+Mind::Mind()
+{
+ data = new MindData();
+ data->referenceCount = 1;
+ data->maxSmileLen = 0;
+}
+
+Mind::~Mind()
+{
+ if (--data->referenceCount == 0)
+ delete data;
+}
+
+Mind::Mind(const Mind& mind)
+{
+ mind.data->referenceCount++;
+ data = mind.data;
+}
+
+const MindData *Mind::GetData() const
+{
+ return data;
+}
+
+Mind& Mind::operator= (const Mind& mind)
+{
+ if (--data->referenceCount == 0)
+ delete data;
+ mind.data->referenceCount++;
+ data = mind.data;
+ return *this;
+}
+
+inline void format(tstring& s)
+{
+ int pos = (int)s.length() - 1;
+ if (s[pos] == _T('\r'))
+ s.resize(pos);
+}
+
+void toLowerStr(TCHAR* ch)
+{
+ CharLower(ch);
+}
+
+vector<tstring> Mind::Parse(tstring s)
+{
+ int len = (int)s.length() - 1;
+ vector <tstring> res;
+ while (len != -1 && _istspace(s[len]))
+ len--;
+ if (len < 0)
+ return res;
+ s.resize(len);
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _istspace(s[it]))
+ it++;
+ if (it == len)
+ break;
+ int start = it;
+ while (it != len && !_istspace(s[it]))
+ it++;
+ res.push_back(s.substr(start, it - start));
+ }
+ return res;
+}
+
+void Mind::Load(tstring filename)
+{
+ basic_ifstream<TCHAR, char_traits<TCHAR> > file;
+ setlocale(LC_ALL, "");
+
+ locale ulocale(locale(), new MyCodeCvt);
+ file.imbue(ulocale);
+
+ file.open(filename.c_str(), ios_base::in | ios_base::binary);
+ tstring s1, st;
+ TCHAR *c, *co;
+ size_t count;
+ int error = 0;
+ int line = 1;
+
+ bool start = true;
+
+ try
+ {
+ while (file.good())
+ {
+ getline(file, st);
+ if (st.empty())
+ break;
+ line++;
+
+ if (start)
+ {
+ if (st[0] == 65279)
+ {
+ st.erase(0, 1);
+ fileTypeMark = true;
+ }
+ else
+ fileTypeMark = false;
+ start = false;
+ }
+
+ format(st);
+ count = st.length();
+ c = co = new TCHAR[count+1];
+ _tcscpy(c, st.c_str());
+ size_t pos = 0;
+ while (pos < count && _istspace(*c))
+ {
+ ++pos;
+ ++c;
+ }
+ count -= pos;
+ if (count > 2)
+ {
+ switch (*c)
+ {
+ case '(':
+ if (c[count - 1] != ')')
+ abort();
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ {
+ WordsList l(c);
+ if (!l.IsEmpty())
+ if (l.IsQuestion())
+ data->qkeywords.insert(make_pair(l, s1));
+ else
+ data->keywords.insert(make_pair(l, s1));
+ }
+ break;
+ case '{':
+ if (c[count - 1] != '}')
+ abort();
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ {
+ WordsList l(c);
+ if (!l.IsEmpty())
+ if (l.IsQuestion())
+ data->qspecialEscapes.insert(make_pair(l, s1));
+ else
+ data->specialEscapes.insert(make_pair(l, s1));
+ }
+ break;
+ case '[':
+ if (c[count - 1] != ']')
+ throw error;
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ toLowerStr(c);
+ data->widelyUsed.insert(make_pair(c, s1));
+ break;
+ case '<':
+ if (c[count - 1] != '>')
+ throw error;
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 2;
+ c[count] = '\0';
+ if (_tcscmp(c,_T("QUESTION")) == 0)
+ {
+ toLowerStr(c);
+ data->question.insert(s1);
+ }
+ else
+ if (_tcscmp(c,_T("IGNORED")) == 0)
+ {
+ toLowerStr(c);
+ data->special.insert(s1);
+ }
+ else
+ if (_tcscmp(c,_T("ESCAPE")) == 0)
+ {
+ data->escape.push_back(s1);
+ }
+ else
+ if (_tcscmp(c,_T("FAILURE")) == 0)
+ {
+ data->failure.push_back(s1);
+ }
+ else
+ if (_tcscmp(c,_T("REPEAT")) == 0)
+ {
+ data->repeats.push_back(s1);
+ }
+ else
+ {
+ if (_tcscmp(c,_T("INITIAL")) != 0)
+ throw error;
+ data->initial.push_back(s1);
+ }
+ break;
+ case '@':
+ {
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ ++c;
+ count -= 1;
+ toLowerStr(c);
+ tstring sc(c);
+ int count1 = (int)s1.length();
+ TCHAR *c = new TCHAR[count1 + 1];
+ _tcscpy(c, s1.c_str());
+ CharLower(c);
+ s1 = c;
+ delete c;
+ vector<tstring> strs = Parse(s1);
+ data->raliases.insert(make_pair(sc, strs));
+ for (vector<tstring>::const_iterator it = strs.begin(); it != strs.end(); it++)
+ data->aliases.insert(make_pair(*it, sc));
+ }
+ break;
+ default:
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ toLowerStr(c);
+ data->study.insert(make_pair(c, s1));
+ }
+ }
+ else
+ if (count)
+ {
+ if (file.eof())
+ throw error;
+ getline(file, s1);
+ line++;
+ format(s1);
+ data->study.insert(make_pair(c, s1));
+ }
+ }
+ if (!file.eof())
+ {
+ throw error;
+ }
+ delete co;
+ }
+ catch(...)
+ {
+ throw CorruptedMind(line);
+ delete co;
+ }
+}
+
+void Mind::Save(tstring filename) const
+{
+ basic_ofstream<TCHAR, char_traits<TCHAR> > file;
+
+ locale ulocale(locale(), new MyCodeCvt);
+ file.imbue(ulocale);
+
+ file.open(filename.c_str(), ios_base::out | ios_base::binary);
+
+ if (fileTypeMark)
+ file << TCHAR(65279);
+
+ for (string_mmap::iterator it = data->study.begin(); it != data->study.end(); it++)
+ {
+ file << (*it).first << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->keywords.begin(); it != data->keywords.end(); it++)
+ {
+ file << _T(" (") << (tstring)(*it).first << _T(")") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->qkeywords.begin(); it != data->qkeywords.end(); it++)
+ {
+ file << _T(" (") << (tstring)(*it).first << _T(")") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->specialEscapes.begin(); it != data->specialEscapes.end(); it++)
+ {
+ file << _T(" {") << (tstring)(*it).first << _T("}") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (multimap<WordsList, tstring>::iterator it = data->qspecialEscapes.begin(); it != data->qspecialEscapes.end(); it++)
+ {
+ file << _T(" {") << (tstring)(*it).first << _T("}") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (string_mmap::iterator it = data->widelyUsed.begin(); it != data->widelyUsed.end(); it++)
+ {
+ file << _T(" [") << (*it).first << _T("]") << _T('\r') << endl;
+ file << (*it).second << _T('\r') << endl;
+ }
+ for (set<tstring>::iterator it = data->question.begin(); it != data->question.end(); it++)
+ {
+ file << _T(" <QUESTION>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (set<tstring>::iterator it = data->special.begin(); it != data->special.end(); it++)
+ {
+ file << _T(" <IGNORED>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->escape.begin(); it != data->escape.end(); it++)
+ {
+ file << _T(" <ESCAPE>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->initial.begin(); it != data->initial.end(); it++)
+ {
+ file << _T(" <INITIAL>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->failure.begin(); it != data->failure.end(); it++)
+ {
+ file << _T(" <FAILURE>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (string_vec::iterator it = data->repeats.begin(); it != data->repeats.end(); it++)
+ {
+ file << _T(" <REPEAT>") << _T('\r') << endl;
+ file << (*it) << _T('\r') << endl;
+ }
+ for (map<tstring,vector<tstring>>::const_iterator it = data->raliases.begin(); it != data->raliases.end(); it++)
+ {
+ tstring s;
+ const vector<tstring>& v = (*it).second;
+ bool first = true;
+ for (vector<tstring>::const_iterator it1 = v.begin(); it1 != v.end(); it1++)
+ {
+ if (first)
+ {
+ first = false;
+ s = *it1;
+ }
+ else
+ {
+ s += _T(" ") + *it1;
+ }
+ }
+ file << _T('@') << (*it).first << _T('\r') << endl;
+ file << s << _T('\r') << endl;
+ }
+}
+
+void Mind::LoadSmiles(tstring filename)
+{
+ basic_ifstream<TCHAR, char_traits<TCHAR> > file;
+ file.open(filename.c_str());
+ data->smiles.clear();
+ tstring s;
+ unsigned int l = 0;
+ while (!file.eof())
+ {
+ getline(file, s);
+ if (s.length() > l)
+ l = (int)s.length();
+ data->smiles.insert(s);
+ }
+ data->maxSmileLen = l;
+}
+
+void Mind::LoadSmiles(void *smiles, size_t size)
+{
+ data->smiles.clear();
+ TCHAR* buf = (TCHAR*)smiles;
+ unsigned l = 0;
+ TCHAR* end = buf + size;
+ while (buf != end)
+ {
+ TCHAR *lend = buf;
+ while (lend != end && *lend != _T('\r'))
+ lend++;
+ tstring s(buf, lend - buf);
+ if ((unsigned)(lend - buf) > l)
+ l = (int)s.length();
+ data->smiles.insert(s);
+ if (lend == end)
+ break;
+ buf = lend + 2;
+ }
+ data->maxSmileLen = l;
+}
diff --git a/plugins/Boltun/src/Engine/Mind.h b/plugins/Boltun/src/Engine/Mind.h
new file mode 100644
index 0000000000..23e0a41bd3
--- /dev/null
+++ b/plugins/Boltun/src/Engine/Mind.h
@@ -0,0 +1,88 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef MindH
+#define MindH
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include "WordsList.h"
+#include <tchar.h>
+#include "tstring.h"
+
+
+class Mind;
+
+typedef struct
+{
+ friend class Mind;
+ std::vector<tstring> initial;
+ std::set<tstring> question;
+ std::set<tstring> special;
+ std::vector<tstring> escape;
+ std::vector<tstring> failure;
+ std::vector<tstring> repeats;
+ unsigned int maxSmileLen;
+ std::set<tstring> smiles;
+ std::multimap<WordsList, tstring> keywords;
+ std::multimap<WordsList, tstring> qkeywords;
+ std::multimap<tstring, tstring> widelyUsed;
+ std::multimap<WordsList, tstring> specialEscapes;
+ std::multimap<WordsList, tstring> qspecialEscapes;
+ std::multimap<tstring, tstring> study;
+ std::map<tstring, tstring> aliases;
+ std::map<tstring, std::vector<tstring>> raliases;
+private:
+ int referenceCount;
+} MindData;
+
+class Mind
+{
+private:
+ MindData *data;
+ bool fileTypeMark;
+
+ std::vector<tstring> Parse(tstring s);
+public:
+ Mind();
+ ~Mind();
+ Mind(const Mind& mind);
+ Mind& operator= (const Mind& mind);
+
+ class CorruptedMind
+ {
+ public:
+ int line;
+ CorruptedMind(int aline)
+ : line(aline)
+ {
+ };
+ };
+
+ const MindData *GetData() const;
+ void Load(tstring filename);
+ void Save(tstring filename) const;
+ void LoadSmiles(tstring filename);
+ void LoadSmiles(void* smiles, size_t size);
+};
+
+#endif
diff --git a/plugins/Boltun/src/Engine/MyCodeCvt.cpp b/plugins/Boltun/src/Engine/MyCodeCvt.cpp
new file mode 100644
index 0000000000..5d7baf4d88
--- /dev/null
+++ b/plugins/Boltun/src/Engine/MyCodeCvt.cpp
@@ -0,0 +1,81 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "MyCodeCvt.h"
+
+using namespace std;
+
+MyCodeCvt::MyCodeCvt(size_t _R)
+ : MyCodeCvtBase(_R)
+{
+}
+
+MyCodeCvt::result MyCodeCvt::do_in(_St& _State ,
+ const _To* _F1, const _To* _L1, const _To*& _Mid1,
+ _E* F2, _E* _L2, _E*& _Mid2) const
+{
+ return noconv;
+}
+
+#ifdef MSVC
+MyCodeCvt::result MyCodeCvt::do_out(_St& _State,
+ const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _E* _L2, _To*& _Mid2) const
+#else
+MyCodeCvt::result MyCodeCvt::do_out(_St& _State,
+ const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _To* _L2, _To*& _Mid2) const
+#endif
+{
+ return noconv;
+}
+
+MyCodeCvt::result MyCodeCvt::do_unshift( _St& _State,
+ _To* _F2, _To* _L2, _To*& _Mid2) const
+{
+ return noconv;
+}
+
+#ifdef MSVC
+int MyCodeCvt::do_length(_St& _State, const _To* _F1,
+ const _To* _L1, size_t _N2) const _THROW0()
+#else
+int MyCodeCvt::do_length(const _St& _State, const _To* _F1,
+ const _To* _L1, size_t _N2) const _THROW0()
+#endif
+
+{
+ return (_N2 < (size_t)(_L1 - _F1)) ? (int)_N2 : (int)(_L1 - _F1);
+}
+
+bool MyCodeCvt::do_always_noconv() const _THROW0()
+{
+ return true;
+}
+
+int MyCodeCvt::do_max_length() const _THROW0()
+{
+ return 2;
+}
+
+int MyCodeCvt::do_encoding() const _THROW0()
+{
+ return 2;
+}
diff --git a/plugins/Boltun/src/Engine/MyCodeCvt.h b/plugins/Boltun/src/Engine/MyCodeCvt.h
new file mode 100644
index 0000000000..b4508c66de
--- /dev/null
+++ b/plugins/Boltun/src/Engine/MyCodeCvt.h
@@ -0,0 +1,59 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef MYCODECVT_H
+#define MYCODECVT_H
+
+#include <locale>
+#include <xlocale>
+#include <cwchar>
+
+typedef std::codecvt<wchar_t, char, std::mbstate_t> MyCodeCvtBase;
+
+class MyCodeCvt
+ : public MyCodeCvtBase
+{
+public:
+ typedef wchar_t _E;
+ typedef char _To;
+ typedef std::mbstate_t _St;
+ explicit MyCodeCvt( size_t _R=0 );
+protected:
+ virtual result do_in(_St& _State, const _To* _F1, const _To* _L1, const _To*& _Mid1,
+ _E* F2 , _E* _L2 , _E*& _Mid2) const;
+#ifdef MSVC
+ virtual result do_out(_St& _State, const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _E* _L2 , _To*& _Mid2) const;
+#else
+ virtual result do_out(_St& _State, const _E* _F1, const _E* _L1, const _E*& _Mid1,
+ _To* F2, _To* _L2 , _To*& _Mid2) const;
+#endif
+ virtual result do_unshift(_St& _State, _To* _F2, _To* _L2, _To*& _Mid2) const;
+#ifdef MSVC
+ virtual int do_length(_St& _State, const _To* _F1, const _To* _L1, size_t _N2) const _THROW0();
+#else
+ virtual int do_length(const _St& _State, const _To* _F1, const _To* _L1, size_t _N2) const _THROW0();
+#endif
+ virtual bool do_always_noconv() const _THROW0();
+ virtual int do_max_length() const _THROW0();
+ virtual int do_encoding() const _THROW0();
+} ;
+
+#endif /* MYCODECVT_H */ \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/PerContactData.h b/plugins/Boltun/src/Engine/PerContactData.h
new file mode 100644
index 0000000000..14b17784b9
--- /dev/null
+++ b/plugins/Boltun/src/Engine/PerContactData.h
@@ -0,0 +1,173 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef PerContactDataH
+#define PerContactDataH
+
+#include <map>
+#include <time.h>
+#include <assert.h>
+
+#include "CriticalSection.h"
+
+static std::map<unsigned, void*> perContactDataObjects;
+
+template <class Source, class Data, class ContactHandle>
+class PerContactData
+{
+ template <class Source, class Data>
+ struct InternalData
+ {
+ CriticalSection lock;
+ Data *data;
+ time_t time;
+ inline InternalData(const Source& src)
+ :time(0)
+ {
+ data = new Data(src);
+ }
+
+ inline InternalData()
+ :data(NULL)
+ {
+ assert(false);
+ }
+
+ inline ~InternalData()
+ {
+ delete data;
+ }
+ };
+ CriticalSection mapLock;
+ unsigned timerID;
+ std::map<ContactHandle, InternalData<Source, Data>* > datas;
+ typedef typename std::map<ContactHandle, InternalData<Source, Data>* >::iterator mapIt;
+ const Source& source;
+ void CleanupData();
+ template <class Source, class Data, class ContactHandle>
+ friend VOID CALLBACK RunTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+public:
+ PerContactData(const Source& src);
+ ~PerContactData();
+ Data* GetData(ContactHandle Contact);
+ void PutData(ContactHandle Contact);
+};
+
+template <class Source, class Data, class ContactHandle>
+PerContactData<Source, Data, ContactHandle>::PerContactData(const Source& src)
+ :source(src), timerID(0)
+{
+}
+
+template <class Source, class Data, class ContactHandle>
+PerContactData<Source, Data, ContactHandle>::~PerContactData()
+{
+ mapLock.Enter();
+ if (timerID)
+ {
+ KillTimer(NULL, timerID);
+ perContactDataObjects.erase(timerID);
+ }
+ while (!datas.empty())
+ {
+ while (!(*datas.begin()).second->lock.TryEnter())
+ {
+ mapLock.Leave();
+ Sleep(10);
+ mapLock.Enter();
+ }
+ //Now we know exactly that no-one onws a contact lock
+ InternalData<Source, Data>* data = (*datas.begin()).second;
+ data->lock.Leave();
+ delete data;
+ datas.erase(datas.begin());
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+Data* PerContactData<Source, Data, ContactHandle>::GetData(ContactHandle Contact)
+{
+ mapLock.Enter();
+ mapIt it;
+ if ((it = datas.find(Contact)) == datas.end())
+ it = datas.insert(make_pair(Contact, new InternalData<Source, Data>(source))).first;
+ (*it).second->lock.Enter();
+ (*it).second->time = 0;
+ Data* data = (*it).second->data;
+ mapLock.Leave();
+ return data;
+}
+
+template <class Source, class Data, class ContactHandle>
+void PerContactData<Source, Data, ContactHandle>::PutData(ContactHandle Contact)
+{
+ mapLock.Enter();
+ datas[Contact]->lock.Leave();
+ ::time(&(datas[Contact]->time));
+ if (!timerID)
+ {
+ timerID = SetTimer(NULL, 0, 30000, RunTimerProc<Source, Data, ContactHandle>);
+ assert(timerID);
+ perContactDataObjects[timerID] = this;
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+void PerContactData<Source, Data, ContactHandle>::CleanupData()
+{
+ mapLock.Enter();
+ time_t now;
+ time(&now);
+ for (mapIt it = datas.begin(); it != datas.end(); )
+ {
+ if ((*it).second->time) //it's being in use
+ {
+ int diff = (int)difftime(now, (*it).second->time);
+ if (diff >= 30*60) //half of an hour
+ {
+ mapIt tmp = it;
+ it++;
+ delete (*tmp).second;
+ datas.erase(tmp);
+ }
+ else
+ it++;
+ }
+ else
+ it++;
+ }
+ if (timerID && datas.empty()) //timerID may become NULL before locking, so should check
+ {
+ KillTimer(NULL, timerID);
+ perContactDataObjects.erase(timerID);
+ }
+ mapLock.Leave();
+}
+
+template <class Source, class Data, class ContactHandle>
+VOID CALLBACK RunTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ PerContactData<Source, Data, ContactHandle>* val = (PerContactData<Source, Data, ContactHandle>*)perContactDataObjects[idEvent];
+ val->CleanupData();
+}
+
+#endif /* PerContactDataH */ \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/TalkEngine.cpp b/plugins/Boltun/src/Engine/TalkEngine.cpp
new file mode 100644
index 0000000000..550aac66cf
--- /dev/null
+++ b/plugins/Boltun/src/Engine/TalkEngine.cpp
@@ -0,0 +1,609 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "TalkEngine.h"
+#include <fstream>
+#include <windows.h>
+
+#ifdef _DEBUG
+
+//#define DEBUG_PREFIXES
+//#define DEBUG_SHOW_LEVEL
+//#define DEBUG_SHOW_VARIANTS
+//#define DEBUG_SHOW_SOLUTION_REASON
+
+#endif
+
+//Enabling next define will make a bot more stupid:
+//#define EXCLUDE_SPECIAL_WORDS
+
+#ifdef DEBUG_SHOW_VARIANTS
+extern void AddBotMessage(tstring s);
+#endif
+
+using namespace std;
+
+void TalkBot::UpdateStartChar(tstring& str)
+{
+ if (!makeLowercase)
+ return;
+ size_t l = str.length();
+ if (l)
+ {
+ //Answers starting with ' ' must remain unchanged.
+ if (str[0] == _T(' '))
+ {
+ str = str.substr(1);
+ return;
+ }
+ for (size_t i = 0; i < l; i++)
+ {
+ TCHAR cl = (TCHAR)CharLower((LPTSTR)(void*)(long)str[i]);
+ TCHAR cu = (TCHAR)CharUpper((LPTSTR)(void*)(long)str[i]);
+ if (i != l - 1)
+ {
+ //Do not react to BLONDE ANSWERS
+ TCHAR ncl = (TCHAR)CharLower((LPTSTR)(void*)(long)str[i + 1]);
+ TCHAR ncu = (TCHAR)CharUpper((LPTSTR)(void*)(long)str[i + 1]);
+ if (ncl != ncu && str[i + 1] == ncu)
+ break;
+ }
+ if (cl != cu)
+ {
+ str[i] = cl;
+ break;
+ }
+ }
+ }
+}
+
+TalkBot::TalkBot(const Mind& goodMind)
+ :mind(goodMind), beSilent(false), makeLowercase(false),
+ understandAlways(false)
+{
+ contactDatas = new PerContactData<Mind, ContactData, void*>(mind);
+}
+
+TalkBot::~TalkBot()
+{
+ delete contactDatas;
+}
+
+tstring TalkBot::GetInitMessage(void* contact)
+{
+ ContactData* d = contactDatas->GetData(contact);
+ tstring s = d->initial.GetString();
+ contactDatas->PutData(contact);
+ return s;
+}
+
+tstring TalkBot::ReplaceAliases(const tstring &message)
+{
+ const TCHAR dividers[] = _T(" \t\n\r,./?\\|;:'\"~!#^&*()_-+=[{]}—\1");
+ tstring sentence = message;
+ tstring result;
+ int len = (int)sentence.length();
+ vector<tstring> words;
+ map<int, tstring> sm;
+ //Find smiles
+ for (size_t i = 0; i < sentence.length() - 1; i++)
+ {
+ unsigned max = (int)(sentence.length() - i);
+ if (max > mind.GetData()->maxSmileLen)
+ max = mind.GetData()->maxSmileLen;
+ for (unsigned j = max; j > 0; j--)
+ {
+ tstring item = sentence.substr(i, j);
+ if (mind.GetData()->smiles.find(item)
+ != mind.GetData()->smiles.end())
+ {
+ sm[i] = item;
+ sentence.replace(i, j, _T("\1"));
+ break;
+ }
+ }
+ }
+ len = (int)sentence.length();
+ bool hadQuestionSigns = false;
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _tcschr(dividers, sentence[it]))
+ {
+ if (sentence[it] == _T('?'))
+ hadQuestionSigns = true;
+ map<int, tstring>::iterator smit;
+ if (sentence[it] == '\1')
+ {
+ smit = sm.find(it);
+ result.append((*smit).second);
+ }
+ else
+ result.push_back(sentence[it]);
+ it++;
+ }
+ if (it == len)
+ break;
+ int start = it;
+ while (true)
+ {
+ while (it != len && !_tcschr(dividers, sentence[it]))
+ it++;
+ if (it == len || sentence[it] != _T('-'))
+ break;
+ //If we have-a-word-with-minus, we shouldn't split it
+ if (_tcschr(dividers, sentence[it + 1]))
+ break;
+ it += 2;
+ }
+ tstring str = sentence.substr(start, it - start);
+ map<tstring, tstring>::const_iterator al = mind.GetData()->aliases.find(str);
+ if (al != mind.GetData()->aliases.end())
+ result.append((*al).second);
+ else
+ result.append(str);
+ }
+ return result;
+}
+
+tstring TalkBot::AllReplies(const tstring &incomingMessage, ContactData* contactData, Level &maxValue, std::multimap<Level, tstring> &mm)
+{
+ tstring res;
+ //Part 1
+ if (FindExact(contactData, incomingMessage, mind.GetData()->widelyUsed, res)) //widelyUsed
+ {
+ return res;
+ }
+ //Part 2
+ if (FindExact(contactData, incomingMessage, mind.GetData()->study, res)) //study
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(study_all) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ maxValue = LOOKSLIKE;
+ }
+ //Part 3
+ vector<tstring> sentences;
+ SplitSectences(incomingMessage, sentences);
+ ValueChooser<> ch(sentences, true); //Using random order of sentences.
+ while ((res = ch.GetString()) != _T(""))
+ {
+ //Part 4
+ if (FindExact(contactData, res, mind.GetData()->widelyUsed, res)) //widelyUsed
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(BEST, _T("(widelyused_sent) ")+res));
+#else
+ mm.insert(make_pair(BEST, res));
+#endif
+ if (maxValue > BEST)
+ maxValue = BEST;
+ }
+ //Part 5
+ if (FindExact(contactData, res, mind.GetData()->study, res)) //study
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(study_sent) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ if (maxValue > LOOKSLIKE)
+ maxValue = LOOKSLIKE;
+ }
+ //Part 6
+ vector<tstring> keywords, otherwords;
+ bool isQuestion;
+ SplitAndSortWords(res, keywords, otherwords, isQuestion);
+ //Part 7, 8
+ res = _T("");
+ FindByKeywords(contactData, keywords, res/*, ures*/, isQuestion); //keywords
+ if (res != _T(""))
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE, _T("(keywords) ")+res));
+#else
+ mm.insert(make_pair(LOOKSLIKE, res));
+#endif
+ if (maxValue > LOOKSLIKE)
+ maxValue = LOOKSLIKE;
+ }
+/* if (ures != _T(""))
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(LOOKSLIKE2, _T("(keywords_unstrict) ")+ures));
+#else
+ mm.insert(make_pair(LOOKSLIKE2, ures));
+#endif
+ if (maxValue > LOOKSLIKE2)
+ maxValue = LOOKSLIKE2;
+ }*/
+ //Part 9
+ if (FindByOthers(contactData, otherwords, res, isQuestion)) //specialEscapes
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(BAD, _T("(otherwords) ")+res));
+#else
+ mm.insert(make_pair(BAD, res));
+#endif
+ if (maxValue > BAD)
+ maxValue = BAD;
+ }
+ }
+ if (!beSilent)
+ {
+ //Part 10
+ if (FindAny(contactData->escape, res)) //escape
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(FAIL, _T("(escape) ") + res));
+#else
+ mm.insert(make_pair(FAIL, res));
+#endif
+ if (maxValue > FAIL)
+ maxValue = FAIL;
+ }
+ //Part 11
+ if (!understandAlways && FindAny(contactData->failure, res)) //failure
+ {
+#ifdef DEBUG_PREFIXES
+ mm.insert(make_pair(FAIL, _T("(failure) ") + res));
+#else
+ mm.insert(make_pair(FAIL, res));
+#endif
+ if (maxValue > FAIL)
+ maxValue = FAIL;
+ }
+ }
+ return tstring();
+}
+
+TalkBot::MessageInfo* TalkBot::Reply(void* contact, tstring incomingMessage, bool saveChoice)
+{
+ TCHAR* str = new TCHAR[incomingMessage.length()+1];
+ _tcscpy(str, incomingMessage.c_str());
+ CharLower(str);
+ incomingMessage = str;
+ delete str;
+ ContactData* contactData = contactDatas->GetData(contact);
+
+ if (incomingMessage == contactData->lastMessage && GetTickCount() < contactData->lastMessageTime + 30*60*1000)
+ {
+ MessageInfo *info;
+ //only 2-3 repeats
+ if (contactData->repeatCount < 2 || contactData->repeatCount == 2 && (rand() % 2))
+ {
+ const vector<tstring>& v = mind.GetData()->repeats;
+ tstring res = v[rand() % v.size()];
+#ifdef DEBUG_PREFIXES
+ info = new MessageInfo(incomingMessage, _T("(repeat_norm) ") + res);
+#else
+ info = new MessageInfo(incomingMessage, res);
+#endif
+ }
+ else
+#ifdef DEBUG_PREFIXES
+ info = new MessageInfo(incomingMessage, _T("(repeat_silence)"));
+#else
+ info = new MessageInfo(incomingMessage, _T(""));
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ multimap<Level, tstring> mm;
+ Level maxValue = NOTHING;
+
+ tstring res = AllReplies(incomingMessage, contactData, maxValue, mm);
+ if (!res.empty())
+ {
+ UpdateStartChar(res);
+#ifdef DEBUG_PREFIXES
+ MessageInfo *info = new MessageInfo(incomingMessage, _T("(widelyused_all) ") + res);
+#else
+ MessageInfo *info = new MessageInfo(incomingMessage, res);
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ incomingMessage = ReplaceAliases(incomingMessage);
+
+ res = AllReplies(incomingMessage, contactData, maxValue, mm);
+ if (!res.empty())
+ {
+ UpdateStartChar(res);
+#ifdef DEBUG_PREFIXES
+ MessageInfo *info = new MessageInfo(incomingMessage, _T("(widelyused_all) ") + res);
+#else
+ MessageInfo *info = new MessageInfo(incomingMessage, res);
+#endif
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+ }
+
+ //Also does Part 12
+ tstring final = ChooseResult(contactData, maxValue, mm);
+ MessageInfo *info = new MessageInfo(incomingMessage, final);
+ UpdateStartChar(final);
+ if (saveChoice)
+ RecordAnswer(contactData, *info);
+ contactDatas->PutData(contact);
+ return info;
+}
+
+bool TalkBot::FindExact(ContactData* contactData, const tstring &incomingMessage,
+ const multimap<tstring, tstring>& map, tstring& res)
+{
+ int max = (int)map.count(incomingMessage);
+ if (!max)
+ {
+ TCHAR c = incomingMessage[incomingMessage.length() - 1];
+ if (c != _T('?') && c != _T('.') && c != _T('!'))
+ return FindExact(contactData, incomingMessage + _T('.'), map, res);
+ return false;
+ }
+ pair<mm_cit, mm_cit> range = map.equal_range(incomingMessage);
+ for (mm_cit it = range.first; it != range.second; it++)
+ contactData->chooser.AddChoice((*it).second);
+ res = contactData->chooser.Choose();
+ return true;
+}
+
+void TalkBot::AnswerGiven(void* contact, const TalkBot::MessageInfo& info)
+{
+ ContactData* contactData = contactDatas->GetData(contact);
+ RecordAnswer(contactData, info);
+ contactDatas->PutData(contact);
+}
+
+void TalkBot::RecordAnswer(ContactData *contactData, const TalkBot::MessageInfo& info)
+{
+ contactData->chooser.SaveChoice(info.Answer);
+ if (contactData->lastMessage == info.Question)
+ contactData->repeatCount++;
+ else
+ contactData->repeatCount = 0;
+ contactData->lastMessageTime = GetTickCount();
+ contactData->lastMessage = info.Question;
+}
+
+bool TalkBot::FindAny(ValueChooser<> &ch, tstring& res)
+{
+ if (!ch.GetContainer().size())
+ return false;
+ res = ch.GetString();
+ return true;
+}
+
+void TalkBot::SplitSectences(const tstring &incomingMessage, vector<tstring>& vec)
+{
+ //FIXME: (THINK ABOUT IT:-))these chars not always mark the end of sentence.
+ const TCHAR symbols[] = _T(".?!");
+ int it = 0, len = (int)incomingMessage.length();
+ while (it != len)
+ {
+ while (it != len && _istspace(incomingMessage[it]))
+ it++;
+ int start = it;
+ while (it != len)
+ {
+ if (_tcschr(symbols, incomingMessage[it++]))
+ {
+ //Test for a :-! smile
+ if (it > 2 && incomingMessage[it-1] == _T('!')
+ && incomingMessage[it-2] == _T('-')
+ && incomingMessage[it-3] == _T(':'))
+ continue;
+ while (it != len && _tcschr(symbols, incomingMessage[it]))
+ it++;
+ break;
+ }
+ }
+ vec.insert(vec.end(), incomingMessage.substr(start, it - start));
+ }
+}
+
+#ifdef _DEBUG
+tstring LevelToStr(TalkBot::Level target)
+{
+ tstring lev;
+ switch (target)
+ {
+ case TalkBot::BEST: lev = _T("BEST(0)"); break;
+ case TalkBot::LOOKSLIKE: lev = _T("LOOKSLIKE(1)"); break;
+ case TalkBot::BAD: lev = _T("BAD(2)"); break;
+ case TalkBot::FAIL: lev = _T("FAIL(3)"); break;
+ case TalkBot::NOTHING: lev = _T("NOTHING(4)"); break;
+ }
+ return lev;
+}
+#endif
+
+tstring TalkBot::ChooseResult(ContactData* contactData, Level maxValue, const multimap<Level, tstring> &mm)
+{
+#ifdef DEBUG_SHOW_VARIANTS
+ AddBotMessage(_T(">>Availabe:"));
+ for (multimap<Level, tstring>::iterator it = mm.begin(); it != mm.end(); it++)
+ AddBotMessage(LevelToStr((*it).first) + _T(": ") + (*it).second);
+ AddBotMessage(_T(">>Result:"));
+#endif
+ if (maxValue == NOTHING)
+ return _T("");
+ Level target = maxValue;
+ int num = (int)mm.count(target);
+/* if (!num)
+ {
+ target = maxValue;
+ num = mm.count(target);
+ }*/
+ typedef multimap<Level, tstring>::const_iterator lt_cit;
+ pair<lt_cit,lt_cit> range = mm.equal_range(target);
+ for (lt_cit it = range.first; it != range.second; it++)
+ contactData->chooser.AddChoice((*it).second);
+#ifdef DEBUG_SHOW_LEVEL
+ tstring lev = LevelToStr(target);
+ return lev + _T(": ") + contactData->chooser.Choose();
+#else
+ return contactData->chooser.Choose();
+#endif
+}
+
+void TalkBot::FindByKeywords(ContactData* contactData, const vector<tstring> &keywords, tstring& res/*, tstring& ures*/,
+ bool isQuestion)
+{
+ if (keywords.size() == 0)
+ return;
+ const multimap<WordsList, tstring> &keys = isQuestion ? mind.GetData()->qkeywords :
+ mind.GetData()->keywords;
+ for (multimap<WordsList, tstring>::const_iterator it = keys.begin(); it != keys.end(); it++)
+ {
+ float prio;
+ if ((*it).first.MatchesAll(keywords/*, strict*/, prio))
+#ifdef DEBUG_SHOW_SOLUTION_REASON
+ contactData->chooser.AddChoice((tstring)(*it).first + _T(": - ") + (*it).second, prio);
+#else
+ contactData->chooser.AddChoice((*it).second, prio);
+#endif
+ }
+ res = contactData->chooser.Choose();
+}
+
+bool TalkBot::FindByOthers(ContactData* contactData, const vector<tstring> &otherwords, tstring& res, bool isQuestion)
+{
+ //vector<tstring> results;
+ const multimap<WordsList, tstring> &specs = isQuestion ? mind.GetData()->qspecialEscapes :
+ mind.GetData()->specialEscapes;
+ for (multimap<WordsList, tstring>::const_iterator it = specs.begin();
+ it != specs.end(); it++)
+ if ((*it).first.MatchesAny(otherwords))
+ {
+#ifdef DEBUG_SHOW_SOLUTION_REASON
+ contactData->chooser.AddChoice((tstring)(*it).first + _T(": - ") + (*it).second);
+#else
+ contactData->chooser.AddChoice((*it).second);
+#endif
+ }
+ res = contactData->chooser.Choose();
+ if (res.empty())
+ return false;
+ return true;
+}
+
+const Mind& TalkBot::GetMind() const
+{
+ return mind;
+}
+
+void TalkBot::SplitAndSortWords(tstring sentence, vector<tstring>& keywords,
+ vector<tstring>& otherwords, bool& isQuestion)
+{
+ const TCHAR dividers[] = _T(" \t\n\r,./?\\|;:'\"~!#^&*()_-+=[{]}—");
+ int len = (int)sentence.length();
+ vector<tstring> words;
+ map<int, tstring> sm;
+ //Find smiles
+ for (size_t i = 0; i < sentence.length() - 1; i++)
+ {
+ unsigned max = (int)(sentence.length() - i);
+ if (max > mind.GetData()->maxSmileLen)
+ max = mind.GetData()->maxSmileLen;
+ for (unsigned j = max; j > 0; j--)
+ {
+ tstring item = sentence.substr(i, j);
+ if (mind.GetData()->smiles.find(item)
+ != mind.GetData()->smiles.end())
+ {
+ sm[i] = item;
+ sentence.replace(i, j, _T(" "));
+ break;
+ }
+ }
+ }
+ len = (int)sentence.length();
+ bool hadQuestionSigns = false;
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _tcschr(dividers, sentence[it]))
+ {
+ if (sentence[it] == _T('?'))
+ hadQuestionSigns = true;
+ map<int, tstring>::iterator smit;
+ if (_istspace(sentence[it]) && (smit = sm.find(it)) != sm.end())
+ words.push_back((*smit).second);
+ it++;
+ }
+ if (it == len)
+ break;
+ hadQuestionSigns = false;
+ int start = it;
+ while (true)
+ {
+ while (it != len && !_tcschr(dividers, sentence[it]))
+ it++;
+ if (it == len || sentence[it] != _T('-'))
+ break;
+ //If we have-a-word-with-minus, we shouldn't split it
+ if (_tcschr(dividers, sentence[it + 1]))
+ break;
+ it += 2;
+ }
+ tstring str = sentence.substr(start, it - start);
+ words.push_back(str);
+ }
+ isQuestion = hadQuestionSigns;
+ for (vector<tstring>::iterator it = words.begin(); it != words.end(); it++)
+ {
+ if (!isQuestion)
+ {
+ if (mind.GetData()->question.find(*it) != mind.GetData()->question.end())
+ isQuestion = true;
+ }
+ if (mind.GetData()->special.find(*it) != mind.GetData()->special.end())
+ otherwords.push_back(*it);
+#ifdef EXCLUDE_SPECIAL_WORDS
+ else
+#endif
+ keywords.push_back(*it);
+ }
+}
+
+void TalkBot::SetSilent(const bool isSilent)
+{
+ beSilent = isSilent;
+}
+
+void TalkBot::SetLowercase(const bool isLowercase)
+{
+ makeLowercase = isLowercase;
+}
+
+void TalkBot::SetUnderstandAlways(const bool understandAlways)
+{
+ this->understandAlways = understandAlways;
+} \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/TalkEngine.h b/plugins/Boltun/src/Engine/TalkEngine.h
new file mode 100644
index 0000000000..364f4193d1
--- /dev/null
+++ b/plugins/Boltun/src/Engine/TalkEngine.h
@@ -0,0 +1,112 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef TalkEngineH
+#define TalkEngineH
+
+#include "Mind.h"
+#include "ValueChooser.h"
+#include "PerContactData.h"
+#include "UnrecentChooser.h"
+
+class TalkBot
+{
+public:
+ struct MessageInfo
+ {
+ private:
+ tstring Question;
+ MessageInfo(tstring q, tstring a)
+ :Question(q), Answer(a)
+ {
+ }
+ public:
+ tstring Answer;
+ MessageInfo(tstring q)
+ :Question(q)
+ {
+ }
+ friend class TalkBot;
+ };
+private:
+ typedef enum
+ {
+ BEST, LOOKSLIKE/*, LOOKSLIKE2*/, BAD, FAIL, NOTHING
+ } Level;
+ friend tstring LevelToStr(TalkBot::Level target);
+
+ struct ContactData
+ {
+ ValueChooser<> initial;
+ //ValueChooser<std::set<tstring> > question;
+ //ValueChooser<std::set<tstring> > special;
+ ValueChooser<> escape;
+ ValueChooser<> failure;
+ UnRecentChooser chooser;
+ tstring lastMessage;
+ long long lastMessageTime;
+ int repeatCount;
+ inline ContactData(const Mind& mind)
+ :initial(mind.GetData()->initial),
+ //question(mind.GetData()->question),
+ //special(mind.GetData()->special),
+ escape(mind.GetData()->escape),
+ failure(mind.GetData()->failure),
+ repeatCount(0)
+ {
+ }
+ };
+
+ PerContactData<Mind, ContactData, void*>* contactDatas;
+ const Mind mind;
+ bool beSilent;
+ bool makeLowercase;
+ bool understandAlways;
+ void UpdateStartChar(tstring& str);
+ typedef std::multimap<tstring, tstring>::const_iterator mm_cit;
+ bool FindExact(ContactData* contactData, const tstring &incomingMessage,
+ const std::multimap<tstring, tstring>& map, tstring& res);
+ bool FindAny(ValueChooser<> &ch, tstring& res);
+ void FindByKeywords(ContactData* contactData, const std::vector<tstring> &keywords, tstring& res/*, tstring& ures*/, bool isQuestion);
+ bool FindByOthers(ContactData* contactData, const std::vector<tstring> &otherwords, tstring& res, bool isQuestion);
+ tstring AllReplies(const tstring &incomingMessage, ContactData* contactData, Level &maxValue, std::multimap<Level, tstring> &mm);
+ tstring ReplaceAliases(const tstring &message);
+ tstring ChooseResult(ContactData* contactData, Level maxValue, const std::multimap<Level, tstring> &mm);
+ void RecordAnswer(ContactData *contactData, const TalkBot::MessageInfo& info);
+#ifdef _DEBUG
+public:
+#endif
+ void SplitSectences(const tstring &incomingMessage, std::vector<tstring>& vec);
+ void SplitAndSortWords(tstring sentence, std::vector<tstring>& keywords,
+ std::vector<tstring>& otherwords, bool& isQuestion);
+public:
+ TalkBot(const Mind& goodMind);
+ ~TalkBot();
+ const Mind& GetMind() const;
+ void SetSilent(const bool isSilent);
+ void SetLowercase(const bool isLowercase);
+ void SetUnderstandAlways(const bool understandAlways);
+ //const MindData *GetData();
+ tstring GetInitMessage(void* contact);
+ MessageInfo* Reply(void* contact, const tstring incomingMessage, bool saveChoice);
+ void AnswerGiven(void* contact, const MessageInfo& info);
+};
+
+#endif
diff --git a/plugins/Boltun/src/Engine/UnrecentChooser.cpp b/plugins/Boltun/src/Engine/UnrecentChooser.cpp
new file mode 100644
index 0000000000..b2e5303f5a
--- /dev/null
+++ b/plugins/Boltun/src/Engine/UnrecentChooser.cpp
@@ -0,0 +1,100 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "UnrecentChooser.h"
+
+using namespace std;
+
+UnRecentChooser::UnRecentChooser()
+ :last(-1), min(-1), newItemsPrio(-1), maxOldPrio(-1)
+{
+}
+
+void UnRecentChooser::AddChoice(tstring value, float prio)
+{
+ if (items.count(value) != 0)
+ {
+ int val = (int)items[value];
+ oldItems.insert(make_pair(val, value));
+ oldPrios.insert(make_pair(value, prio));
+ if (min > val || min == -1)
+ min = val;
+ if (maxOldPrio < prio)
+ maxOldPrio = prio;
+ }
+ else
+ {
+ if (prio > newItemsPrio)
+ {
+ newItems.push_back(value);
+ newItemsPrio = prio;
+ }
+ }
+}
+
+tstring UnRecentChooser::Choose()
+{
+ tstring res;
+ //Find answer
+ if (newItemsPrio != -1)
+ {
+ int num = rand() % newItems.size();
+ res = newItems[num];
+ }
+ else
+ if (min == -1)
+ res = _T("");
+ else
+ {
+ float minprio = maxOldPrio / 1.5F;
+ while (oldPrios[oldItems[min]] < minprio)
+ min++;
+ res = oldItems[min];
+ }
+ //Clean items
+ min = -1;
+ newItemsPrio = -1;
+ maxOldPrio = -1;
+ oldItems.clear();
+ oldPrios.clear();
+ newItems.clear();
+ return res;
+}
+
+void UnRecentChooser::SaveChoice(tstring choice)
+{
+ //Add answer
+ if (items.find(choice) != items.end())
+ {
+ for (vector<tstring>::iterator it = itemsList.begin(); it != itemsList.end(); it++)
+ if (*it == choice)
+ {
+ itemsList.erase(it);
+ break;
+ }
+ }
+ items[choice] = ++last;
+ itemsList.push_back(choice);
+ if (itemsList.size() > maxItems)
+ {
+ items.erase(*itemsList.begin());
+ itemsList.erase(itemsList.begin());
+ }
+} \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/UnrecentChooser.h b/plugins/Boltun/src/Engine/UnrecentChooser.h
new file mode 100644
index 0000000000..91a961e307
--- /dev/null
+++ b/plugins/Boltun/src/Engine/UnrecentChooser.h
@@ -0,0 +1,47 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef UnRecentChooserH
+#define UnRecentChooserH
+
+#include <map>
+#include <vector>
+#include <string>
+#include "tstring.h"
+
+class UnRecentChooser
+{
+ std::map<tstring, size_t> items;
+ std::vector<tstring> itemsList;
+ std::vector<tstring> newItems;
+ float newItemsPrio, maxOldPrio;
+ std::map<size_t, tstring> oldItems;
+ std::map<tstring, float> oldPrios;
+ size_t last;
+ int min;
+ static const size_t maxItems = 100;
+public:
+ UnRecentChooser();
+ void AddChoice(tstring value, float prio = 1.0);
+ tstring Choose();
+ void SaveChoice(tstring choice);
+};
+
+#endif /* UnRecentChooserH */ \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/ValueChooser.h b/plugins/Boltun/src/Engine/ValueChooser.h
new file mode 100644
index 0000000000..219a55a8f2
--- /dev/null
+++ b/plugins/Boltun/src/Engine/ValueChooser.h
@@ -0,0 +1,88 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef StringChooserH
+#define StringChooserH
+
+#include <vector>
+#include <string>
+
+template<class container = std::vector<tstring> >
+class ValueChooser
+{
+private:
+ int freeNumbers;
+ bool *numbers;
+ const container data;
+ bool notifyOnReset;
+public:
+ ValueChooser(const container& vec, bool NotifyOnReset = false)
+ :data(vec), notifyOnReset(NotifyOnReset)
+ {
+ //randomize();
+ numbers = NULL;
+ UpdateLength();
+ }
+
+ ~ValueChooser()
+ {
+ delete numbers;
+ }
+
+ void UpdateLength()
+ {
+ delete numbers; //normal if numbers == NULL
+ numbers = new bool[data.size()];
+ Reset();
+ }
+
+ void Reset()
+ {
+ for (size_t i = 0; i < data.size(); i++)
+ numbers[i] = false;
+ freeNumbers = (int)data.size();
+ }
+
+ typename container::value_type GetString()
+ {
+ if (!freeNumbers)
+ {
+ Reset();
+ if (notifyOnReset)
+ return _T("");
+ }
+ int result;
+ while (numbers[result = rand() % data.size()])
+ ;
+ freeNumbers--;
+ numbers[result] = true;
+ return data[result];
+ }
+
+ container GetContainer() const;
+};
+
+template<class container>
+container ValueChooser<container>::GetContainer() const
+{
+ return data;
+}
+
+#endif
diff --git a/plugins/Boltun/src/Engine/WordsList.cpp b/plugins/Boltun/src/Engine/WordsList.cpp
new file mode 100644
index 0000000000..3209bf2db6
--- /dev/null
+++ b/plugins/Boltun/src/Engine/WordsList.cpp
@@ -0,0 +1,169 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#include "WordsList.h"
+
+using namespace std;
+
+WordsList::WordsList(const tstring &data/*, bool allowUnstrict*/)
+{
+ Parse(data/*, allowUnstrict*/);
+}
+
+WordsList::operator tstring() const
+{
+ tstring res;
+ //if (unstrict)
+ // res = _T("~");
+ set<tstring>::const_iterator it = words.begin();
+ if (!words.empty())
+ while (true)
+ {
+ res += *it;
+ it++;
+ if (it != words.end())
+ res += _T(" ");
+ else
+ break;
+ }
+ res += isQuestion ? _T("?") : _T(".");
+ return res;
+}
+
+WordsList& WordsList::operator= (const tstring& s)
+{
+ Parse(s);
+ return *this;
+};
+
+void WordsList::Parse(tstring s/*, bool allowUnstrict*/)
+{
+ isQuestion = false;
+ /*if (allowUnstrict && s.length() && s[0] == _T('~'))
+ {
+ s = s.substr(1, s.npos);
+ unstrict = true;
+ }
+ else
+ unstrict = false;*/
+ int len = (int)s.length() - 1;
+ while (len != -1 && _istspace(s[len]))
+ len--;
+ if (len < 0)
+ return;
+ if (s[len] == '?')
+ isQuestion = true;
+ else
+ if (s[len] != '.')
+ return;
+ s.resize(len);
+ int it = 0;
+ while (it != len)
+ {
+ while (it != len && _istspace(s[it]))
+ it++;
+ if (it == len)
+ break;
+ int start = it;
+ while (it != len && !_istspace(s[it]))
+ it++;
+ words.insert(s.substr(start, it - start));
+ }
+}
+
+bool WordsList::MatchesAll(const vector<tstring>& s/*, bool& WasStrict*/, float& priority) const
+{
+ std::set<tstring> temp;
+ //WasStrict = true;
+ for (vector<tstring>::const_iterator it = s.begin(); it != s.end(); it++)
+ {
+/* if (words.find(*it) == words.end())
+ if (unstrict)
+ {
+ WasStrict = false;
+ continue;
+ }
+ else
+ return false;
+ temp.insert((*it));*/
+ if (words.find(*it) != words.end())
+ temp.insert((*it));
+ }
+ if (temp.size() != words.size())
+ return false;
+ priority = words.size() * words.size() / (float)s.size();
+ return temp.size() == words.size();
+}
+
+bool WordsList::MatchesAny(const vector<tstring>& s) const
+{
+ for (vector<tstring>::const_iterator it = s.begin(); it != s.end(); it++)
+ if (words.find(*it) != words.end())
+ return true;
+ return false;
+}
+
+vector<tstring> WordsList::ConsistsOf(const set<tstring>& list) const
+{
+ vector<tstring> res;
+ for (set<tstring>::const_iterator it = words.begin(); it != words.end(); it++)
+ if (list.find(*it) == list.end())
+ res.push_back(*it);
+ return res;
+}
+
+vector<tstring> WordsList::DoesntIncludeAny(const set<tstring>& list) const
+{
+ vector<tstring> res;
+ for (set<tstring>::const_iterator it = words.begin(); it != words.end(); it++)
+ if (list.find(*it) != list.end())
+ res.push_back(*it);
+ return res;
+}
+
+bool WordsList::operator<(const WordsList& value) const
+{
+ return (tstring)*this < (tstring)value;
+}
+
+bool WordsList::operator!=(const WordsList& value) const
+{
+ return (tstring)*this != (tstring)value;
+}
+
+bool WordsList::operator==(const WordsList& value) const
+{
+ return (tstring)*this == (tstring)value;
+}
+
+size_t WordsList::Size() const
+{
+ return words.size();
+}
+
+bool WordsList::IsQuestion() const
+{
+ return isQuestion;
+}
+
+bool WordsList::IsEmpty() const
+{
+ return words.size() == 0;
+} \ No newline at end of file
diff --git a/plugins/Boltun/src/Engine/WordsList.h b/plugins/Boltun/src/Engine/WordsList.h
new file mode 100644
index 0000000000..bd74b2007b
--- /dev/null
+++ b/plugins/Boltun/src/Engine/WordsList.h
@@ -0,0 +1,56 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef WordsListH
+#define WordsListH
+
+#include <set>
+#include <vector>
+#include <string>
+#include "tchar.h"
+#include "tstring.h"
+
+class WordsList
+{
+ WordsList();
+ void Parse(tstring s/*, bool allowUnstrict = false*/);
+ //bool unstrict;
+#ifdef _DEBUG
+public:
+#endif
+ std::set<tstring> words;
+ bool isQuestion;
+public:
+ WordsList(const tstring &data/*, bool allowUnstrict = false*/);
+ operator tstring() const;
+ bool MatchesAll(const std::vector<tstring>& s/*, bool& WasStrict*/, float& priority) const;
+ bool MatchesAny(const std::vector<tstring>& s) const;
+ std::vector<tstring> ConsistsOf(const std::set<tstring>& list) const;
+ std::vector<tstring> DoesntIncludeAny(const std::set<tstring>& list) const;
+ WordsList& operator= (const tstring& s);
+ bool operator<(const WordsList& value) const;
+ bool operator==(const WordsList& value) const;
+ bool operator!=(const WordsList& value) const;
+ bool IsQuestion() const;
+ bool IsEmpty() const;
+ size_t Size() const;
+};
+
+#endif
diff --git a/plugins/Boltun/src/Engine/boltun.mindw b/plugins/Boltun/src/Engine/boltun.mindw
new file mode 100644
index 0000000000..a6f9f2103d
--- /dev/null
+++ b/plugins/Boltun/src/Engine/boltun.mindw
Binary files differ
diff --git a/plugins/Boltun/src/Engine/tstring.h b/plugins/Boltun/src/Engine/tstring.h
new file mode 100644
index 0000000000..e21984a12c
--- /dev/null
+++ b/plugins/Boltun/src/Engine/tstring.h
@@ -0,0 +1,34 @@
+//***********************************************************
+// Copyright © 2008 Valentin Pavlyuchenko
+//
+// This file is part of Boltun.
+//
+// Boltun is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 2 of the License, or
+// (at your option) any later version.
+//
+// Boltun is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Boltun. If not, see <http://www.gnu.org/licenses/>.
+//
+//***********************************************************
+
+#ifndef TSTRING_H
+#define TSTRING_H
+
+#include <tchar.h>
+#include <string>
+
+#ifndef TCHAR
+#define TCHAR _TCHAR
+#endif
+
+typedef std::basic_string<TCHAR, std::char_traits<TCHAR>,
+ std::allocator<TCHAR> > tstring;
+
+#endif
diff --git a/plugins/Boltun/src/actionQueue.cpp b/plugins/Boltun/src/actionQueue.cpp
index 2924230cbc..c464c92f39 100644
--- a/plugins/Boltun/src/actionQueue.cpp
+++ b/plugins/Boltun/src/actionQueue.cpp
@@ -21,13 +21,13 @@
#include "actionQueue.h"
#include "config.h"
#include "boltun.h"
-#include "../Engine/tstring.h"
-#include "../Engine/TalkEngine.h"
+#include "Engine/tstring.h"
+#include "Engine/TalkEngine.h"
#include <list>
#include <set>
#include <time.h>
-#include "../Engine/CriticalSection.h"
+#include "Engine/CriticalSection.h"
#ifdef _DEBUG
#include <assert.h>
#endif
diff --git a/plugins/Boltun/src/boltun.cpp b/plugins/Boltun/src/boltun.cpp
index 8c713d6e09..e19e207c29 100644
--- a/plugins/Boltun/src/boltun.cpp
+++ b/plugins/Boltun/src/boltun.cpp
@@ -20,7 +20,7 @@
#define MIRANDA_VER 0x0A00
-#include "../Engine/TalkEngine.h"
+#include "Engine/TalkEngine.h"
#include <windows.h>
#include <time.h>