charman.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. #include "charman.h"
  2. #include "render.h"
  3. #include "utils.h"
  4. #include "zf_log.h"
  5. #include <ctype.h>
  6. #include <regex>
  7. #include <string>
  8. #include <vector>
  9. void CharMan::validate(void) {
  10. // Control buffer debugging output.
  11. #ifndef NO_BUFFER_DEBUG
  12. bool valid = true;
  13. ZF_LOGE("validate: text_offsets %d", (int)text_offsets.size());
  14. ZF_LOGE("validate work size %d, buffer size %d, text size %d",
  15. (int)work.size(), (int)buffer.size(), (int)text.size());
  16. for (int x = 0; x < (int)text_offsets.size(); ++x) {
  17. int offset = text_offsets[x];
  18. ZF_LOGE("%d : %d", x, offset);
  19. if (offset >= 0) {
  20. if (text[x] != work[offset]) {
  21. ZF_LOGE("validate: %d off %d [%c != %c]", x, offset, text[x],
  22. work[offset]);
  23. valid = false;
  24. }
  25. if ((text[x] != ' ') && (text[x] != buffer[offset])) {
  26. ZF_LOGE("validate: %d off %d [%c != %c]", x, offset, text[x],
  27. buffer[offset]);
  28. valid = false;
  29. }
  30. }
  31. }
  32. if (!valid) {
  33. ZF_LOGE("* NOT VALID* Somethings hosed.");
  34. diagnostics();
  35. }
  36. #endif
  37. }
  38. void CharMan::diagnostics(void) {
  39. // Control buffer debugging output.
  40. #ifndef NO_BUFFER_DEBUG
  41. ZF_LOGV_MEM(buffer.data(), buffer.size(), "Buffer:");
  42. ZF_LOGV_MEM(work.data(), work.size(), "Work:");
  43. ZF_LOGV_MEM(text.data(), text.size(), "Text Buffer:");
  44. std::ostringstream oss;
  45. int comma = 0;
  46. for (auto it = std::begin(text_offsets); it != std::end(text_offsets); ++it) {
  47. if (comma) {
  48. oss << ", ";
  49. };
  50. comma++;
  51. oss << *it;
  52. if (comma == 30) {
  53. std::string temp_output = oss.str();
  54. ZF_LOGV("Vector: %s", temp_output.c_str());
  55. // reset ostringstream
  56. oss.str(std::string());
  57. oss.clear();
  58. comma = 0;
  59. }
  60. }
  61. std::string vector_output = oss.str();
  62. ZF_LOGV("Vector: %s", vector_output.c_str());
  63. // reset oss (if we need it)
  64. oss.str(std::string());
  65. oss.clear();
  66. #endif
  67. }
  68. void CharMan::regular_expressions(void) {
  69. static std::regex words("[a-zA-Z'-]+([ ]{1,2}[a-zA-Z&'-]+)+");
  70. // I need position and length.
  71. pos_len.clear();
  72. for (auto it =
  73. std::sregex_iterator(this->text.begin(), this->text.end(), words);
  74. it != std::sregex_iterator(); ++it) {
  75. int pos = it->position(), len = it->length();
  76. ZF_LOGD("pos %d len %d (%s)", pos, len,
  77. this->text.substr(pos, len).c_str());
  78. if (len > 4)
  79. pos_len.push_back(std::make_pair(it->position(), it->length()));
  80. }
  81. }
  82. char CharMan::get(int pos) { return this->text[pos]; }
  83. void CharMan::set(int pos, char ch) {
  84. this->text[pos] = ch;
  85. int idx = this->text_offsets[pos];
  86. if (idx >= 0) {
  87. this->buffer[idx] = ch;
  88. this->work[idx] = ch;
  89. }
  90. }
  91. void CharMan::insert(int pos, std::string str) {
  92. int len = str.size();
  93. // What happens if pos is at the end of the buffer?
  94. int idx;
  95. if (pos == (int)text_offsets.size()) {
  96. // Ok, this is at the very end of the buffer, which is beyond what the
  97. // vector is holding.
  98. idx = this->text_offsets[pos - 1] + 1;
  99. ZF_LOGE("Use %d for idx", idx);
  100. } else {
  101. idx = this->text_offsets[pos];
  102. // What happens if pos is pointing at color code? Or ANSI cursor movement?
  103. // (that follows text)
  104. if (idx == -1) {
  105. idx = this->text_offsets[pos - 1] + 1;
  106. ZF_LOGE("-1 take 2: %d", idx);
  107. }
  108. }
  109. // ZF_LOGE("insert( POS %d, LEN %d, IDX %d, %s)", pos, len, idx, str.c_str());
  110. // diagnostics();
  111. std::string blank(len, ' ');
  112. // Don't insert into text.
  113. // Insert blank into work.
  114. // Update indexes >= idx
  115. if (idx >= 0) {
  116. this->buffer.insert(idx, str);
  117. this->work.insert(idx, blank);
  118. // UPDATE indexes!
  119. for (auto it = std::begin(this->text_offsets);
  120. it != std::end(this->text_offsets); ++it) {
  121. if (*it >= idx) {
  122. *it += len;
  123. }
  124. }
  125. // ZF_LOGE("Indexes updated... check your work");
  126. // diagnostics();
  127. }
  128. }
  129. int CharMan::word_mangler(std::pair<int, int> pos_len) {
  130. int pos = pos_len.first;
  131. int state = randrange(-1, 1);
  132. int count = 0;
  133. for (int p = 0; p < pos_len.second; ++p) {
  134. char c = this->get(pos + p);
  135. if (randint(pos_len.second) == p)
  136. break;
  137. switch (state) {
  138. case -1:
  139. if (islower(c)) {
  140. count++;
  141. this->set(pos + p, toupper(c));
  142. }
  143. break;
  144. case 0:
  145. if (islower(c)) {
  146. count++;
  147. this->set(pos + p, toupper(c));
  148. } else {
  149. if (isupper(c)) {
  150. count++;
  151. this->set(pos + p, tolower(c));
  152. }
  153. }
  154. break;
  155. case 1:
  156. if (isupper(c)) {
  157. count++;
  158. this->set(pos + p, tolower(c));
  159. }
  160. break;
  161. }
  162. }
  163. return count;
  164. }
  165. // What is the max number of characters I should wrangle?
  166. #define MAX_TRANSPOSE 2
  167. int CharMan::word_wrangler(std::pair<int, int> pos_len) {
  168. int count = 0;
  169. int p;
  170. int len;
  171. if (pos_len.second < 4)
  172. return 0;
  173. p = randint(pos_len.second - 4) + 2;
  174. for (len = 0; len < MAX_TRANSPOSE; ++len) {
  175. if (!isalpha(this->get(pos_len.first + p + len)))
  176. break;
  177. }
  178. ZF_LOGD("Wrangler: %d, %d", p, len);
  179. if (len >= 2) {
  180. for (int x = 0; x < len / 2; x++) {
  181. char ch = this->get(pos_len.first + p + x);
  182. this->set(pos_len.first + p + x,
  183. this->get(pos_len.first + p + len - 1 - x));
  184. this->set(pos_len.first + p + len - 1 - x, ch);
  185. }
  186. count++;
  187. }
  188. return count;
  189. }
  190. /*
  191. Display up to certain point.
  192. Print some characters slowly. Delay.
  193. */
  194. int CharMan::word_tangler(std::pair<int, int> pos_len) {
  195. int p;
  196. int len;
  197. std::string part = this->text.substr(pos_len.first, pos_len.second);
  198. ZF_LOGE("tangler [%s]", logrepr(part.c_str()));
  199. if (pos_len.second < 4)
  200. return 0;
  201. /* p = randint(pos_len.second - 4);
  202. len = randint(pos_len.second - p); */
  203. p = pos_len.first;
  204. len = pos_len.second;
  205. if (len >= 2) {
  206. ZF_LOGD("Tangler: %d, %d", p, len);
  207. this->validate();
  208. std::ostringstream buffer;
  209. std::string tangle;
  210. int r = 1; // randint(2) + 1;
  211. buffer << TRIGGER "P1" TRIGGER "R" << r;
  212. r = randint(3) + 1; // This will cause us to speed up regarding render
  213. // speed. (reducing delay for callers using the bbs)
  214. buffer << TRIGGER "S" << r;
  215. tangle = buffer.str();
  216. std::string reset = TRIGGER "R0" TRIGGER "S0";
  217. ZF_LOGD("insert reset %s", reset.c_str());
  218. this->insert(p + len, reset);
  219. // this->validate();
  220. ZF_LOGD("insert tangle %s", tangle.c_str());
  221. this->insert(p, tangle);
  222. // this->validate();
  223. return 1;
  224. }
  225. return 0;
  226. }
  227. CharMan::CharMan(std::string &buffer, std::string &work, std::string &text,
  228. std::vector<int> &text_offsets)
  229. : buffer(buffer), work(work), text(text), text_offsets(text_offsets) {
  230. this->mangle_count = 0;
  231. this->mangle_chars = 0;
  232. this->need_render = 0;
  233. this->level = harry_level();
  234. if (!this->level)
  235. return;
  236. // validate();
  237. regular_expressions();
  238. ZF_LOGD("Found %d word groups", (int)pos_len.size());
  239. if (pos_len.size() > 0) {
  240. for (int i = 0; i < (int)pos_len.size(); ++i) {
  241. int active = 0;
  242. // if (random_activate((level + 1) / 2)) {
  243. if (random_activate(level * 11)) {
  244. int c = word_mangler(pos_len[i]);
  245. if (c) {
  246. active = 1;
  247. this->mangle_count++;
  248. this->mangle_chars += c;
  249. }
  250. }
  251. // if (random_activate((level + 1) / 2)) {
  252. if (random_activate(level * 11)) {
  253. if (word_wrangler(pos_len[i])) {
  254. this->mangle_count++;
  255. active = 1;
  256. }
  257. }
  258. // if (!active && random_activate(level)) {
  259. if (!active && random_activate(((level + 1) / 2))) {
  260. if (word_tangler(pos_len[i])) {
  261. this->need_render = 1;
  262. }
  263. }
  264. }
  265. }
  266. };
  267. CharMan::~CharMan() {
  268. ZF_LOGD("~CharMan");
  269. // validate();
  270. // diagnostics();
  271. }