Neuron.php.makeNeuronStore.js 28 KB

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