diff --git a/noobhack/game/brain.py b/noobhack/game/brain.py index 343bc25..4954903 100644 --- a/noobhack/game/brain.py +++ b/noobhack/game/brain.py @@ -5,10 +5,15 @@ import re +from noobhack import ui +import vt102 + from noobhack.game.graphics import ibm from noobhack.game import shops, status, intrinsics, sounds, dungeon from noobhack.game.events import dispatcher +import intrinsicsC + class Brain: """ GrraaAAaaaAaaaa... braaaAAaaains... @@ -53,12 +58,12 @@ def _dispatch_level_feature_events(self, data): if match is not None: dispatcher.dispatch("level-feature", feature) - def _dispatch_intrinsic_events(self, data): - for name, messages in intrinsics.messages.iteritems(): - for message, value in messages.iteritems(): - match = re.search(message, data, re.I | re.M) - if match is not None: - dispatcher.dispatch("intrinsic", name, value) + def _dispatch_intrinsic_events(self, data): + intrinsicsResult = intrinsicsC.DispatchIntrinsics( + intrinsics.messages, self.term.display) + + for name, value in intrinsicsResult.items(): + dispatcher.dispatch("intrinsic", name, value) def _dispatch_status_events(self, data): """ diff --git a/noobhack/game/cpp/intrinsics.cpp b/noobhack/game/cpp/intrinsics.cpp new file mode 100644 index 0000000..8b1e907 --- /dev/null +++ b/noobhack/game/cpp/intrinsics.cpp @@ -0,0 +1,178 @@ +#include + +#include + +#include +#include +#include + +using namespace std; + +namespace +{ + static bool firstTime = true; + + struct TextSearchItem + { + TextSearchItem() + {} + + TextSearchItem(const string& name, const wstring& searchTerm, bool result) + : name_(name), + searchTerm_(searchTerm), + result_(result) + {} + + string name_; + wstring searchTerm_; + bool result_; // intrinsic/status enabled or disabled + }; + + typedef vector TextSearchItems; + static TextSearchItems searchItems; + + string ToStr(PyObject* pyOb) + { + // is type-check needed? + return PyString_AsString(pyOb); + } + + wstring ToWStr(PyObject* pyOb) + { + // is type-check needed? + const string str = PyString_AsString(pyOb); + return wstring(str.begin(), str.end()); + } + + bool ToBool(PyObject* pyOb) + { + // if PyBool_check(pyOb) ... is type-check needed? + return Py_True == pyOb; + } + +static void ParseDictionary(PyObject* dictionary) + { + firstTime = false; // we don't want to do this again... + + PyObject *keys = PyDict_Keys(dictionary); + for(int i = 0; i < PyList_Size(keys); ++i) + { + PyObject* pyItemName = PyList_GetItem(keys, i); + const string itemName = ToStr(pyItemName); + + PyObject *itemSearchTerms = PyDict_GetItem(dictionary, pyItemName); + if(!PyDict_Check(itemSearchTerms)) + { + continue; + } + + PyObject *subKeys = PyDict_Keys(itemSearchTerms); + + for(int j = 0; j < PyList_Size(subKeys); ++j) + { + PyObject* pySearchTerm = PyList_GetItem(subKeys, j); + + const wstring wsSearchTerm = ToWStr(pySearchTerm); + bool searchTermValue = ToBool(PyDict_GetItem(itemSearchTerms, pySearchTerm)); + + searchItems.push_back(TextSearchItem(itemName, wsSearchTerm, searchTermValue)); + } + } + } +} + +static PyObject* DispatchIntrinsics(PyObject *self, PyObject* args) +{ + if(PyTuple_Size(args) != 2) + { + return PyDict_New(); + } + + if(firstTime) + { + PyObject *dictionary = PyTuple_GetItem(args, 0); + // is type-check needed? + if(PyDict_Check(dictionary)) + { + ParseDictionary(dictionary); + } + else + { + return NULL; + } + } + + typedef pair StdResult; + typedef vector< StdResult > StdResults; + StdResults stdResults; + + PyObject *messageData = PyTuple_GetItem(args, 1); + // is type-check needed? + if(PyList_Check(messageData)) + { + int listSize = PyList_Size(messageData); + + wostringstream lines; + + for(int i=0 ; i < listSize ; ++i) + { + PyObject* line = PyList_GetItem(messageData, i); + if(!PyUnicode_Check(line)) + { + continue; + } + + const wstring wsLine( reinterpret_cast< wchar_t* >( PyUnicode_AS_UNICODE(line) ) ); + lines << wsLine; + + if(wsLine == L"") + { + continue; + } + } + + const wstring fullScreenData = lines.str(); + + for(TextSearchItems::const_iterator it = searchItems.begin(); + it != searchItems.end(); + ++it) + { + if(boost::regex_search(fullScreenData, boost::wregex(it->searchTerm_))) + { + const string name(it->name_); + const wstring wsName(name.begin(), name.end()); + + if(it->result_) + { + stdResults.push_back( StdResult(it->name_, true) ); + } + else + { + stdResults.push_back( StdResult(it->name_, false) ); + } + } + } + + } + + PyObject* pyResults = PyDict_New(); + for(StdResults::const_iterator it = stdResults.begin(); it != stdResults.end(); ++it) + { + const string name(it->first); + PyDict_SetItem(pyResults, PyString_FromString(name.c_str()), it->second ? Py_True : Py_False); + } + return pyResults; +} + + +static PyMethodDef IntrinsicsMethods[] = +{ + {"DispatchIntrinsics", DispatchIntrinsics, METH_VARARGS, "Figure out Intrinsics changed..."}, + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC initintrinsicsC() +{ + (void) Py_InitModule("intrinsicsC", IntrinsicsMethods); +} + diff --git a/noobhack/game/intrinsics.py b/noobhack/game/intrinsics.py index b5894e2..8ddd7f9 100644 --- a/noobhack/game/intrinsics.py +++ b/noobhack/game/intrinsics.py @@ -1,42 +1,42 @@ messages = { "Warning": { - "You feel sensitive!":True, - "You feel less sensitive!":False, + "You feel sensitive":True, + "You feel less sensitive":False, }, "Shock resistance": { - "Your health currently feels amplified!":True, - "You feel insulated!":True, + "Your health currently feels amplified":True, + "You feel insulated":True, "You are shock resistant":True, - "You feel grounded in reality.":True, + "You feel grounded in reality":True, "You feel conductive":False }, "Fire resistance": { - "You be chillin'.":True, - "You feel cool!":True, + "You be chillin'":True, + "You feel cool":True, "You are fire resistant":True, - "You feel a momentary chill.":True, - "You feel warmer!":False + "You feel a momentary chill":True, + "You feel warmer":False }, "Cold resistance": { "You are cold resistant":True, - "You feel warm!":True, - "You feel full of hot air.":True, - "You feel cooler!":False + "You feel warm":True, + "You feel full of hot air":True, + "You feel cooler":False }, - "Disintegration resist.": { + "Disintegration resist": { "You are disintegration-resistant":True, - "You feel very firm.":True, - "You feel totally together, man.":True + "You feel very firm":True, + "You feel totally together, man":True }, "Poison resistance": { "You are poison resistant":True, "You feel( especially)? (healthy)|(hardy)":True, - "You feel a little sick!":False + "You feel a little sick":False }, "Sleep resistance": { "You are sleep resistant":True, "You feel( wide)? awake":True, - "You feel tired!":False + "You feel tired":False }, "Aggravate monster": { "You feel that monsters are aware of your presence":True, @@ -46,22 +46,22 @@ "You feel vulnerable":False }, "Invisible": { - "You feel hidden!":True + "You feel hidden":True }, "See invisible": { - "You see an image of someone stalking you.":True, + "You see an image of someone stalking you":True, "You feel transparent":True, "You feel very self-conscious":True, "Your vision becomes clear":True }, "Searching": { - "You feel perceptive!":True + "You feel perceptive":True }, "Speed": { - "You feel quick!":True, - "You feel yourself speed up.":True, # speed boots put on (want this here?)! - "You feel yourself slow down.":False, # speed boots removed (want this here?)! - "You feel slow!":False + "You feel quick":True, + "You feel yourself speed up":True, # speed boots put on (want this here?)! + "You feel yourself slow down":False, # speed boots removed (want this here?)! + "You feel slow":False }, "Teleportitis": { "You feel very jumpy":True, @@ -70,9 +70,9 @@ }, "Teleport control": { "You feel in control of yourself":True, - "You feel controlled!":True, + "You feel controlled":True, "You feel centered in your personal space":True, - "You feel less jumpy":False + "You feel less jumpy":False }, "Telepathy": { "You feel in touch with the cosmos":True, diff --git a/scripts/noobhack b/scripts/noobhack index ac86b5c..53ee22f 100755 --- a/scripts/noobhack +++ b/scripts/noobhack @@ -10,6 +10,10 @@ import optparse import cPickle as pickle +#import profile + +import cProfile + import vt102 from noobhack import ui, telnet, process, proxy @@ -314,9 +318,10 @@ class Noobhack: if __name__ == "__main__": locale.setlocale(locale.LC_ALL, "") - + hack = Noobhack() try: - curses.wrapper(hack.run) +# profile.run('curses.wrapper(hack.run); print') + cProfile.run('curses.wrapper(hack.run)', "noobhack.trace") except IOError, exit_message: print exit_message diff --git a/setup.py b/setup.py index 9b9c97d..c8626fa 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,8 @@ -from distutils.core import setup +from distutils.core import setup, Extension + +intrinsicsModule = Extension('intrinsicsC', + sources = ['noobhack/game/cpp/intrinsics.cpp'], + libraries = ['boost_regex']) setup( name="noobhack", @@ -11,5 +15,8 @@ requires=["vt102 (>=0.3.2)"], packages=["noobhack", "noobhack.game"], scripts=["scripts/noobhack"], - license="Lesser General Public License v3.0" + license="Lesser General Public License v3.0", + ext_modules=[intrinsicsModule], #Extension('intrinsicsC', ['noobhack/game/cpp/intrinsics.cpp'])], ) + + diff --git a/test/game/test_intrinsics.py b/test/game/test_intrinsics.py new file mode 100644 index 0000000..4e2c796 --- /dev/null +++ b/test/game/test_intrinsics.py @@ -0,0 +1,75 @@ +import unittest + +import intrinsicsC + +testIntrinsics = { + "valA": { "aaa" : True, + "aaZ" : False }, + "valB": { "bbb" :True, + "bbZ" : False }, + } + +class IntrinsicsTest(unittest.TestCase): + + def test_parseOnlyNoResult(self): + testEmpty = "" + uTestEmpty = testEmpty.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestEmpty]) + self.assertEqual(len(res), 0) + + def test_found_valA_Enable(self): + testInput = "blah aaa blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 1) + self.assertEqual(res.items()[0][0], "valA") + self.assertEqual(res.items()[0][1], True) + + def test_found_valA_Disable(self): + testInput = "blah aaZ blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 1) + self.assertEqual(res.items()[0][0], "valA") + self.assertEqual(res.items()[0][1], False) + + def test_found_valA_EnableDisable(self): + testInput = "aaa blah aaZ blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 1) + self.assertEqual(res.items()[0][0], "valA") + self.assertEqual(res.items()[0][1], False) + + def test_found_valAvalB_Enabled(self): + testInput = "aaa blah bbb blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 2) + self.assertEqual(res.items()[0][0], "valB") + self.assertEqual(res.items()[0][1], True) + self.assertEqual(res.items()[1][0], "valA") + self.assertEqual(res.items()[1][1], True) + + def test_found_valBvalA_Enabled(self): + testInput = "bbb blah aaa blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 2) + self.assertEqual(res.items()[0][0], "valB") + self.assertEqual(res.items()[0][1], True) + self.assertEqual(res.items()[1][0], "valA") + self.assertEqual(res.items()[1][1], True) + + def test_found_valA_Enabled_valB_Disabled(self): + testInput = "aaa blah bbZ blah" + uTestInput = testInput.decode('utf-8') + res = intrinsicsC.DispatchIntrinsics(testIntrinsics, [uTestInput]) + self.assertEqual(len(res), 2) + self.assertEqual(res.items()[0][0], "valB") + self.assertEqual(res.items()[0][1], False) + self.assertEqual(res.items()[1][0], "valA") + self.assertEqual(res.items()[1][1], True) + +if __name__ == "__main__": + unittest.main()