Neuron.php.makeNeuronStore.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. var MAKE_STORE_FUNCTION_NAME = MAKE_STORE_FUNCTION_NAME || 'makeNeuronStore'
  2. var DBG = DBG || false;
  3. var DBG1 = true;
  4. var DEFAULT_NEURON = {
  5. value: '',
  6. charge: 0,
  7. maxCharge: 1.5, // DEFAULT_CONFIG.config_max_neuron_charge,
  8. uiShape: "circle",
  9. ui: { cx: 0, cy: 0 },
  10. source: [],
  11. next: [],
  12. }
  13. function makeDefaultNeuronsStoreState() {
  14. return {
  15. receptor: [],
  16. mapReceptorChar: {},
  17. neuron: [],
  18. connection: [],
  19. doubleConn: [], // connections between two nodes
  20. charge: [], // TODO: list of [ { charge: number, nodeType: (receptor|neuron), nodeIdx: int }, ... ] groupd by node (type/idx)
  21. inputReadPos: 0,
  22. animPos: 0,
  23. doAnim: true,
  24. uiOutputHeight: 300,
  25. }
  26. }
  27. function makeNeuronStore() {
  28. DBG && console.log("DBG:NeuronStore:makeNeuronStore");
  29. var _state = makeDefaultNeuronsStoreState()
  30. var _config = {}
  31. var _inputText = ''
  32. var _animIntervalID = null
  33. var _callback = null
  34. function stateReset(inputText, config) {
  35. _inputText = inputText
  36. _config = config
  37. _state = makeDefaultNeuronsStoreState()
  38. var distinct = function (value, idx, self) {
  39. return (idx === self.indexOf(value));
  40. };
  41. var foundLetters = inputText.split("").filter(distinct);
  42. _state.receptor = foundLetters.sort().map(makeReceptor);
  43. _state.mapReceptorChar = _state.receptor.map(function (node) { return node.value; });
  44. if (_animIntervalID) clearInterval(_animIntervalID)
  45. _notifySubscribers();
  46. }
  47. function readCharFromInput() {
  48. if (_state.inputReadPos >= _inputText.length) return null;
  49. var char = _inputText.charAt(_state.inputReadPos)
  50. _state.inputReadPos += 1
  51. return char;
  52. }
  53. function forwardAnim() {
  54. DBG1 && console.log("DBG:NeuronStore:forwardAnim:doAnim = '" + (_state.doAnim ? 1 : 0) + "'");
  55. if (!_state.doAnim) return;
  56. // 1. read from input if its time
  57. // 2. check overcharged nodes
  58. // 2.1 check overcharged Receptor
  59. // 2.2 check overcharged Neuron
  60. // 3. move all charges (Receptor -> Connection, Connection -> Neuron, Neuron -> Connection)
  61. // 4. discharge all
  62. // ad.2. TODO: what to do with Charge when create new Neuron?
  63. // - idea 1: use Charge to create new Neuron: rm Charge, add Neuron, add Connection
  64. // 1. read from input if its time
  65. if (0 === _state.animPos % _config.config_read_speed_multiplier) {
  66. // read from input = add charge to receptor with given letter
  67. var char = readCharFromInput()
  68. if (null === char) {
  69. DBG1 && console.log("DBG:NeuronStore:forwardAnim:1(readInput): STOP - end of input", { inputLength: _inputText.length, animPos: _state.animPos })
  70. _state.doAnim = false
  71. _notifySubscribers()
  72. return;
  73. }
  74. var foundReceptorIdx = getReceptorIdx(char)
  75. DBG1 && console.log("DBG:NeuronStore:forwardAnim:1(readInput): char('" + char + "', [" + char.charCodeAt(0) + "])", { animPos: _state.animPos, char, len: _inputText.length, doAnim: _state.doAnim, foundReceptorIdx })
  76. if (-1 === foundReceptorIdx) {
  77. DBG1 && console.warn("BUG: receptor '" + char + "' not found (NeuronStore:forwardAnim:readInput)")
  78. _state.doAnim = false
  79. _notifySubscribers()
  80. return;
  81. }
  82. chargeAdd(_config.config_charge_receptor_at_input, { type: 'receptor', idx: foundReceptorIdx })
  83. _state.animPos += 1
  84. _notifySubscribers()
  85. return;
  86. }
  87. // 2. check overcharged nodes
  88. // 2.1 check overcharged Neuron
  89. // 2.1.1 is overcharged Neuron
  90. // 2.1.1.1 is another Neuron with Charge
  91. // 2.1.1.2 ! another Neuron with Charge
  92. // 2.1.2 ! overcharged Neuron
  93. // 2.2 check overcharged Receptor
  94. { // check overcharged nodes
  95. var overchargedNeuronIdx = _state.neuron.reduce(function (ret, neuron, idx) {
  96. var charge = getNeuronCharge(idx)
  97. if (charge < neuron.maxCharge) return ret;
  98. ret.push({ idx: idx, charge: charge })
  99. return ret;
  100. }, [])
  101. var overchargedReceptorIdx = _state.receptor.reduce(function (ret, receptor, idx) {
  102. var charge = getReceptorCharge(idx)
  103. if (charge < _config.config_max_receptor_charge) return ret;
  104. ret.push({ idx: idx, charge: charge })
  105. return ret;
  106. }, [])
  107. if (overchargedNeuronIdx.length > 0) {
  108. var overcharged = overchargedNeuronIdx[0]
  109. var firstNeuron = _state.neuron[overcharged.idx]
  110. var secondNeuronWithCharge = _state.neuron.reduce(function (ret, neuron, idx) { // find neuron with max charge
  111. if (idx === overcharged.idx) return ret; // skip firstNeuron
  112. var charge = getNeuronCharge(idx)
  113. if (!charge) return ret; // skip Neuron without charge
  114. if (null === ret) return { idx: idx, charge: charge }
  115. return (charge > ret.charge) ? { idx: idx, charge: charge } : ret;
  116. }, null)
  117. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2.1(check overcharged)/neuron.1", { overcharged, firstNeuron, secondNeuronWithCharge })
  118. if (null !== secondNeuronWithCharge) {
  119. // is already exists - check doubleConn: { from: [ neuronIdx, neuronIdx ], to: neuronIdx }
  120. var existingConn = _state.doubleConn.reduce(function (ret, dblConn) {
  121. if (ret) return ret;
  122. if (dblConn.from[0] === overcharged.idx && dblConn.from[1] === secondNeuronWithCharge.idx) return dblConn;
  123. // if (dblConn.from[1] === overcharged.idx && dblConn.from[0] === secondNeuronWithCharge.idx) return dblConn;
  124. return ret;
  125. }, null)
  126. var toNode = _state.neuron[secondNeuronWithCharge.idx]
  127. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2.1(check overcharged)/neuron.2(secondNeuronWithCharge)", { from: firstNeuron.value, to: toNode.value, existingConn })
  128. if (existingConn) { // charge Neuron existingConn.to
  129. var idxNewNeuron = existingConn.to
  130. chargeRemove({ type: 'neuron', idx: overcharged.idx })
  131. chargeRemove({ type: 'neuron', idx: secondNeuronWithCharge.idx })
  132. switch (_config.config_strategy_overcharge) {
  133. case "REMOVE_CHARGE": break;
  134. case "LEAVE_HALF_CHARGE": {
  135. chargeAdd(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
  136. chargeAdd(secondNeuronWithCharge.charge / 2, { type: 'neuron', idx: secondNeuronWithCharge.idx })
  137. } break;
  138. case "LEAVE_ALMOST_MAX": {
  139. chargeAdd(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
  140. chargeAdd(getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx), { type: 'neuron', idx: secondNeuronWithCharge.idx })
  141. } break;
  142. }
  143. chargeAdd(overcharged.charge + secondNeuronWithCharge.charge, { type: 'neuron', idx: existingConn.to })
  144. // _state.animPos += 1
  145. // dischargeAll()
  146. _notifySubscribers()
  147. return;
  148. } else { // create new
  149. var newNeuron = makeNeuronFromTwoNeurons(overcharged.idx, overcharged.charge, secondNeuronWithCharge.idx, secondNeuronWithCharge.charge)
  150. if (!newNeuron) {
  151. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2(check overcharged)/neuron.2.1(!newNeuron)", { overcharged })
  152. // _state.animPos += 1
  153. dischargeAll()
  154. _notifySubscribers()
  155. return;
  156. } else {
  157. _state.neuron.push(newNeuron)
  158. updateOuputHeight(newNeuron)
  159. var idxNewNeuron = _state.neuron.length - 1
  160. _state.connection.push({ fromType: 'neuron', fromIdx: overcharged.idx, toIdx: idxNewNeuron, timesCreated: 1 })
  161. _state.connection.push({ fromType: 'neuron', fromIdx: secondNeuronWithCharge.idx, toIdx: idxNewNeuron, timesCreated: 1 })
  162. _state.doubleConn.push({ from: [overcharged.idx, secondNeuronWithCharge.idx], to: idxNewNeuron, connIdx: [_state.connection.length - 2, _state.connection.length - 1] })
  163. chargeRemove({ type: 'neuron', idx: overcharged.idx })
  164. chargeRemove({ type: 'neuron', idx: secondNeuronWithCharge.idx })
  165. switch (_config.config_strategy_overcharge) {
  166. case "REMOVE_CHARGE": break;
  167. case "LEAVE_HALF_CHARGE": {
  168. chargeAdd(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
  169. chargeAdd(secondNeuronWithCharge.charge / 2, { type: 'neuron', idx: secondNeuronWithCharge.idx })
  170. } break;
  171. case "LEAVE_ALMOST_MAX": {
  172. // chargeAdd(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
  173. // chargeAdd(getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx), { type: 'neuron', idx: secondNeuronWithCharge.idx })
  174. var fromCharge = getNeuronAlmostMaxCharge(overcharged.idx)
  175. var toCharge = getNeuronAlmostMaxCharge(secondNeuronWithCharge.idx)
  176. chargeAdd((fromCharge + toCharge) / 2, { type: 'dbl_conn', idx: _state.doubleConn.length - 1, fromCharge: fromCharge, toCharge: toCharge })
  177. } break;
  178. }
  179. // _state.animPos += 1
  180. // dischargeAll()
  181. _notifySubscribers()
  182. return;
  183. }
  184. }
  185. // // _state.animPos += 1
  186. // // dischargeAll()
  187. // _notifySubscribers()
  188. // return;
  189. } else { // !secondNeuronWithCharge
  190. // TODO: check if already exists! if yes then charge else create new
  191. var idxFoundConnection = _state.connection.reduce(function (ret, conn, idx) {
  192. if (ret > -1) return ret;
  193. if (conn.fromType === 'neuron' && conn.fromIdx === overcharged.idx) return idx;
  194. return ret;
  195. }, -1)
  196. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2(check overcharged)/neuron.2(!secondNeuronWithCharge)", { idxFoundConnection })
  197. if (-1 === idxFoundConnection) {
  198. var newNeuron = makeNeuronFromOneNeuron(overcharged.idx, overcharged.charge)
  199. if (!newNeuron) {
  200. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2(check overcharged)/neuron.2.1(!newNeuron)", { overcharged })
  201. // _state.animPos += 1
  202. dischargeAll()
  203. _notifySubscribers()
  204. return;
  205. } else {
  206. _state.neuron.push(newNeuron)
  207. updateOuputHeight(newNeuron)
  208. var idxNewNeuron = _state.neuron.length - 1
  209. _state.connection.push({ fromType: 'neuron', fromIdx: overcharged.idx, toIdx: idxNewNeuron, timesCreated: 1 })
  210. chargeRemove({ type: 'neuron', idx: overcharged.idx })
  211. switch (_config.config_strategy_overcharge) {
  212. case "REMOVE_CHARGE": break;
  213. case "LEAVE_HALF_CHARGE": {
  214. chargeAdd(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
  215. } break;
  216. case "LEAVE_ALMOST_MAX": {
  217. chargeAdd(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
  218. } break;
  219. }
  220. _notifySubscribers()
  221. return;
  222. }
  223. } else {
  224. _state.connection[idxFoundConnection].timesCreated += 1
  225. // chargeMove(overcharged.charge, { type: 'receptor', idx: overcharged.idx }, { type: 'connection', idx: idxFoundConnection })
  226. chargeRemove({ type: 'neuron', idx: overcharged.idx })
  227. switch (_config.config_strategy_overcharge) {
  228. case "REMOVE_CHARGE": break;
  229. case "LEAVE_HALF_CHARGE": {
  230. chargeAdd(overcharged.charge / 2, { type: 'neuron', idx: overcharged.idx })
  231. } break;
  232. case "LEAVE_ALMOST_MAX": {
  233. chargeAdd(getNeuronAlmostMaxCharge(overcharged.idx), { type: 'neuron', idx: overcharged.idx })
  234. } break;
  235. }
  236. chargeAdd(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
  237. }
  238. _notifySubscribers()
  239. return;
  240. }
  241. // _notifySubscribers()
  242. // return;
  243. }
  244. if (overchargedReceptorIdx.length > 0) { // TODO: while ?
  245. if (1 === overchargedReceptorIdx.length) {
  246. var overcharged = overchargedReceptorIdx.shift()
  247. // Restriction: only one connection between nodes
  248. {
  249. var idxFoundConnection = _state.connection.reduce(function (ret, conn, idx) {
  250. if (ret > -1) return ret;
  251. if (conn.fromType === 'receptor' && conn.fromIdx === overcharged.idx) return idx;
  252. return ret;
  253. }, -1)
  254. DBG1 && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)/receptor", { idxFoundConnection, from: { type: 'receptor', idx: overcharged.idx }, conn: [].concat(_state.connection) })
  255. if (-1 === idxFoundConnection) {
  256. var newNeuron = makeNeuronFromOneReceptor(overcharged.idx, overcharged.charge)
  257. _state.neuron.push(newNeuron)
  258. var idxNewNeuron = _state.neuron.length - 1
  259. _state.connection.push({ fromType: 'receptor', fromIdx: overcharged.idx, toIdx: idxNewNeuron, timesCreated: 1 })
  260. idxFoundConnection = _state.connection.length - 1
  261. chargeRemove({ type: 'receptor', idx: overcharged.idx })
  262. // switch (_config.config_strategy_overcharge) {
  263. // case "REMOVE_CHARGE": break;
  264. // case "LEAVE_HALF_CHARGE": {
  265. // chargeAdd(overcharged.charge / 2, { type: 'receptor', idx: overcharged.idx })
  266. // } break;
  267. // case "LEAVE_ALMOST_MAX": {
  268. // chargeAdd(getReceptorAlmostMaxCharge(overcharged.idx), { type: 'receptor', idx: overcharged.idx })
  269. // } break;
  270. // }
  271. chargeAdd(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
  272. } else {
  273. _state.connection[idxFoundConnection].timesCreated += 1
  274. // chargeMove(overcharged.charge, { type: 'receptor', idx: overcharged.idx }, { type: 'connection', idx: idxFoundConnection })
  275. chargeRemove({ type: 'receptor', idx: overcharged.idx })
  276. // switch (_config.config_strategy_overcharge) {
  277. // case "REMOVE_CHARGE": break;
  278. // case "LEAVE_HALF_CHARGE": {
  279. // chargeAdd(overcharged.charge / 2, { type: 'receptor', idx: overcharged.idx })
  280. // } break;
  281. // case "LEAVE_ALMOST_MAX": {
  282. // chargeAdd(getReceptorAlmostMaxCharge(overcharged.idx), { type: 'receptor', idx: overcharged.idx })
  283. // } break;
  284. // }
  285. chargeAdd(overcharged.charge, { type: 'connection', idx: idxFoundConnection })
  286. }
  287. }
  288. // _state.animPos += 1
  289. // dischargeAll()
  290. _notifySubscribers()
  291. return;
  292. } else {
  293. DBG1 && console.log("TODO:NeuronStore:forwardAnim:2(check overcharged)/receptor", { TODO: "more then 1 receptor overcharged!" })
  294. }
  295. }
  296. // DBG1 && console.log("DBG:NeuronStore:forwardAnim:2(check overcharged)", { overchargedNeuronIdx, overchargedReceptorIdx })
  297. }
  298. { // 3. move all charges (Receptor -> Connection, Connection -> Neuron, Neuron -> Connection)
  299. DBG1 && console.log("TODO:NeuronStore:forwardAnim:3(move all charges)", { charge: [].concat(_state.charge) })
  300. var isChargeTypeConnection = function (charge) {
  301. if ("connection" === charge.nodeType) return true;
  302. if ("dbl_conn" === charge.nodeType) return true;
  303. return false;
  304. }
  305. var toAddChargeNeuronIdxList = _state.charge.filter(isChargeTypeConnection).map(function (charge) {
  306. DBG1 && console.log("TODO:NeuronStore:forwardAnim:3(move all charges).LOOP", { charge })
  307. switch (charge.nodeType) {
  308. case "connection": return { charge: charge.charge, toIdx: _state.connection[charge.nodeIdx].toIdx };
  309. case "dbl_conn": return { charge: charge.charge, toIdx: _state.doubleConn[charge.nodeIdx].to };
  310. }
  311. return null;
  312. })
  313. _state.charge = _state.charge.filter(function (charge) { return !isChargeTypeConnection(charge); })
  314. toAddChargeNeuronIdxList.forEach(function (toAddCharge) {
  315. chargeAdd(toAddCharge.charge, { type: 'neuron', idx: toAddCharge.toIdx })
  316. })
  317. }
  318. { // 4. discharge all
  319. dischargeAll()
  320. }
  321. _state.animPos += 1
  322. _notifySubscribers();
  323. }
  324. function dischargeAll() {
  325. _state.charge = _state.charge.map(function (charge) {
  326. return Object.assign({}, charge, {
  327. charge: Math.max(0, charge.charge - _config.config_discharge_per_tick),
  328. })
  329. }).filter(function (charge) {
  330. return charge.charge > 0;
  331. })
  332. }
  333. function makeNeuronFromTwoNeurons(fromNeuronIdx, fromNeuronCharge, toNeuronIdx, toNeuronCharge) {
  334. var fromNode = _state.neuron[fromNeuronIdx]
  335. var toNode = _state.neuron[toNeuronIdx]
  336. var maxCharge = getNewNeuronMaxCharge(fromNode)
  337. DBG1 && console.log("DBG:NeuronStore:makeNeuronFromTwoNeurons", { fromNode, toNode, maxCharge })
  338. if (maxCharge < _config.config_discharge_per_tick) return null;
  339. var uiNewNodePos = { cx: 0, cy: 0 }
  340. { // closer to node with more charge (first)
  341. var xDiff = Math.abs(fromNode.ui.cx - toNode.ui.cx)
  342. var isFirstOnRight = (fromNode.ui.cx > toNode.ui.cx)
  343. // fromNeuronCharge + toNeuronCharge -- xDiff
  344. // toNeuronCharge -- x ==> x = toNeuronCharge * xDiff / (fromNeuronCharge + toNeuronCharge)
  345. var xToFirst = (toNeuronCharge * xDiff) / (fromNeuronCharge + toNeuronCharge)
  346. uiNewNodePos.cx = fromNode.ui.cx + (isFirstOnRight ? -1 : 1) * xToFirst
  347. }
  348. uiNewNodePos.cy = (fromNode.ui.cy != toNode.ui.cy)
  349. ? Math.max(fromNode.ui.cy, toNode.ui.cy) + 10 + _config.ui_space_y / 2
  350. : fromNode.ui.cy + 10 + _config.ui_space_y
  351. ;
  352. uiNewNodePos.cy = _state.neuron.reduce(function (ret, neuron) {
  353. if (uiNewNodePos.cx < neuron.ui.cx - 10) return ret;
  354. if (uiNewNodePos.cx > neuron.ui.cx + 10) return ret;
  355. if (ret < neuron.ui.cy - 10) return ret;
  356. if (ret > neuron.ui.cy + 10) return ret;
  357. return ret + 10 + _config.ui_space_y;
  358. }, uiNewNodePos.cy)
  359. DBG1 && console.log("DBG:NeuronStore:makeNeuronFromTwoNeurons", { uiNewNodePos, fromNode, toNode, newNodeMaxCharge: getNewNeuronMaxCharge(fromNode) })
  360. // var value = '' + (_state.neuron.length + 1);
  361. var value = [fromNode.value, toNode.value].join('')
  362. return Object.assign({}, DEFAULT_NEURON, {
  363. value: value,
  364. charge: 0,
  365. maxCharge: getNewNeuronMaxCharge(fromNode),
  366. uiShape: "ellipse",
  367. ui: Object.assign({}, uiNewNodePos, {
  368. rx: 10,
  369. ry: 10,
  370. }),
  371. });
  372. }
  373. function makeNeuronFromOneReceptor(fromReceptorIdx, charge) {
  374. var sourceNode = _state.receptor[fromReceptorIdx]
  375. // var value = '' + (_state.neuron.length + 1);
  376. var value = [sourceNode.value, ""].join('') // TODO: same name what Receptor
  377. DBG1 && console.log("DBG:NeuronStore:makeNeuronFromOneReceptor", { sourceNode, newNodeMaxCharge: getNewNeuronMaxCharge(sourceNode) })
  378. return Object.assign({}, DEFAULT_NEURON, {
  379. value: value,
  380. charge: 0,
  381. maxCharge: getNewNeuronMaxCharge(sourceNode),
  382. uiShape: "ellipse",
  383. ui: {
  384. cx: sourceNode.ui.cx,
  385. cy: sourceNode.ui.cy + 10 + _config.ui_space_y,
  386. rx: 10,
  387. ry: 10,
  388. },
  389. });
  390. }
  391. function makeNeuronFromOneNeuron(fromNeuronIdx, charge) {
  392. var sourceNode = _state.neuron[fromNeuronIdx]
  393. // var value = '' + (_state.neuron.length + 1);
  394. var value = [sourceNode.value, "^"].join('')
  395. var maxCharge = getNewNeuronMaxCharge(sourceNode)
  396. if (maxCharge < _config.config_discharge_per_tick) return null;
  397. DBG1 && console.log("DBG:NeuronStore:makeNeuronFromOneNeuron", { sourceNode, newNodeMaxCharge: maxCharge })
  398. return Object.assign({}, DEFAULT_NEURON, {
  399. value: value,
  400. charge: 0,
  401. maxCharge: maxCharge,
  402. uiShape: "ellipse",
  403. ui: {
  404. cx: sourceNode.ui.cx,
  405. cy: sourceNode.ui.cy + 10 + _config.ui_space_y,
  406. rx: 10,
  407. ry: 10,
  408. },
  409. });
  410. }
  411. function getNewNeuronMaxCharge(sourceNode) {
  412. // return sourceNode.maxCharge * _config.config_discharge_max_in_new_neuron_from_one;
  413. return sourceNode.maxCharge - _config.config_discharge_per_tick;
  414. }
  415. function chargeAdd(charge, to) {
  416. var foundChargeIdx = _state.charge.reduce(function (ret, charge, idx) {
  417. if (charge.nodeType === to.type && charge.nodeIdx === to.idx) return idx;
  418. return ret;
  419. }, -1)
  420. DBG1 && console.log("DBG:NeuronStore:chargeAdd", { charge, to, foundChargeIdx })
  421. if (-1 === foundChargeIdx) {
  422. _state.charge.push({ charge: charge, nodeType: to.type, nodeIdx: to.idx })
  423. foundChargeIdx = _state.charge.length - 1
  424. } else {
  425. _state.charge[foundChargeIdx].charge += charge
  426. }
  427. if ('dbl_conn' === to.type) {
  428. if (!_state.charge[foundChargeIdx].hasOwnProperty('fromCharge')) _state.charge[foundChargeIdx].fromCharge = 0
  429. if (!_state.charge[foundChargeIdx].hasOwnProperty('toCharge')) _state.charge[foundChargeIdx].toCharge = 0
  430. _state.charge[foundChargeIdx].fromCharge += to.fromCharge
  431. _state.charge[foundChargeIdx].toCharge += to.toCharge
  432. }
  433. DBG1 && console.log("DBG:NeuronStore:chargeAdd.2", { state_charge: [].concat(_state.charge) })
  434. return foundChargeIdx;
  435. }
  436. function chargeRemove(from) {
  437. _state.charge = _state.charge.filter(function (item) {
  438. return !(from.type === item.nodeType && from.idx === item.nodeIdx);
  439. })
  440. }
  441. function updateOuputHeight(newNeuron) {
  442. if (newNeuron.ui.cy + 30 > _state.uiOutputHeight) {
  443. _state.uiOutputHeight = newNeuron.ui.cy + 30
  444. }
  445. }
  446. function dispatch(payload) {
  447. DBG1 && console.log("DBG:NeuronStore:dispatch('" + payload.type + "')", payload);
  448. switch (payload.type) {
  449. case 'INIT': stateReset(payload.inputText, payload.config); startAnimation(); break;
  450. case 'PAUSE': _state.doAnim = false; _notifySubscribers(); break;
  451. case 'PLAY': _state.doAnim = true; break;
  452. default: {
  453. DBG1 && console.warn("Not implemented dispatch action type '" + payload.type + "'")
  454. }
  455. }
  456. }
  457. function subscribe(callback) {
  458. _callback = callback; // TODO: array
  459. return unsubscribe;
  460. }
  461. function unsubscribe() {
  462. _callback = null
  463. }
  464. function _notifySubscribers() {
  465. if (_callback) _callback()
  466. }
  467. function makeReceptor(value, idx, arr) { // usage: [].map(makeReceptor)
  468. var totalReceptors = arr.length;
  469. var cx = Math.ceil(_config.ui_output_width / (totalReceptors + 1));
  470. var r = Math.min(Math.ceil(_config.ui_output_width / (totalReceptors + 10) / 2), _config.ui_max_receptor_r);
  471. var xCenter = cx + cx * idx - r;
  472. return Object.assign({}, DEFAULT_NEURON, {
  473. value: value,
  474. maxCharge: _config.config_max_receptor_charge,
  475. uiShape: "circle",
  476. ui: {
  477. cx: xCenter,
  478. cy: 40,
  479. r: r,
  480. },
  481. next: [],
  482. })
  483. }
  484. function getReceptorIdx(char) {
  485. return _state.mapReceptorChar.indexOf(char)
  486. }
  487. function startAnimation() {
  488. _state.doAnim = true
  489. if (_animIntervalID) clearInterval(_animIntervalID)
  490. _animIntervalID = setInterval(forwardAnim, _config.config_anim_speed)
  491. }
  492. function getReceptorCharge(idx) {
  493. return _state.charge.filter(function (charge) {
  494. return ('receptor' === charge.nodeType && idx === charge.nodeIdx);
  495. }).reduce(function (ret, charge) {
  496. return ret + charge.charge;
  497. }, 0)
  498. }
  499. function getNeuronCharge(idx) {
  500. return _state.charge.filter(function (charge) {
  501. return ('neuron' === charge.nodeType && idx === charge.nodeIdx);
  502. }).reduce(function (ret, charge) {
  503. return ret + charge.charge;
  504. }, 0)
  505. }
  506. function getReceptorAlmostMaxCharge(idx) {
  507. return _state.receptor[idx].maxCharge - _config.config_discharge_per_tick;
  508. }
  509. function getNeuronAlmostMaxCharge(idx) {
  510. DBG1 && console.log("DBG:getNeuronAlmostMaxCharge(" + idx + ")", { charge: _state.neuron[idx].maxCharge - _config.config_discharge_per_tick })
  511. return _state.neuron[idx].maxCharge - _config.config_discharge_per_tick;
  512. }
  513. function getNodesChargeState() {
  514. var receptor = [].concat(_state.receptor);
  515. var neuron = [].concat(_state.neuron);
  516. _state.charge.forEach(function (charge) {
  517. // _state.charge: [ { charge: number, nodeType: (receptor|neuron), nodeIdx: int }, ... ] groupd by node (type/idx)
  518. switch (charge.nodeType) {
  519. case 'receptor': receptor[charge.nodeIdx].charge = charge.charge; break;
  520. case 'neuron': neuron[charge.nodeIdx].charge = charge.charge; break;
  521. }
  522. })
  523. function getNodeInfoFunction(type) {
  524. return function _getNodeInfo(node) {
  525. return {
  526. type: type,
  527. value: node.value,
  528. charge: node.charge,
  529. }
  530. }
  531. }
  532. return receptor.map(getNodeInfoFunction('receptor'))
  533. .concat(
  534. neuron.map(getNodeInfoFunction('neuron'))
  535. )
  536. ;
  537. }
  538. return {
  539. subscribe: subscribe,
  540. dispatch: dispatch,
  541. getState: function () { return _state; },
  542. getNodesChargeState: getNodesChargeState,
  543. getListReceptor: function () { return _state.receptor; },
  544. getListNeuron: function () { return _state.neuron; },
  545. getReceptor: function (idx) { return _state.receptor[idx]; },
  546. getNeuron: function (idx) { return _state.neuron[idx]; },
  547. getConnection: function (idx) { return _state.connection[idx]; },
  548. getDoubleConnection: function (idx) { return _state.doubleConn[idx]; },
  549. getCharge: function (idx) { return _state.charge[idx]; },
  550. getListConnection: function () { return _state.connection; },
  551. getListCharge: function () { return _state.charge; },
  552. getReceptorCharge: getReceptorCharge,
  553. getNeuronCharge: getNeuronCharge,
  554. }
  555. }
  556. global[MAKE_STORE_FUNCTION_NAME] = makeNeuronStore
  557. // export default makeNeuronStore